mirror of https://github.com/1099438829/apeblog
更新文件
This commit is contained in:
parent
ae98df7961
commit
c72547f73e
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 模板设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 模板后缀
|
||||
'view_suffix' => 'html',
|
||||
// 视图输出字符串内容替换
|
||||
'tpl_replace_string' => [
|
||||
'__PUBLIC_PATH__' => '/', //public 目录
|
||||
'__ADMIN_PATH__' => '/static/admin/', //全局静态目录
|
||||
]
|
||||
];
|
||||
|
|
@ -130,6 +130,8 @@ class AdminAuth extends AuthController
|
|||
$data['update_time'] = time();
|
||||
$res = aModel::update($data, ['id' => $id]);
|
||||
}
|
||||
//清理缓存
|
||||
aModel::clearCache($this->adminId);
|
||||
return $res ? app("json")->success("操作成功", 'code') : app("json")->fail("操作失败");
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +145,8 @@ class AdminAuth extends AuthController
|
|||
if (!$id) return app("json")->fail("参数有误,Id为空!");
|
||||
$where = Util::postMore([['field', ''], ['value', '']]);
|
||||
if ($where['field'] == '' || $where['value'] == '') return app("json")->fail("参数有误!");
|
||||
//清理缓存
|
||||
aModel::clearCache($this->adminId);
|
||||
return aModel::update([$where['field'] => $where['value']], ['id' => $id]) ? app("json")->success("操作成功") : app("json")->fail("操作失败");
|
||||
}
|
||||
}
|
||||
|
|
@ -106,6 +106,8 @@ class AdminRole extends AuthController
|
|||
$data['update_time'] = time();
|
||||
$res = rModel::update($data, ['id' => $id]);
|
||||
}
|
||||
//清理缓存
|
||||
aModel::clearCache($this->adminId);
|
||||
return $res ? app("json")->success("操作成功", 'code') : app("json")->fail("操作失败");
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,6 @@ use app\admin\extend\Util as Util;
|
|||
|
||||
class Index extends AuthController
|
||||
{
|
||||
// 无需登录的
|
||||
protected $noNeedLogin = ['test', 'accessauth', 'pddlogin'];
|
||||
|
||||
/**
|
||||
* 后台首页
|
||||
* @return string
|
||||
|
|
@ -26,8 +23,8 @@ class Index extends AuthController
|
|||
cache(AdminAuth::getMenuCacheKey($this->adminId), $menuList, 1 * 60 * 60);
|
||||
}
|
||||
$this->assign("menu", $menuList);
|
||||
$message = ['data' => [], 'count' => 0];
|
||||
$this->assign("message", $message);
|
||||
$messageList = ['data' => [], 'count' => 0];
|
||||
$this->assign("message", $messageList);
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\model\Admin;
|
||||
use app\common\model\Admin as adminModel;
|
||||
use app\admin\extend\Util as Util;
|
||||
|
||||
|
|
@ -34,20 +33,15 @@ class Login extends AuthController
|
|||
*/
|
||||
public function verify()
|
||||
{
|
||||
list($account, $pwd, $verify) = Util::postMore(['account', 'password', 'verify'], null, true);
|
||||
if (empty($account) || empty($pwd)) return app("json")->fail("账号、密码和验证码不能为空!");
|
||||
list($username, $password, $captcha) = Util::postMore(['username', 'password', 'captcha'], null, true);
|
||||
if (empty($username) || empty($password)) return app("json")->fail("账号、密码和验证码不能为空!");
|
||||
// 验证码验证
|
||||
if (!captcha_check($verify)) return app("json")->fail("验证码不正确!");
|
||||
if (!captcha_check($captcha)) return app("json")->fail("验证码不正确!");
|
||||
// 验证登录
|
||||
if (!adminModel::login($account, $pwd)) return app("json")->fail("登录失败!");
|
||||
if (!adminModel::login($username, $password)) return app("json")->fail(adminModel::getErrorInfo());
|
||||
return app("json")->success("登录成功!");
|
||||
}
|
||||
|
||||
public function wechatLogin()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
* @return string
|
||||
|
|
@ -75,7 +69,7 @@ class Login extends AuthController
|
|||
*/
|
||||
public function logout()
|
||||
{
|
||||
return Admin::clearLoginInfo() ? $this->successfulNotice("操作成功", "/admin/login/login") : $this->failedNotice("操作失败", "/admin/index/index");
|
||||
return adminModel::clearLoginInfo() ? $this->successfulNotice("操作成功", "/admin/login/login") : $this->failedNotice("操作失败", "/admin/index/index");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ class SystemBasic extends BaseController
|
|||
/**
|
||||
* 操作失败提示框
|
||||
* @param string $msg 提示信息
|
||||
* @param int $backUrl 跳转地址
|
||||
* @param string $info
|
||||
* @param string $backUrl 跳转地址
|
||||
* @param string $title 标题
|
||||
* @param int $duration 持续时间
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function failedNotice($msg = '操作失败', $backUrl = 0, $info = '', $duration = 3)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ trait TemplateTrait
|
|||
$ids = $request->param("id", 0);
|
||||
if (empty($ids) || !$ids) return app("json")->fail("参数有误,Id为空!");
|
||||
if (!is_array($ids)) $ids = explode(",", $ids);
|
||||
if (in_array(1, $ids)) return app("json")->fail("参数有误,初始ID不允许操作!");
|
||||
return $this->model->where($this->model->getPk(), "in", $ids)->delete() ? app("json")->success("操作成功") : app("json")->fail("操作失败");
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ trait TemplateTrait
|
|||
$ids = $request->param("id", 0);
|
||||
if (empty($ids) || !$ids) return app("json")->fail("参数有误,Id为空!");
|
||||
if (!is_array($ids)) $ids = explode(",", $ids);
|
||||
if (in_array(1, $ids)) return app("json")->fail("参数有误,初始ID不允许操作!");
|
||||
return $this->model->where($this->model->getPk(), "in", $ids)->update(['status' => 1]) ? app("json")->success("操作成功") : app("json")->fail("操作失败");
|
||||
}
|
||||
|
||||
|
|
@ -52,6 +54,7 @@ trait TemplateTrait
|
|||
$ids = $request->param("id", 0);
|
||||
if (empty($ids) || !$ids) return app("json")->fail("参数有误,Id为空!");
|
||||
if (!is_array($ids)) $ids = explode(",", $ids);
|
||||
if (in_array(1, $ids)) return app("json")->fail("参数有误,初始ID不允许操作!");
|
||||
return $this->model->where($this->model->getPk(), "in", $ids)->update(['status' => 0]) ? app("json")->success("操作成功") : app("json")->fail("操作失败");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.css">
|
||||
<link href="__ADMIN_PATH__css/cropper.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/admin/js/jconfirm/jquery-confirm.min.css">
|
||||
<link href="/static/admin/css/cropper.min.css" rel="stylesheet">
|
||||
<style type="text/css">
|
||||
/* 裁剪样式 */
|
||||
.image-wrapper {
|
||||
|
|
@ -226,14 +226,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<!--图片裁剪END-->
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/main.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/cropper.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/lightyear.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.lyear.loading.js"></script>
|
||||
<script src="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/cropper.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/lightyear.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.lyear.loading.js"></script>
|
||||
<script src="/static/admin/js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script>
|
||||
$(".site-form").submit(function () {
|
||||
if ($("input[name='nickname']").val() == '') lightyear.notify('昵称不能为空', 'danger', 3000, 'mdi mdi-emoticon-neutral', 'top', 'center');
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.css">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/admin/js/jconfirm/jquery-confirm.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
|
|
@ -42,12 +42,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/main.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/lightyear.js"></script>
|
||||
<script src="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/lightyear.js"></script>
|
||||
<script src="/static/admin/js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script>
|
||||
$("#changePwd").submit(function () {
|
||||
if ($("input[name='oldpwd']").val() == '') lightyear.notify('旧密码为空', 'danger', 3000, 'mdi mdi-emoticon-neutral', 'top', 'center');
|
||||
|
|
|
|||
|
|
@ -67,10 +67,10 @@
|
|||
|
||||
{include file="public/footer"/}
|
||||
<!--以下是tree-grid的使用示例-->
|
||||
<link href="__ADMIN_PATH__js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<link href="/static/admin/js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="/static/admin/js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="__ADMIN_PATH__js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
src="/static/admin/js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
// tree-grid使用
|
||||
var $treeTable = $('.tree-table');
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@
|
|||
|
||||
{include file="public/footer"/}
|
||||
<!--以下是tree-grid的使用示例-->
|
||||
<link href="__ADMIN_PATH__js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<link href="/static/admin/js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="/static/admin/js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="__ADMIN_PATH__js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
src="/static/admin/js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
// tree-grid使用
|
||||
var $treeTable = $('.tree-table');
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<title>添加文章 - {:system_config('title')}后台管理系统</title>
|
||||
{include file="public/header" /}
|
||||
<!--标签插件-->
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
<link rel="stylesheet" href="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="row">
|
||||
|
|
@ -155,11 +155,11 @@
|
|||
</div>
|
||||
{include file="public/footer"/}
|
||||
<!--select2-->
|
||||
<script src="__ADMIN_PATH__js/select2.min.js"></script>
|
||||
<script src="/static/admin/js/select2.min.js"></script>
|
||||
<!--富文本输入框-->
|
||||
<script src="__ADMIN_PATH__js/tinymce/tinymce.min.js"></script>
|
||||
<script src="/static/admin/js/tinymce/tinymce.min.js"></script>
|
||||
<!--标签-->
|
||||
<script src="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script src="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$('#tag').select2();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<title>编辑文章 - {:system_config('title')}后台管理系统</title>
|
||||
{include file="public/header" /}
|
||||
<!--标签插件-->
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
<link rel="stylesheet" href="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="row">
|
||||
|
|
@ -176,11 +176,11 @@
|
|||
</div>
|
||||
{include file="public/footer"/}
|
||||
<!--select2-->
|
||||
<script src="__ADMIN_PATH__js/select2.min.js"></script>
|
||||
<script src="/static/admin/js/select2.min.js"></script>
|
||||
<!--富文本输入框-->
|
||||
<script src="__ADMIN_PATH__js/tinymce/tinymce.min.js"></script>
|
||||
<script src="/static/admin/js/tinymce/tinymce.min.js"></script>
|
||||
<!--标签-->
|
||||
<script src="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script src="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$('#tag').select2();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<title>添加分类 - {:system_config('title')}后台管理系统</title>
|
||||
{include file="public/header" /}
|
||||
<!--标签插件-->
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
<link rel="stylesheet" href="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="row">
|
||||
|
|
@ -112,11 +112,11 @@
|
|||
</div>
|
||||
{include file="public/footer"/}
|
||||
<!--select2-->
|
||||
<script src="__ADMIN_PATH__js/select2.min.js"></script>
|
||||
<script src="/static/admin/js/select2.min.js"></script>
|
||||
<!--富文本输入框-->
|
||||
<script src="__ADMIN_PATH__js/tinymce/tinymce.min.js"></script>
|
||||
<script src="/static/admin/js/tinymce/tinymce.min.js"></script>
|
||||
<!--标签-->
|
||||
<script src="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script src="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$('#tag').select2();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<title>添加文章 - {:system_config('title')}后台管理系统</title>
|
||||
{include file="public/header" /}
|
||||
<!--标签插件-->
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
<link rel="stylesheet" href="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="row">
|
||||
|
|
@ -122,11 +122,11 @@
|
|||
</div>
|
||||
{include file="public/footer"/}
|
||||
<!--select2-->
|
||||
<script src="__ADMIN_PATH__js/select2.min.js"></script>
|
||||
<script src="/static/admin/js/select2.min.js"></script>
|
||||
<!--富文本输入框-->
|
||||
<script src="__ADMIN_PATH__js/tinymce/tinymce.min.js"></script>
|
||||
<script src="/static/admin/js/tinymce/tinymce.min.js"></script>
|
||||
<!--标签-->
|
||||
<script src="__ADMIN_PATH__js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script src="/static/admin/js/jquery-tags-input/jquery.tagsinput.min.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$('#tag').select2();
|
||||
|
|
|
|||
|
|
@ -55,10 +55,10 @@
|
|||
|
||||
{include file="public/footer"/}
|
||||
<!--以下是tree-grid的使用示例-->
|
||||
<link href="__ADMIN_PATH__js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<link href="/static/admin/js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="/static/admin/js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="__ADMIN_PATH__js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
src="/static/admin/js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
// tree-grid使用
|
||||
var $treeTable = $('#tree-table');
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<style>
|
||||
/**
|
||||
* 图标示例样式(这里单独放页面中)
|
||||
|
|
@ -2150,9 +2150,9 @@
|
|||
<!-- .row -->
|
||||
|
||||
</div>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/main.min.js"></script>
|
||||
<script>
|
||||
var parentinputname = '{$Request.param.fodder}';
|
||||
$('.example-icons div').on('click', function () {
|
||||
|
|
|
|||
|
|
@ -196,9 +196,9 @@
|
|||
<input id="fileUpload" type="file" name="file" style="display: none;" @change="upload" accept="image/*">
|
||||
</div>
|
||||
{include file="public/footer"/}
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-treeview/bootstrap-treeview.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/vue/vue.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/vue-page/pageination.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-treeview/bootstrap-treeview.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/vue/vue.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/vue-page/pageination.js"></script>
|
||||
<script>
|
||||
let id = '', pid = 0;
|
||||
let parentinputname = '{$Request.param.fodder}';
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/bootstrap-multitabs/multitabs.min.css">
|
||||
<link href="__ADMIN_PATH__css/animate.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.css">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/admin/js/bootstrap-multitabs/multitabs.min.css">
|
||||
<link href="/static/admin/css/animate.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/admin/js/jconfirm/jquery-confirm.min.css">
|
||||
<style>
|
||||
body {
|
||||
opacity: 0;
|
||||
|
|
@ -38,9 +38,8 @@
|
|||
<aside class="lyear-layout-sidebar">
|
||||
<!-- logo -->
|
||||
<div id="logo" class="sidebar-header">
|
||||
<a href="/admin/index/index.html"><img src="__ADMIN_PATH__images/logo-sidebar.png"
|
||||
title="{:system_config('title")}后台管理系统"
|
||||
alt="{:system_config('title")}后台管理系统" /></a>
|
||||
<a href="/admin/index/index.html"><img src="/static/admin/images/logo-sidebar.png" title="{:system_config('title')}后台管理系统"
|
||||
alt="{:system_config('title')}后台管理系统" /></a>
|
||||
</div>
|
||||
<div class="lyear-layout-sidebar-scroll">
|
||||
<nav class="sidebar-main">
|
||||
|
|
@ -95,9 +94,9 @@
|
|||
<ul class="topbar-right">
|
||||
<li class="dropdown dropdown-profile">
|
||||
<a href="javascript:void(0)" data-toggle="dropdown">
|
||||
<img class="img-avatar img-avatar-48 m-r-10" src="{$adminInfo.avatar}"
|
||||
alt="{$adminInfo.nickname}" style="width: 32px;height: 32px;"/>
|
||||
<span>{$adminInfo.nickname} <span class="caret"></span></span>
|
||||
<img class="img-avatar img-avatar-48 m-r-10" src="{$admin_info.avatar}"
|
||||
alt="{$admin_info.nickname}" style="width: 32px;height: 32px;"/>
|
||||
<span>{$admin_info.nickname} <span class="caret"></span></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a class="multitabs" data-url="/admin/admin/profile" href="javascript:void(0)"><i
|
||||
|
|
@ -261,14 +260,14 @@
|
|||
<!--End 页面主要内容-->
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-multitabs/multitabs.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.cookie.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/index.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/lightyear.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-multitabs/multitabs.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.cookie.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/index.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/lightyear.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jconfirm/jquery-confirm.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -200,12 +200,12 @@
|
|||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/main.min.js"></script>
|
||||
|
||||
<!--图表插件-->
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/Chart.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/Chart.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function (e) {
|
||||
var $dashChartBarsCnt = jQuery('.js-chartjs-bars')[0].getContext('2d'),
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.lyear-wrapper {
|
||||
position: relative;
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
</style>
|
||||
</head>
|
||||
|
||||
<body style="background: url('__ADMIN_PATH__images/back.jpg')">
|
||||
<body style="background: url('/static/admin/images/back.jpg')">
|
||||
<div class="row lyear-wrapper">
|
||||
<div class="lyear-login">
|
||||
<div class="login-center">
|
||||
|
|
@ -65,23 +65,8 @@
|
|||
<div class="col-xs-8 text-left">
|
||||
<a href="/index" style="font-size: 20px;color: #1c1e2f;">{:system_config('title')}后台管理系统</a>
|
||||
</div>
|
||||
<!-- <div class="col-xs-4 text-right">-->
|
||||
<!-- <a onclick="changeLoginType()" style="cursor: pointer;" id="loginLabel">扫码登录</a>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 212px;width: 100%;display: none;" id="scan">
|
||||
<div style="padding: 10px;text-align: center;position: relative;">
|
||||
<img src="__ADMIN_PATH__images/wechat.png" id="qrcode" width="160" height="160" alt="请打开微信扫一扫登录"
|
||||
title="请打开微信扫一扫登录">
|
||||
<div style="position: absolute;top: 0;left: 0;width: 100%;padding: 10px;display: none;"
|
||||
class="lab_qrcode_text">
|
||||
<p style="height: 160px;width: 160px;margin: auto;line-height: 160px;background: rgba(0, 0, 0, 0.6);color: #fff;font-size: 16px;cursor: pointer;"
|
||||
title="点击重新加载" onclick="openws();">二维码已过期</p>
|
||||
</div>
|
||||
</div>
|
||||
<p style="text-align: center;margin-top: 10px;font-size: 20px;">请打开微信扫一扫登录</p>
|
||||
</div>
|
||||
<form action="#" method="post" style="height: 212px;width: 100%;" id="loginFrm">
|
||||
<div class="form-group has-feedback feedback-left has-username" style="margin-bottom: 20px;">
|
||||
<input type="text" placeholder="请输入您的用户名" class="form-control" name="username" id="username"/>
|
||||
|
|
@ -113,106 +98,21 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/login.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script>
|
||||
let scand = false;
|
||||
let type = false;
|
||||
let protocol = location.protocol === 'https:' ? "wss://" + window.location.host + "/wss" : "ws://" + window.location.host + ":1996";
|
||||
|
||||
// 切换登录
|
||||
function changeLoginType() {
|
||||
$("#loginFrm").toggle();
|
||||
$("#scan").toggle();
|
||||
type = !type;
|
||||
if (type) $("#loginLabel").text("账号登录");
|
||||
else $("#loginLabel").text("扫码登录");
|
||||
if (!scand && type) {
|
||||
scand = true;
|
||||
openws();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取cookie,单个或者多个
|
||||
* @param name
|
||||
* @returns {any[]|any}
|
||||
*/
|
||||
function getCookies(name) {
|
||||
var _cookies = document.cookie.split(";");
|
||||
const value = new Array();
|
||||
if (!Array.isArray(name)) _name = new Array(name);
|
||||
for (var i = 0; i < _cookies.length; i++) {
|
||||
var _cookie = _cookies[i].trim().split("=");
|
||||
if (_name.includes(_cookie[0].trim())) value[_cookie[0].trim()] = _cookie[1].trim();
|
||||
}
|
||||
if (Array.isArray(name)) return value;
|
||||
else return value[name];
|
||||
}
|
||||
|
||||
let ws = new WebSocket(protocol);
|
||||
|
||||
// 打开 ws
|
||||
function openws() {
|
||||
let time = null, tryNum = 10;
|
||||
if (ws.readyState == 3) ws = new WebSocket(protocol);
|
||||
// 关闭遮罩
|
||||
$(".lab_qrcode_text").hide();
|
||||
try {
|
||||
ws.send('{"type":"qrcode","token":"' + getCookies("PHPSESSID") + '"}');
|
||||
} catch (err) {
|
||||
// 重试10次,每次之间间隔3秒
|
||||
time = setTimeout(function () {
|
||||
if (tryNum > 0) {
|
||||
tryNum--;
|
||||
if (ws.readyState == 1) {
|
||||
clearTimeout(time);
|
||||
ws.send('{"type":"qrcode","token":"' + getCookies("PHPSESSID") + '"}');
|
||||
}
|
||||
} else {
|
||||
clearTimeout(time);
|
||||
scand = false;
|
||||
console.error("连接超时.");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
ws.onmessage = function (e) {
|
||||
data = JSON.parse(e.data);
|
||||
event(data['type'], data['data'])
|
||||
}
|
||||
}
|
||||
|
||||
let t1;
|
||||
|
||||
// 事件处理
|
||||
function event(type, data) {
|
||||
switch (type) {
|
||||
case 'qrcode':
|
||||
t1 = setInterval(function () {
|
||||
ws.send('{"type":"valid","token":"' + getCookies("PHPSESSID") + '"}');
|
||||
}, 2000);
|
||||
$("#qrcode").attr("src", data['src']);
|
||||
break;
|
||||
case 'valid':
|
||||
switch (data['status']) {
|
||||
case 200:
|
||||
clearInterval(t1);
|
||||
window.location = "/admin/index/index";
|
||||
break;
|
||||
case 300:
|
||||
break;
|
||||
case 400:
|
||||
clearInterval(t1);
|
||||
scand = false;
|
||||
$(".lab_qrcode_text").show();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'timeout':
|
||||
clearInterval(t1);
|
||||
scand = false;
|
||||
}
|
||||
// 账号登录
|
||||
function btnLogin() {
|
||||
if ($("#username").val() == "") {$(".has-username").addClass("has-error");return false;}
|
||||
if ($("#password").val() == "") {$(".has-password").addClass("has-error");return false;}
|
||||
if ($("#captchas").val() == "") {$(".has-captchas").addClass("has-error");return false;}
|
||||
$.post(url="/admin/login/verify",$("#loginFrm").serialize(),function (res) {
|
||||
if (res.status == 200) window.location = "/admin/index/index";
|
||||
else alert(res.msg);
|
||||
return true;
|
||||
});
|
||||
$("#captcha").attr("src",'/admin/login/captcha?d='+Math.random());
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
|
|
@ -49,8 +49,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">;</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
|
|
@ -47,8 +47,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">;</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/iframe.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-table/bootstrap-table.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-table/bootstrap-table-zh-CN.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/iframe.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-table/bootstrap-table.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-table/bootstrap-table-zh-CN.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="__ADMIN_PATH__js/x-editable/1.5.1/bootstrap3-editable/js/bootstrap-editable.min.js"></script>
|
||||
src="/static/admin/js/x-editable/1.5.1/bootstrap3-editable/js/bootstrap-editable.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="__ADMIN_PATH__js/bootstrap-table/extensions/editable/bootstrap-table-editable.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/main.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-datetimepicker/moment.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-datetimepicker/locale/zh-cn.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/lightyear.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/layer/layer.js"></script>
|
||||
src="/static/admin/js/bootstrap-table/extensions/editable/bootstrap-table-editable.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-datetimepicker/moment.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-datetimepicker/locale/zh-cn.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/lightyear.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/layer/layer.js"></script>
|
||||
<script type="text/javascript">
|
||||
Array.prototype.remove = function (val) {
|
||||
var index = this.indexOf(val);
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?=$form->getTitle()?></title>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/formbuilder/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/formbuilder/vue.min.js"></script>
|
||||
<link href="__ADMIN_PATH__js/formbuilder/iview.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/formbuilder/iview.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/formbuilder/form-create.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/formbuilder/province_city.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/formbuilder/province_city_area.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/formbuilder/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/formbuilder/vue.min.js"></script>
|
||||
<link href="/static/admin/js/formbuilder/iview.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="/static/admin/js/formbuilder/iview.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/formbuilder/form-create.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/formbuilder/province_city.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/formbuilder/province_city_area.js"></script>
|
||||
<style>
|
||||
.ivu-modal-footer {
|
||||
display: none;
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/animate.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__js/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__js/x-editable/1.5.1/bootstrap3-editable/css/bootstrap-editable.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/animate.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/js/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/js/x-editable/1.5.1/bootstrap3-editable/css/bootstrap-editable.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/js/jconfirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.form-inline .form-group label {
|
||||
padding: 0 10px;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
|
|
@ -74,8 +74,8 @@
|
|||
})();
|
||||
</script>
|
||||
{/if}
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">;</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -8,10 +8,10 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.css">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/admin/js/jconfirm/jquery-confirm.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
|
|
@ -104,12 +104,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/main.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/lightyear.js"></script>
|
||||
<script src="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/lightyear.js"></script>
|
||||
<script src="/static/admin/js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 选择文件
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.css">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/admin/js/jconfirm/jquery-confirm.min.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
|
|
@ -49,12 +49,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<!--消息提示-->
|
||||
<script src="__ADMIN_PATH__js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/lightyear.js"></script>
|
||||
<script src="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script src="/static/admin/js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/lightyear.js"></script>
|
||||
<script src="/static/admin/js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script>
|
||||
function clearCache() {
|
||||
$.alert({
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@
|
|||
<meta name="keywords" content="{:system_config('keywords')}">
|
||||
<meta name="description" content="{:system_config('description')}">
|
||||
<meta name="author" content="{:system_config('author')}">
|
||||
<link href="__ADMIN_PATH__css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="__ADMIN_PATH__css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.css">
|
||||
<link href="/static/admin/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/static/admin/css/style.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/admin/js/jconfirm/jquery-confirm.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
|
|
@ -89,12 +89,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/main.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="__ADMIN_PATH__js/lightyear.js"></script>
|
||||
<script src="__ADMIN_PATH__js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/bootstrap-notify.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/lightyear.js"></script>
|
||||
<script src="/static/admin/js/jconfirm/jquery-confirm.min.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* 选择文件
|
||||
|
|
|
|||
|
|
@ -26,34 +26,14 @@ class Admin extends BaseModel
|
|||
public static function login(string $name, string $pwd): bool
|
||||
{
|
||||
$info = self::where("name|tel", "=", $name)->find();
|
||||
if (!$info) return self::setErrorInfo("登录账号不存在");
|
||||
if(!empty($info->avatar)) $info->avatar = url($info->avatar);
|
||||
if (empty($info)) return self::setErrorInfo("登录账号不存在");
|
||||
if ($info['password'] != md5(md5($pwd))) return self::setErrorInfo("密码不正确!");
|
||||
if ($info['status'] != 1) return self::setErrorInfo("账号已被冻结!");
|
||||
self::setLoginInfo($info);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信扫码登录后台
|
||||
* @param array $message
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*/
|
||||
public static function wechatLogin(array $message)
|
||||
{
|
||||
$param = param_to_array(str_replace("qrscene_", "", $message['EventKey']));
|
||||
// 查找用户uid
|
||||
$uid = WechatUser::getUidByOpenId($message['FromUserName']);
|
||||
if (!$uid) return ['status' => 101];
|
||||
$adminInfo = self::where("uid", $uid)->find();
|
||||
if (!$adminInfo) return ['status' => 102];
|
||||
Cache::store('redis')->set("info_" . $param['token'], $adminInfo, 60);
|
||||
return ['status' => 100];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置登录信息
|
||||
* @param $info
|
||||
|
|
|
|||
|
|
@ -22,7 +22,20 @@ class AdminAuth extends BaseModel
|
|||
*/
|
||||
public static function getAuthId(string $module, string $controller, string $action): int
|
||||
{
|
||||
return self::where("module", $module)->where("controller", $controller)->where("action", $action)->value('id') ?: -1;
|
||||
//先检查缓存是否存在
|
||||
$authList = cache(AdminAuth::getAuthCacheKey());
|
||||
//不存在则更新缓存
|
||||
if ($authList === null) {
|
||||
$authList = self::column('module,controller,action', 'id');
|
||||
$temp = [];
|
||||
foreach ($authList as $key => $value) {
|
||||
$temp[$value['module'] . '_' . $value['controller'] . '_' . $value['action']] = $key;
|
||||
}
|
||||
$authList = $temp;
|
||||
cache(AdminAuth::getAuthCacheKey(), $authList, 24 * 60 * 60);
|
||||
unset($temp);
|
||||
}
|
||||
return $authList[$module . '_' . $controller . '_' . $action] ?? -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -51,18 +64,6 @@ class AdminAuth extends BaseModel
|
|||
return $data->toArray() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单列表缓存key
|
||||
* @param $adminId
|
||||
* @return string
|
||||
* @author 李玉坤
|
||||
* @date 2021-06-09 17:24
|
||||
*/
|
||||
public static function getMenuCacheKey($adminId)
|
||||
{
|
||||
return 'menu:List:' . $adminId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限列表
|
||||
* @param $where
|
||||
|
|
@ -104,6 +105,34 @@ class AdminAuth extends BaseModel
|
|||
return $data->toArray() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单列表缓存key
|
||||
* @param $adminId
|
||||
* @return string
|
||||
* @author 李玉坤
|
||||
* @date 2021-06-09 17:24
|
||||
*/
|
||||
public static function getMenuCacheKey($adminId)
|
||||
{
|
||||
return 'menu:List:' . $adminId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @author 李玉坤
|
||||
* @date 2021-06-15 11:11
|
||||
*/
|
||||
public static function getAuthCacheKey()
|
||||
{
|
||||
return 'auth:key:list';
|
||||
}
|
||||
|
||||
public static function clearCache($adminId)
|
||||
{
|
||||
cache(AdminAuth::getMenuCacheKey($adminId), null);
|
||||
cache(AdminAuth::getAuthCacheKey(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历选择项
|
||||
* @param array $data
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ trait ModelTrait
|
|||
}
|
||||
}
|
||||
|
||||
public static function all($function)
|
||||
{
|
||||
public static function all($function){
|
||||
$query = self::newQuery();
|
||||
$function($query);
|
||||
return $query->select();
|
||||
|
|
@ -154,10 +153,9 @@ trait ModelTrait
|
|||
* @param string $like 模糊查找 关键字
|
||||
* @return array
|
||||
*/
|
||||
public static function setWherePage($model = null, $where = [], $field = [], $fieldOr = [], $fun = null, $like = 'LIKE')
|
||||
{
|
||||
if (!is_array($where) || !is_array($field)) return false;
|
||||
if ($model === null) $model = new self();
|
||||
public static function setWherePage($model=null,$where=[],$field=[],$fieldOr=[],$fun=null,$like='LIKE'){
|
||||
if(!is_array($where) || !is_array($field)) return false;
|
||||
if($model===null) $model=new self();
|
||||
//处理等于行查询
|
||||
foreach ($field as $key => $item) {
|
||||
if (($count = strpos($item, '.')) === false) {
|
||||
|
|
@ -196,68 +194,59 @@ trait ModelTrait
|
|||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
private static function get_field($id, $str = '|')
|
||||
{
|
||||
if (is_array($id)) {
|
||||
$sql = "";
|
||||
$i = 0;
|
||||
foreach ($id as $val) {
|
||||
private static function get_field($id,$str='|'){
|
||||
if(is_array($id)){
|
||||
$sql="";
|
||||
$i=0;
|
||||
foreach($id as $val){
|
||||
$i++;
|
||||
if ($i < count($id)) {
|
||||
$sql .= $val . $str;
|
||||
} else {
|
||||
$sql .= $val;
|
||||
if($i<count($id)){
|
||||
$sql.=$val.$str;
|
||||
}else{
|
||||
$sql.=$val;
|
||||
}
|
||||
}
|
||||
return $sql;
|
||||
} else {
|
||||
return $sql;
|
||||
}else{
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件切割
|
||||
* @param string $order
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
public static function setOrder($order, $file = '-')
|
||||
{
|
||||
if (empty($order)) return '';
|
||||
return str_replace($file, ' ', $order);
|
||||
public static function setOrder($order,$file='-'){
|
||||
if(empty($order)) return '';
|
||||
return str_replace($file,' ',$order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取时间段之间的model
|
||||
* @param int|string $time
|
||||
* @param string $ceil
|
||||
* @return array
|
||||
*/
|
||||
public static function getModelTime($where, $model = null, $prefix = 'create_time', $data = 'data', $field = ' - ')
|
||||
{
|
||||
public static function getModelTime($where,$model=null,$prefix='add_time',$data='data',$field=' - '){
|
||||
if ($model == null) $model = new self;
|
||||
if (!isset($where[$data])) return $model;
|
||||
switch ($where[$data]) {
|
||||
case 'today':
|
||||
case 'week':
|
||||
case 'month':
|
||||
case 'year':
|
||||
case 'yesterday':
|
||||
$model = $model->whereTime($prefix, $where[$data]);
|
||||
break;
|
||||
if(!isset($where[$data])) return $model;
|
||||
switch ($where[$data]){
|
||||
case 'today':case 'week':case 'month':case 'year':case 'yesterday':
|
||||
$model=$model->whereTime($prefix,$where[$data]);
|
||||
break;
|
||||
case 'quarter':
|
||||
list($startTime, $endTime) = self::getMonth();
|
||||
list($startTime,$endTime)=self::getMonth();
|
||||
$model = $model->where($prefix, '>', strtotime($startTime));
|
||||
$model = $model->where($prefix, '<', strtotime($endTime));
|
||||
break;
|
||||
case 'lately7':
|
||||
$model = $model->where($prefix, 'between', [strtotime("-7 day"), time()]);
|
||||
$model = $model->where($prefix,'between',[strtotime("-7 day"),time()]);
|
||||
break;
|
||||
case 'lately30':
|
||||
$model = $model->where($prefix, 'between', [strtotime("-30 day"), time()]);
|
||||
$model = $model->where($prefix,'between',[strtotime("-30 day"),time()]);
|
||||
break;
|
||||
default:
|
||||
if (strstr($where[$data], $field) !== false) {
|
||||
if(strstr($where[$data],$field)!==false){
|
||||
list($startTime, $endTime) = explode($field, $where[$data]);
|
||||
$model = $model->where($prefix, '>', strtotime($startTime));
|
||||
$model = $model->where($prefix, '<', strtotime($endTime));
|
||||
|
|
@ -266,17 +255,14 @@ trait ModelTrait
|
|||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取去除html去除空格去除软回车,软换行,转换过后的字符串
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
public static function HtmlToMbStr($str)
|
||||
{
|
||||
return trim(strip_tags(str_replace(["\n", "\t", "\r", " ", " "], '', htmlspecialchars_decode($str))));
|
||||
public static function HtmlToMbStr($str){
|
||||
return trim(strip_tags(str_replace(["\n","\t","\r"," "," "],'',htmlspecialchars_decode($str))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取中文指定字节
|
||||
* @param string $str
|
||||
|
|
@ -285,31 +271,27 @@ trait ModelTrait
|
|||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
public static function getSubstrUTf8($str, $utf8len = 100, $chaet = 'UTF-8', $file = '....')
|
||||
{
|
||||
if (mb_strlen($str, $chaet) > $utf8len) {
|
||||
$str = mb_substr($str, 0, $utf8len, $chaet) . $file;
|
||||
public static function getSubstrUTf8($str,$utf8len=100,$chaet='UTF-8',$file='....'){
|
||||
if(mb_strlen($str,$chaet)>$utf8len){
|
||||
$str=mb_substr($str,0,$utf8len,$chaet).$file;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本季度 time
|
||||
* @param int|string $time
|
||||
* @param string $ceil
|
||||
* @return array
|
||||
*/
|
||||
public static function getMonth($time = '', $ceil = 0)
|
||||
{
|
||||
if ($ceil != 0)
|
||||
$season = ceil(date('n') / 3) - $ceil;
|
||||
public static function getMonth($time='',$ceil=0){
|
||||
if($ceil!=0)
|
||||
$season = ceil(date('n') /3)-$ceil;
|
||||
else
|
||||
$season = ceil(date('n') / 3);
|
||||
$firstday = date('Y-m-01', mktime(0, 0, 0, ($season - 1) * 3 + 1, 1, date('Y')));
|
||||
$lastday = date('Y-m-t', mktime(0, 0, 0, $season * 3, 1, date('Y')));
|
||||
return array($firstday, $lastday);
|
||||
$season = ceil(date('n') /3);
|
||||
$firstday=date('Y-m-01',mktime(0,0,0,($season - 1) *3 +1,1,date('Y')));
|
||||
$lastday=date('Y-m-t',mktime(0,0,0,$season * 3,1,date('Y')));
|
||||
return array($firstday,$lastday);
|
||||
}
|
||||
|
||||
/**
|
||||
* 高精度 加法
|
||||
* @param int|string $uid id
|
||||
|
|
@ -319,15 +301,15 @@ trait ModelTrait
|
|||
* @param int $acc 精度
|
||||
* @return bool
|
||||
*/
|
||||
public static function bcInc($key, $incField, $inc, $keyField = null, $acc = 2)
|
||||
public static function bcInc($key, $incField, $inc, $keyField = null, $acc=2)
|
||||
{
|
||||
if (!is_numeric($inc)) return false;
|
||||
if(!is_numeric($inc)) return false;
|
||||
$model = new self();
|
||||
if ($keyField === null) $keyField = $model->getPk();
|
||||
$result = self::where($keyField, $key)->find();
|
||||
if (!$result) return false;
|
||||
$new = bcadd($result[$incField], $inc, $acc);
|
||||
return false !== $model->where($keyField, $key)->update([$incField => $new]);
|
||||
if($keyField === null) $keyField = $model->getPk();
|
||||
$result = self::where($keyField,$key)->find();
|
||||
if(!$result) return false;
|
||||
$new = bcadd($result[$incField],$inc,$acc);
|
||||
return false !== $model->where($keyField,$key)->update([$incField=>$new]);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -341,16 +323,16 @@ trait ModelTrait
|
|||
* @param int $acc 精度
|
||||
* @return bool
|
||||
*/
|
||||
public static function bcDec($key, $decField, $dec, $keyField = null, $minus = false, $acc = 2)
|
||||
public static function bcDec($key, $decField, $dec, $keyField = null, $minus = false, $acc=2)
|
||||
{
|
||||
if (!is_numeric($dec)) return false;
|
||||
if(!is_numeric($dec)) return false;
|
||||
$model = new self();
|
||||
if ($keyField === null) $keyField = $model->getPk();
|
||||
$result = self::where($keyField, $key)->find();
|
||||
if (!$result) return false;
|
||||
if (!$minus && $result[$decField] < $dec) return false;
|
||||
$new = bcsub($result[$decField], $dec, $acc);
|
||||
return false !== $model->where($keyField, $key)->update([$decField => $new]);
|
||||
if($keyField === null) $keyField = $model->getPk();
|
||||
$result = self::where($keyField,$key)->find();
|
||||
if(!$result) return false;
|
||||
if(!$minus && $result[$decField] < $dec) return false;
|
||||
$new = bcsub($result[$decField],$dec,$acc);
|
||||
return false !== $model->where($keyField,$key)->update([$decField=>$new]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,20 +20,18 @@
|
|||
"topthink/framework": "^6.0.0",
|
||||
"topthink/think-orm": "^2.0",
|
||||
"topthink/think-multi-app": "^1.0",
|
||||
"topthink/think-view": "^1.0",
|
||||
"firebase/php-jwt": "^5.0",
|
||||
"xaboy/form-builder": "^2.0",
|
||||
"topthink/think-captcha": "^3.0",
|
||||
"phpmailer/phpmailer": "^6.1",
|
||||
"spatie/macroable": "^1.0"
|
||||
"spatie/macroable": "^1.0",
|
||||
"topthink/think-view": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"app\\": "app",
|
||||
"learn\\": "learn"
|
||||
"app\\": "app"
|
||||
},
|
||||
"psr-0": {
|
||||
"": "app/common/extend/"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ return [
|
|||
// 默认的路由变量规则
|
||||
'default_route_pattern' => '[\w\.]+',
|
||||
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
|
||||
'request_cache_key' => false,
|
||||
'request_cache' => false,
|
||||
// 请求缓存有效期
|
||||
'request_cache_expire' => null,
|
||||
// 全局请求缓存排除规则
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
// 账号登录
|
||||
function btnLogin() {
|
||||
if ($("#username").val() == "") {$(".has-username").addClass("has-error");return false;}
|
||||
if ($("#password").val() == "") {$(".has-password").addClass("has-error");return false;}
|
||||
if ($("#captchas").val() == "") {$(".has-captchas").addClass("has-error");return false;}
|
||||
$.post(url="/admin/login/verify",data={account:$("#username").val(),pwd:$("#password").val(),verify:$("#captchas").val()},function (res) {
|
||||
if (res.status == 200) window.location = "/admin/index/index";
|
||||
else alert(res.msg);
|
||||
return true;
|
||||
});
|
||||
$("#captcha").attr("src",'/admin/login/captcha?d='+Math.random());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit179ad4390eaa61356c3a52b9b610e467::getLoader();
|
||||
return ComposerAutoloaderInit4b57298e8d0e895486f3307a354a7e1a::getLoader();
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ $baseDir = dirname($vendorDir);
|
|||
|
||||
return array(
|
||||
'9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ return array(
|
|||
'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
|
||||
'think\\app\\' => array($vendorDir . '/topthink/think-multi-app/src'),
|
||||
'think\\' => array($vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-template/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/framework/src/think'),
|
||||
'learn\\' => array($baseDir . '/learn'),
|
||||
'app\\' => array($baseDir . '/app'),
|
||||
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
||||
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
||||
|
|
@ -28,7 +27,6 @@ return array(
|
|||
'League\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/src'),
|
||||
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
|
||||
'FormBuilder\\' => array($vendorDir . '/xaboy/form-builder/src'),
|
||||
'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
|
||||
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit179ad4390eaa61356c3a52b9b610e467
|
||||
class ComposerAutoloaderInit4b57298e8d0e895486f3307a354a7e1a
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
|
|
@ -24,15 +24,15 @@ class ComposerAutoloaderInit179ad4390eaa61356c3a52b9b610e467
|
|||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit179ad4390eaa61356c3a52b9b610e467', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInit4b57298e8d0e895486f3307a354a7e1a', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit179ad4390eaa61356c3a52b9b610e467', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit4b57298e8d0e895486f3307a354a7e1a', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit179ad4390eaa61356c3a52b9b610e467::getInitializer($loader));
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit4b57298e8d0e895486f3307a354a7e1a::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
|
|
@ -53,19 +53,19 @@ class ComposerAutoloaderInit179ad4390eaa61356c3a52b9b610e467
|
|||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit179ad4390eaa61356c3a52b9b610e467::$files;
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit4b57298e8d0e895486f3307a354a7e1a::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire179ad4390eaa61356c3a52b9b610e467($fileIdentifier, $file);
|
||||
composerRequire4b57298e8d0e895486f3307a354a7e1a($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire179ad4390eaa61356c3a52b9b610e467($fileIdentifier, $file)
|
||||
function composerRequire4b57298e8d0e895486f3307a354a7e1a($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
|
||||
class ComposerStaticInit4b57298e8d0e895486f3307a354a7e1a
|
||||
{
|
||||
public static $files = array (
|
||||
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
|
|
@ -25,10 +25,6 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
|
|||
'think\\app\\' => 10,
|
||||
'think\\' => 6,
|
||||
),
|
||||
'l' =>
|
||||
array (
|
||||
'learn\\' => 6,
|
||||
),
|
||||
'a' =>
|
||||
array (
|
||||
'app\\' => 4,
|
||||
|
|
@ -60,7 +56,6 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
|
|||
'F' =>
|
||||
array (
|
||||
'FormBuilder\\' => 12,
|
||||
'Firebase\\JWT\\' => 13,
|
||||
),
|
||||
'D' =>
|
||||
array (
|
||||
|
|
@ -89,10 +84,6 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
|
|||
2 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
||||
3 => __DIR__ . '/..' . '/topthink/framework/src/think',
|
||||
),
|
||||
'learn\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/learn',
|
||||
),
|
||||
'app\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/app',
|
||||
|
|
@ -161,10 +152,6 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/xaboy/form-builder/src',
|
||||
),
|
||||
'Firebase\\JWT\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
|
||||
),
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
|
||||
|
|
@ -190,10 +177,10 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
|
|||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit179ad4390eaa61356c3a52b9b610e467::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit179ad4390eaa61356c3a52b9b610e467::$prefixDirsPsr4;
|
||||
$loader->fallbackDirsPsr0 = ComposerStaticInit179ad4390eaa61356c3a52b9b610e467::$fallbackDirsPsr0;
|
||||
$loader->classMap = ComposerStaticInit179ad4390eaa61356c3a52b9b610e467::$classMap;
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit4b57298e8d0e895486f3307a354a7e1a::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit4b57298e8d0e895486f3307a354a7e1a::$prefixDirsPsr4;
|
||||
$loader->fallbackDirsPsr0 = ComposerStaticInit4b57298e8d0e895486f3307a354a7e1a::$fallbackDirsPsr0;
|
||||
$loader->classMap = ComposerStaticInit4b57298e8d0e895486f3307a354a7e1a::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,72 +170,6 @@
|
|||
],
|
||||
"install-path": "../doctrine/lexer"
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"version": "v5.5.1",
|
||||
"version_normalized": "5.5.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/firebase/php-jwt.git",
|
||||
"reference": "83b609028194aa042ea33b5af2d41a7427de80e6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/83b609028194aa042ea33b5af2d41a7427de80e6",
|
||||
"reference": "83b609028194aa042ea33b5af2d41a7427de80e6",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": ">=4.8 <=9"
|
||||
},
|
||||
"suggest": {
|
||||
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||
},
|
||||
"time": "2021-11-08T20:18:51+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Firebase\\JWT\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Neuman Vong",
|
||||
"email": "neuman+pear@twilio.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Anant Narayanan",
|
||||
"email": "anant@php.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||
"homepage": "https://github.com/firebase/php-jwt",
|
||||
"keywords": [
|
||||
"jwt",
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||
"source": "https://github.com/firebase/php-jwt/tree/v5.5.1"
|
||||
},
|
||||
"install-path": "../firebase/php-jwt"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "1.1.9",
|
||||
|
|
@ -1435,12 +1369,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/framework.git",
|
||||
"reference": "109ade68d6ad3b10af5c8d9d8f53161a513eec18"
|
||||
"reference": "d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/framework/zipball/109ade68d6ad3b10af5c8d9d8f53161a513eec18",
|
||||
"reference": "109ade68d6ad3b10af5c8d9d8f53161a513eec18",
|
||||
"url": "https://api.github.com/repos/top-think/framework/zipball/d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb",
|
||||
"reference": "d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
|
|
@ -1468,7 +1402,7 @@
|
|||
"mockery/mockery": "^1.2",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
},
|
||||
"time": "2021-12-30T13:54:49+00:00",
|
||||
"time": "2021-12-31T09:14:28+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -1568,17 +1502,17 @@
|
|||
},
|
||||
{
|
||||
"name": "topthink/think-helper",
|
||||
"version": "v3.1.5",
|
||||
"version_normalized": "3.1.5.0",
|
||||
"version": "v3.1.6",
|
||||
"version_normalized": "3.1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/think-helper.git",
|
||||
"reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905"
|
||||
"reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/think-helper/zipball/f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
|
||||
"reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
|
||||
"url": "https://api.github.com/repos/top-think/think-helper/zipball/769acbe50a4274327162f9c68ec2e89a38eb2aff",
|
||||
"reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
|
|
@ -1590,7 +1524,10 @@
|
|||
"require": {
|
||||
"php": ">=7.1.0"
|
||||
},
|
||||
"time": "2021-06-21T06:17:31+00:00",
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"time": "2021-12-15T04:27:55+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -1614,7 +1551,7 @@
|
|||
"description": "The ThinkPHP6 Helper Package",
|
||||
"support": {
|
||||
"issues": "https://github.com/top-think/think-helper/issues",
|
||||
"source": "https://github.com/top-think/think-helper/tree/v3.1.5"
|
||||
"source": "https://github.com/top-think/think-helper/tree/v3.1.6"
|
||||
},
|
||||
"install-path": "../topthink/think-helper"
|
||||
},
|
||||
|
|
@ -1677,17 +1614,17 @@
|
|||
},
|
||||
{
|
||||
"name": "topthink/think-orm",
|
||||
"version": "v2.0.46",
|
||||
"version_normalized": "2.0.46.0",
|
||||
"version": "v2.0.47",
|
||||
"version_normalized": "2.0.47.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/think-orm.git",
|
||||
"reference": "51ec287abdb99521dbbc66526b114f2e32b8fff4"
|
||||
"reference": "e69151fba9dd21f86e392a0ae208825904d6d49a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/think-orm/zipball/51ec287abdb99521dbbc66526b114f2e32b8fff4",
|
||||
"reference": "51ec287abdb99521dbbc66526b114f2e32b8fff4",
|
||||
"url": "https://api.github.com/repos/top-think/think-orm/zipball/e69151fba9dd21f86e392a0ae208825904d6d49a",
|
||||
"reference": "e69151fba9dd21f86e392a0ae208825904d6d49a",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
|
|
@ -1707,7 +1644,7 @@
|
|||
"require-dev": {
|
||||
"phpunit/phpunit": "^7|^8|^9.5"
|
||||
},
|
||||
"time": "2021-12-29T05:48:27+00:00",
|
||||
"time": "2021-12-31T06:12:13+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
@ -1735,7 +1672,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/top-think/think-orm/issues",
|
||||
"source": "https://github.com/top-think/think-orm/tree/v2.0.46"
|
||||
"source": "https://github.com/top-think/think-orm/tree/v2.0.47"
|
||||
},
|
||||
"install-path": "../topthink/think-orm"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'b719a4b772e06fd2165da8eb317ef17bc73126ed',
|
||||
'reference' => 'ae98df79618898655c1e41dedd3b193518617109',
|
||||
'name' => 'topthink/think',
|
||||
'dev' => true,
|
||||
),
|
||||
|
|
@ -28,15 +28,6 @@
|
|||
'reference' => 'e864bbf5904cb8f5bb334f99209b48018522f042',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'firebase/php-jwt' => array(
|
||||
'pretty_version' => 'v5.5.1',
|
||||
'version' => '5.5.1.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../firebase/php-jwt',
|
||||
'aliases' => array(),
|
||||
'reference' => '83b609028194aa042ea33b5af2d41a7427de80e6',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'league/flysystem' => array(
|
||||
'pretty_version' => '1.1.9',
|
||||
'version' => '1.1.9.0',
|
||||
|
|
@ -187,7 +178,7 @@
|
|||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../topthink/framework',
|
||||
'aliases' => array(),
|
||||
'reference' => '109ade68d6ad3b10af5c8d9d8f53161a513eec18',
|
||||
'reference' => 'd9cadb6971ae92ff85ba5f2be77a40b0ad5718fb',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think' => array(
|
||||
|
|
@ -196,7 +187,7 @@
|
|||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'b719a4b772e06fd2165da8eb317ef17bc73126ed',
|
||||
'reference' => 'ae98df79618898655c1e41dedd3b193518617109',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-captcha' => array(
|
||||
|
|
@ -209,12 +200,12 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-helper' => array(
|
||||
'pretty_version' => 'v3.1.5',
|
||||
'version' => '3.1.5.0',
|
||||
'pretty_version' => 'v3.1.6',
|
||||
'version' => '3.1.6.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../topthink/think-helper',
|
||||
'aliases' => array(),
|
||||
'reference' => 'f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905',
|
||||
'reference' => '769acbe50a4274327162f9c68ec2e89a38eb2aff',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-multi-app' => array(
|
||||
|
|
@ -227,12 +218,12 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-orm' => array(
|
||||
'pretty_version' => 'v2.0.46',
|
||||
'version' => '2.0.46.0',
|
||||
'pretty_version' => 'v2.0.47',
|
||||
'version' => '2.0.47.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../topthink/think-orm',
|
||||
'aliases' => array(),
|
||||
'reference' => '51ec287abdb99521dbbc66526b114f2e32b8fff4',
|
||||
'reference' => 'e69151fba9dd21f86e392a0ae208825904d6d49a',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-template' => array(
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
Copyright (c) 2011, Neuman Vong
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of other
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
[](https://travis-ci.org/firebase/php-jwt)
|
||||
[](https://packagist.org/packages/firebase/php-jwt)
|
||||
[](https://packagist.org/packages/firebase/php-jwt)
|
||||
[](https://packagist.org/packages/firebase/php-jwt)
|
||||
|
||||
PHP-JWT
|
||||
=======
|
||||
A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519).
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Use composer to manage your dependencies and download PHP-JWT:
|
||||
|
||||
```bash
|
||||
composer require firebase/php-jwt
|
||||
```
|
||||
|
||||
Optionally, install the `paragonie/sodium_compat` package from composer if your
|
||||
php is < 7.2 or does not have libsodium installed:
|
||||
|
||||
```bash
|
||||
composer require paragonie/sodium_compat
|
||||
```
|
||||
|
||||
Example
|
||||
-------
|
||||
```php
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
$key = "example_key";
|
||||
$payload = array(
|
||||
"iss" => "http://example.org",
|
||||
"aud" => "http://example.com",
|
||||
"iat" => 1356999524,
|
||||
"nbf" => 1357000000
|
||||
);
|
||||
|
||||
/**
|
||||
* IMPORTANT:
|
||||
* You must specify supported algorithms for your application. See
|
||||
* https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
|
||||
* for a list of spec-compliant algorithms.
|
||||
*/
|
||||
$jwt = JWT::encode($payload, $key, 'HS256');
|
||||
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||
|
||||
print_r($decoded);
|
||||
|
||||
/*
|
||||
NOTE: This will now be an object instead of an associative array. To get
|
||||
an associative array, you will need to cast it as such:
|
||||
*/
|
||||
|
||||
$decoded_array = (array) $decoded;
|
||||
|
||||
/**
|
||||
* You can add a leeway to account for when there is a clock skew times between
|
||||
* the signing and verifying servers. It is recommended that this leeway should
|
||||
* not be bigger than a few minutes.
|
||||
*
|
||||
* Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
|
||||
*/
|
||||
JWT::$leeway = 60; // $leeway in seconds
|
||||
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||
```
|
||||
Example with RS256 (openssl)
|
||||
----------------------------
|
||||
```php
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
$privateKey = <<<EOD
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn
|
||||
vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9
|
||||
5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB
|
||||
AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz
|
||||
bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J
|
||||
Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1
|
||||
cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5
|
||||
5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck
|
||||
ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe
|
||||
k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb
|
||||
qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k
|
||||
eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm
|
||||
B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
EOD;
|
||||
|
||||
$publicKey = <<<EOD
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H
|
||||
4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t
|
||||
0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4
|
||||
ehde/zUxo6UvS7UrBQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
EOD;
|
||||
|
||||
$payload = array(
|
||||
"iss" => "example.org",
|
||||
"aud" => "example.com",
|
||||
"iat" => 1356999524,
|
||||
"nbf" => 1357000000
|
||||
);
|
||||
|
||||
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
||||
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||
|
||||
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
|
||||
|
||||
/*
|
||||
NOTE: This will now be an object instead of an associative array. To get
|
||||
an associative array, you will need to cast it as such:
|
||||
*/
|
||||
|
||||
$decoded_array = (array) $decoded;
|
||||
echo "Decode:\n" . print_r($decoded_array, true) . "\n";
|
||||
```
|
||||
|
||||
Example with a passphrase
|
||||
-------------------------
|
||||
|
||||
```php
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
// Your passphrase
|
||||
$passphrase = '[YOUR_PASSPHRASE]';
|
||||
|
||||
// Your private key file with passphrase
|
||||
// Can be generated with "ssh-keygen -t rsa -m pem"
|
||||
$privateKeyFile = '/path/to/key-with-passphrase.pem';
|
||||
|
||||
// Create a private key of type "resource"
|
||||
$privateKey = openssl_pkey_get_private(
|
||||
file_get_contents($privateKeyFile),
|
||||
$passphrase
|
||||
);
|
||||
|
||||
$payload = array(
|
||||
"iss" => "example.org",
|
||||
"aud" => "example.com",
|
||||
"iat" => 1356999524,
|
||||
"nbf" => 1357000000
|
||||
);
|
||||
|
||||
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
||||
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||
|
||||
// Get public key from the private key, or pull from from a file.
|
||||
$publicKey = openssl_pkey_get_details($privateKey)['key'];
|
||||
|
||||
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
|
||||
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
|
||||
```
|
||||
|
||||
Example with EdDSA (libsodium and Ed25519 signature)
|
||||
----------------------------
|
||||
```php
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
// Public and private keys are expected to be Base64 encoded. The last
|
||||
// non-empty line is used so that keys can be generated with
|
||||
// sodium_crypto_sign_keypair(). The secret keys generated by other tools may
|
||||
// need to be adjusted to match the input expected by libsodium.
|
||||
|
||||
$keyPair = sodium_crypto_sign_keypair();
|
||||
|
||||
$privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
|
||||
|
||||
$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
|
||||
|
||||
$payload = array(
|
||||
"iss" => "example.org",
|
||||
"aud" => "example.com",
|
||||
"iat" => 1356999524,
|
||||
"nbf" => 1357000000
|
||||
);
|
||||
|
||||
$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
|
||||
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||
|
||||
$decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
|
||||
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
|
||||
````
|
||||
|
||||
Using JWKs
|
||||
----------
|
||||
|
||||
```php
|
||||
use Firebase\JWT\JWK;
|
||||
use Firebase\JWT\JWT;
|
||||
|
||||
// Set of keys. The "keys" key is required. For example, the JSON response to
|
||||
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
|
||||
$jwks = ['keys' => []];
|
||||
|
||||
// JWK::parseKeySet($jwks) returns an associative array of **kid** to private
|
||||
// key. Pass this as the second parameter to JWT::decode.
|
||||
// NOTE: The deprecated $supportedAlgorithm must be supplied when parsing from JWK.
|
||||
JWT::decode($payload, JWK::parseKeySet($jwks), $supportedAlgorithm);
|
||||
```
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
#### 5.0.0 / 2017-06-26
|
||||
- Support RS384 and RS512.
|
||||
See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
|
||||
- Add an example for RS256 openssl.
|
||||
See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
|
||||
- Detect invalid Base64 encoding in signature.
|
||||
See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
|
||||
- Update `JWT::verify` to handle OpenSSL errors.
|
||||
See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
|
||||
- Add `array` type hinting to `decode` method
|
||||
See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
|
||||
- Add all JSON error types.
|
||||
See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
|
||||
- Bugfix 'kid' not in given key list.
|
||||
See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
|
||||
- Miscellaneous cleanup, documentation and test fixes.
|
||||
See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
|
||||
[#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
|
||||
[#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
|
||||
[@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
|
||||
|
||||
#### 4.0.0 / 2016-07-17
|
||||
- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
|
||||
- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
|
||||
- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
|
||||
- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
|
||||
|
||||
#### 3.0.0 / 2015-07-22
|
||||
- Minimum PHP version updated from `5.2.0` to `5.3.0`.
|
||||
- Add `\Firebase\JWT` namespace. See
|
||||
[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
|
||||
[@Dashron](https://github.com/Dashron)!
|
||||
- Require a non-empty key to decode and verify a JWT. See
|
||||
[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
|
||||
[@sjones608](https://github.com/sjones608)!
|
||||
- Cleaner documentation blocks in the code. See
|
||||
[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
|
||||
[@johanderuijter](https://github.com/johanderuijter)!
|
||||
|
||||
#### 2.2.0 / 2015-06-22
|
||||
- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
|
||||
[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
|
||||
[@mcocaro](https://github.com/mcocaro)!
|
||||
|
||||
#### 2.1.0 / 2015-05-20
|
||||
- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
|
||||
between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
|
||||
- Add support for passing an object implementing the `ArrayAccess` interface for
|
||||
`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
|
||||
|
||||
#### 2.0.0 / 2015-04-01
|
||||
- **Note**: It is strongly recommended that you update to > v2.0.0 to address
|
||||
known security vulnerabilities in prior versions when both symmetric and
|
||||
asymmetric keys are used together.
|
||||
- Update signature for `JWT::decode(...)` to require an array of supported
|
||||
algorithms to use when verifying token signatures.
|
||||
|
||||
|
||||
Tests
|
||||
-----
|
||||
Run the tests using phpunit:
|
||||
|
||||
```bash
|
||||
$ pear install PHPUnit
|
||||
$ phpunit --configuration phpunit.xml.dist
|
||||
PHPUnit 3.7.10 by Sebastian Bergmann.
|
||||
.....
|
||||
Time: 0 seconds, Memory: 2.50Mb
|
||||
OK (5 tests, 5 assertions)
|
||||
```
|
||||
|
||||
New Lines in private keys
|
||||
-----
|
||||
|
||||
If your private key contains `\n` characters, be sure to wrap it in double quotes `""`
|
||||
and not single quotes `''` in order to properly interpret the escaped characters.
|
||||
|
||||
License
|
||||
-------
|
||||
[3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause).
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||
"homepage": "https://github.com/firebase/php-jwt",
|
||||
"keywords": [
|
||||
"php",
|
||||
"jwt"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Neuman Vong",
|
||||
"email": "neuman+pear@twilio.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Anant Narayanan",
|
||||
"email": "anant@php.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Firebase\\JWT\\": "src"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": ">=4.8 <=9"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class BeforeValidException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class ExpiredException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use DomainException;
|
||||
use InvalidArgumentException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* JSON Web Key implementation, based on this spec:
|
||||
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Authentication
|
||||
* @package Authentication_JWT
|
||||
* @author Bui Sy Nguyen <nguyenbs@gmail.com>
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||
* @link https://github.com/firebase/php-jwt
|
||||
*/
|
||||
class JWK
|
||||
{
|
||||
/**
|
||||
* Parse a set of JWK keys
|
||||
*
|
||||
* @param array $jwks The JSON Web Key Set as an associative array
|
||||
*
|
||||
* @return array An associative array that represents the set of keys
|
||||
*
|
||||
* @throws InvalidArgumentException Provided JWK Set is empty
|
||||
* @throws UnexpectedValueException Provided JWK Set was invalid
|
||||
* @throws DomainException OpenSSL failure
|
||||
*
|
||||
* @uses parseKey
|
||||
*/
|
||||
public static function parseKeySet(array $jwks)
|
||||
{
|
||||
$keys = array();
|
||||
|
||||
if (!isset($jwks['keys'])) {
|
||||
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||
}
|
||||
if (empty($jwks['keys'])) {
|
||||
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||
}
|
||||
|
||||
foreach ($jwks['keys'] as $k => $v) {
|
||||
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||
if ($key = self::parseKey($v)) {
|
||||
$keys[$kid] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === \count($keys)) {
|
||||
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a JWK key
|
||||
*
|
||||
* @param array $jwk An individual JWK
|
||||
*
|
||||
* @return resource|array An associative array that represents the key
|
||||
*
|
||||
* @throws InvalidArgumentException Provided JWK is empty
|
||||
* @throws UnexpectedValueException Provided JWK was invalid
|
||||
* @throws DomainException OpenSSL failure
|
||||
*
|
||||
* @uses createPemFromModulusAndExponent
|
||||
*/
|
||||
public static function parseKey(array $jwk)
|
||||
{
|
||||
if (empty($jwk)) {
|
||||
throw new InvalidArgumentException('JWK must not be empty');
|
||||
}
|
||||
if (!isset($jwk['kty'])) {
|
||||
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
|
||||
}
|
||||
|
||||
switch ($jwk['kty']) {
|
||||
case 'RSA':
|
||||
if (!empty($jwk['d'])) {
|
||||
throw new UnexpectedValueException('RSA private keys are not supported');
|
||||
}
|
||||
if (!isset($jwk['n']) || !isset($jwk['e'])) {
|
||||
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
|
||||
}
|
||||
|
||||
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
|
||||
$publicKey = \openssl_pkey_get_public($pem);
|
||||
if (false === $publicKey) {
|
||||
throw new DomainException(
|
||||
'OpenSSL error: ' . \openssl_error_string()
|
||||
);
|
||||
}
|
||||
return $publicKey;
|
||||
default:
|
||||
// Currently only RSA is supported
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a public key represented in PEM format from RSA modulus and exponent information
|
||||
*
|
||||
* @param string $n The RSA modulus encoded in Base64
|
||||
* @param string $e The RSA exponent encoded in Base64
|
||||
*
|
||||
* @return string The RSA public key represented in PEM format
|
||||
*
|
||||
* @uses encodeLength
|
||||
*/
|
||||
private static function createPemFromModulusAndExponent($n, $e)
|
||||
{
|
||||
$modulus = JWT::urlsafeB64Decode($n);
|
||||
$publicExponent = JWT::urlsafeB64Decode($e);
|
||||
|
||||
$components = array(
|
||||
'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
|
||||
'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
|
||||
);
|
||||
|
||||
$rsaPublicKey = \pack(
|
||||
'Ca*a*a*',
|
||||
48,
|
||||
self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
|
||||
$components['modulus'],
|
||||
$components['publicExponent']
|
||||
);
|
||||
|
||||
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
||||
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
||||
$rsaPublicKey = \chr(0) . $rsaPublicKey;
|
||||
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
|
||||
|
||||
$rsaPublicKey = \pack(
|
||||
'Ca*a*',
|
||||
48,
|
||||
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
|
||||
$rsaOID . $rsaPublicKey
|
||||
);
|
||||
|
||||
$rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
|
||||
\chunk_split(\base64_encode($rsaPublicKey), 64) .
|
||||
'-----END PUBLIC KEY-----';
|
||||
|
||||
return $rsaPublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* DER-encode the length
|
||||
*
|
||||
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
||||
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
private static function encodeLength($length)
|
||||
{
|
||||
if ($length <= 0x7F) {
|
||||
return \chr($length);
|
||||
}
|
||||
|
||||
$temp = \ltrim(\pack('N', $length), \chr(0));
|
||||
|
||||
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,611 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use ArrayAccess;
|
||||
use DomainException;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use OpenSSLAsymmetricKey;
|
||||
use UnexpectedValueException;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* JSON Web Token implementation, based on this spec:
|
||||
* https://tools.ietf.org/html/rfc7519
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Authentication
|
||||
* @package Authentication_JWT
|
||||
* @author Neuman Vong <neuman@twilio.com>
|
||||
* @author Anant Narayanan <anant@php.net>
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||
* @link https://github.com/firebase/php-jwt
|
||||
*/
|
||||
class JWT
|
||||
{
|
||||
const ASN1_INTEGER = 0x02;
|
||||
const ASN1_SEQUENCE = 0x10;
|
||||
const ASN1_BIT_STRING = 0x03;
|
||||
|
||||
/**
|
||||
* When checking nbf, iat or expiration times,
|
||||
* we want to provide some extra leeway time to
|
||||
* account for clock skew.
|
||||
*/
|
||||
public static $leeway = 0;
|
||||
|
||||
/**
|
||||
* Allow the current timestamp to be specified.
|
||||
* Useful for fixing a value within unit testing.
|
||||
*
|
||||
* Will default to PHP time() value if null.
|
||||
*/
|
||||
public static $timestamp = null;
|
||||
|
||||
public static $supported_algs = array(
|
||||
'ES384' => array('openssl', 'SHA384'),
|
||||
'ES256' => array('openssl', 'SHA256'),
|
||||
'HS256' => array('hash_hmac', 'SHA256'),
|
||||
'HS384' => array('hash_hmac', 'SHA384'),
|
||||
'HS512' => array('hash_hmac', 'SHA512'),
|
||||
'RS256' => array('openssl', 'SHA256'),
|
||||
'RS384' => array('openssl', 'SHA384'),
|
||||
'RS512' => array('openssl', 'SHA512'),
|
||||
'EdDSA' => array('sodium_crypto', 'EdDSA'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Decodes a JWT string into a PHP object.
|
||||
*
|
||||
* @param string $jwt The JWT
|
||||
* @param Key|array<Key>|mixed $keyOrKeyArray The Key or array of Key objects.
|
||||
* If the algorithm used is asymmetric, this is the public key
|
||||
* Each Key object contains an algorithm and matching key.
|
||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
* @param array $allowed_algs [DEPRECATED] List of supported verification algorithms. Only
|
||||
* should be used for backwards compatibility.
|
||||
*
|
||||
* @return object The JWT's payload as a PHP object
|
||||
*
|
||||
* @throws InvalidArgumentException Provided JWT was empty
|
||||
* @throws UnexpectedValueException Provided JWT was invalid
|
||||
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
|
||||
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
|
||||
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
|
||||
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
|
||||
*
|
||||
* @uses jsonDecode
|
||||
* @uses urlsafeB64Decode
|
||||
*/
|
||||
public static function decode($jwt, $keyOrKeyArray, array $allowed_algs = array())
|
||||
{
|
||||
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
|
||||
|
||||
if (empty($keyOrKeyArray)) {
|
||||
throw new InvalidArgumentException('Key may not be empty');
|
||||
}
|
||||
$tks = \explode('.', $jwt);
|
||||
if (\count($tks) != 3) {
|
||||
throw new UnexpectedValueException('Wrong number of segments');
|
||||
}
|
||||
list($headb64, $bodyb64, $cryptob64) = $tks;
|
||||
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
|
||||
throw new UnexpectedValueException('Invalid header encoding');
|
||||
}
|
||||
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
|
||||
throw new UnexpectedValueException('Invalid claims encoding');
|
||||
}
|
||||
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
|
||||
throw new UnexpectedValueException('Invalid signature encoding');
|
||||
}
|
||||
if (empty($header->alg)) {
|
||||
throw new UnexpectedValueException('Empty algorithm');
|
||||
}
|
||||
if (empty(static::$supported_algs[$header->alg])) {
|
||||
throw new UnexpectedValueException('Algorithm not supported');
|
||||
}
|
||||
|
||||
list($keyMaterial, $algorithm) = self::getKeyMaterialAndAlgorithm(
|
||||
$keyOrKeyArray,
|
||||
empty($header->kid) ? null : $header->kid
|
||||
);
|
||||
|
||||
if (empty($algorithm)) {
|
||||
// Use deprecated "allowed_algs" to determine if the algorithm is supported.
|
||||
// This opens up the possibility of an attack in some implementations.
|
||||
// @see https://github.com/firebase/php-jwt/issues/351
|
||||
if (!\in_array($header->alg, $allowed_algs)) {
|
||||
throw new UnexpectedValueException('Algorithm not allowed');
|
||||
}
|
||||
} else {
|
||||
// Check the algorithm
|
||||
if (!self::constantTimeEquals($algorithm, $header->alg)) {
|
||||
// See issue #351
|
||||
throw new UnexpectedValueException('Incorrect key for this algorithm');
|
||||
}
|
||||
}
|
||||
if ($header->alg === 'ES256' || $header->alg === 'ES384') {
|
||||
// OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
|
||||
$sig = self::signatureToDER($sig);
|
||||
}
|
||||
|
||||
if (!static::verify("$headb64.$bodyb64", $sig, $keyMaterial, $header->alg)) {
|
||||
throw new SignatureInvalidException('Signature verification failed');
|
||||
}
|
||||
|
||||
// Check the nbf if it is defined. This is the time that the
|
||||
// token can actually be used. If it's not yet that time, abort.
|
||||
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
|
||||
throw new BeforeValidException(
|
||||
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
|
||||
);
|
||||
}
|
||||
|
||||
// Check that this token has been created before 'now'. This prevents
|
||||
// using tokens that have been created for later use (and haven't
|
||||
// correctly used the nbf claim).
|
||||
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
|
||||
throw new BeforeValidException(
|
||||
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if this token has expired.
|
||||
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
|
||||
throw new ExpiredException('Expired token');
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts and signs a PHP object or array into a JWT string.
|
||||
*
|
||||
* @param object|array $payload PHP object or array
|
||||
* @param string|resource $key The secret key.
|
||||
* If the algorithm used is asymmetric, this is the private key
|
||||
* @param string $alg The signing algorithm.
|
||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
* @param mixed $keyId
|
||||
* @param array $head An array with header elements to attach
|
||||
*
|
||||
* @return string A signed JWT
|
||||
*
|
||||
* @uses jsonEncode
|
||||
* @uses urlsafeB64Encode
|
||||
*/
|
||||
public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
|
||||
{
|
||||
$header = array('typ' => 'JWT', 'alg' => $alg);
|
||||
if ($keyId !== null) {
|
||||
$header['kid'] = $keyId;
|
||||
}
|
||||
if (isset($head) && \is_array($head)) {
|
||||
$header = \array_merge($head, $header);
|
||||
}
|
||||
$segments = array();
|
||||
$segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
|
||||
$segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
|
||||
$signing_input = \implode('.', $segments);
|
||||
|
||||
$signature = static::sign($signing_input, $key, $alg);
|
||||
$segments[] = static::urlsafeB64Encode($signature);
|
||||
|
||||
return \implode('.', $segments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a string with a given key and algorithm.
|
||||
*
|
||||
* @param string $msg The message to sign
|
||||
* @param string|resource $key The secret key
|
||||
* @param string $alg The signing algorithm.
|
||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
*
|
||||
* @return string An encrypted message
|
||||
*
|
||||
* @throws DomainException Unsupported algorithm or bad key was specified
|
||||
*/
|
||||
public static function sign($msg, $key, $alg = 'HS256')
|
||||
{
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||
switch ($function) {
|
||||
case 'hash_hmac':
|
||||
return \hash_hmac($algorithm, $msg, $key, true);
|
||||
case 'openssl':
|
||||
$signature = '';
|
||||
$success = \openssl_sign($msg, $signature, $key, $algorithm);
|
||||
if (!$success) {
|
||||
throw new DomainException("OpenSSL unable to sign data");
|
||||
}
|
||||
if ($alg === 'ES256') {
|
||||
$signature = self::signatureFromDER($signature, 256);
|
||||
} elseif ($alg === 'ES384') {
|
||||
$signature = self::signatureFromDER($signature, 384);
|
||||
}
|
||||
return $signature;
|
||||
case 'sodium_crypto':
|
||||
if (!function_exists('sodium_crypto_sign_detached')) {
|
||||
throw new DomainException('libsodium is not available');
|
||||
}
|
||||
try {
|
||||
// The last non-empty line is used as the key.
|
||||
$lines = array_filter(explode("\n", $key));
|
||||
$key = base64_decode(end($lines));
|
||||
return sodium_crypto_sign_detached($msg, $key);
|
||||
} catch (Exception $e) {
|
||||
throw new DomainException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a signature with the message, key and method. Not all methods
|
||||
* are symmetric, so we must have a separate verify and sign method.
|
||||
*
|
||||
* @param string $msg The original message (header and body)
|
||||
* @param string $signature The original signature
|
||||
* @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
|
||||
* @param string $alg The algorithm
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
|
||||
*/
|
||||
private static function verify($msg, $signature, $key, $alg)
|
||||
{
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
|
||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||
switch ($function) {
|
||||
case 'openssl':
|
||||
$success = \openssl_verify($msg, $signature, $key, $algorithm);
|
||||
if ($success === 1) {
|
||||
return true;
|
||||
} elseif ($success === 0) {
|
||||
return false;
|
||||
}
|
||||
// returns 1 on success, 0 on failure, -1 on error.
|
||||
throw new DomainException(
|
||||
'OpenSSL error: ' . \openssl_error_string()
|
||||
);
|
||||
case 'sodium_crypto':
|
||||
if (!function_exists('sodium_crypto_sign_verify_detached')) {
|
||||
throw new DomainException('libsodium is not available');
|
||||
}
|
||||
try {
|
||||
// The last non-empty line is used as the key.
|
||||
$lines = array_filter(explode("\n", $key));
|
||||
$key = base64_decode(end($lines));
|
||||
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
|
||||
} catch (Exception $e) {
|
||||
throw new DomainException($e->getMessage(), 0, $e);
|
||||
}
|
||||
case 'hash_hmac':
|
||||
default:
|
||||
$hash = \hash_hmac($algorithm, $msg, $key, true);
|
||||
return self::constantTimeEquals($signature, $hash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a JSON string into a PHP object.
|
||||
*
|
||||
* @param string $input JSON string
|
||||
*
|
||||
* @return object Object representation of JSON string
|
||||
*
|
||||
* @throws DomainException Provided string was invalid JSON
|
||||
*/
|
||||
public static function jsonDecode($input)
|
||||
{
|
||||
if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
|
||||
/** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
|
||||
* to specify that large ints (like Steam Transaction IDs) should be treated as
|
||||
* strings, rather than the PHP default behaviour of converting them to floats.
|
||||
*/
|
||||
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
|
||||
} else {
|
||||
/** Not all servers will support that, however, so for older versions we must
|
||||
* manually detect large ints in the JSON string and quote them (thus converting
|
||||
*them to strings) before decoding, hence the preg_replace() call.
|
||||
*/
|
||||
$max_int_length = \strlen((string) PHP_INT_MAX) - 1;
|
||||
$json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
|
||||
$obj = \json_decode($json_without_bigints);
|
||||
}
|
||||
|
||||
if ($errno = \json_last_error()) {
|
||||
static::handleJsonError($errno);
|
||||
} elseif ($obj === null && $input !== 'null') {
|
||||
throw new DomainException('Null result with non-null input');
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a PHP object into a JSON string.
|
||||
*
|
||||
* @param object|array $input A PHP object or array
|
||||
*
|
||||
* @return string JSON representation of the PHP object or array
|
||||
*
|
||||
* @throws DomainException Provided object could not be encoded to valid JSON
|
||||
*/
|
||||
public static function jsonEncode($input)
|
||||
{
|
||||
$json = \json_encode($input);
|
||||
if ($errno = \json_last_error()) {
|
||||
static::handleJsonError($errno);
|
||||
} elseif ($json === 'null' && $input !== null) {
|
||||
throw new DomainException('Null result with non-null input');
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string with URL-safe Base64.
|
||||
*
|
||||
* @param string $input A Base64 encoded string
|
||||
*
|
||||
* @return string A decoded string
|
||||
*/
|
||||
public static function urlsafeB64Decode($input)
|
||||
{
|
||||
$remainder = \strlen($input) % 4;
|
||||
if ($remainder) {
|
||||
$padlen = 4 - $remainder;
|
||||
$input .= \str_repeat('=', $padlen);
|
||||
}
|
||||
return \base64_decode(\strtr($input, '-_', '+/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a string with URL-safe Base64.
|
||||
*
|
||||
* @param string $input The string you want encoded
|
||||
*
|
||||
* @return string The base64 encode of what you passed in
|
||||
*/
|
||||
public static function urlsafeB64Encode($input)
|
||||
{
|
||||
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if an algorithm has been provided for each Key
|
||||
*
|
||||
* @param Key|array<Key>|mixed $keyOrKeyArray
|
||||
* @param string|null $kid
|
||||
*
|
||||
* @throws UnexpectedValueException
|
||||
*
|
||||
* @return array containing the keyMaterial and algorithm
|
||||
*/
|
||||
private static function getKeyMaterialAndAlgorithm($keyOrKeyArray, $kid = null)
|
||||
{
|
||||
if (
|
||||
is_string($keyOrKeyArray)
|
||||
|| is_resource($keyOrKeyArray)
|
||||
|| $keyOrKeyArray instanceof OpenSSLAsymmetricKey
|
||||
) {
|
||||
return array($keyOrKeyArray, null);
|
||||
}
|
||||
|
||||
if ($keyOrKeyArray instanceof Key) {
|
||||
return array($keyOrKeyArray->getKeyMaterial(), $keyOrKeyArray->getAlgorithm());
|
||||
}
|
||||
|
||||
if (is_array($keyOrKeyArray) || $keyOrKeyArray instanceof ArrayAccess) {
|
||||
if (!isset($kid)) {
|
||||
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
|
||||
}
|
||||
if (!isset($keyOrKeyArray[$kid])) {
|
||||
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
|
||||
}
|
||||
|
||||
$key = $keyOrKeyArray[$kid];
|
||||
|
||||
if ($key instanceof Key) {
|
||||
return array($key->getKeyMaterial(), $key->getAlgorithm());
|
||||
}
|
||||
|
||||
return array($key, null);
|
||||
}
|
||||
|
||||
throw new UnexpectedValueException(
|
||||
'$keyOrKeyArray must be a string|resource key, an array of string|resource keys, '
|
||||
. 'an instance of Firebase\JWT\Key key or an array of Firebase\JWT\Key keys'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $left
|
||||
* @param string $right
|
||||
* @return bool
|
||||
*/
|
||||
public static function constantTimeEquals($left, $right)
|
||||
{
|
||||
if (\function_exists('hash_equals')) {
|
||||
return \hash_equals($left, $right);
|
||||
}
|
||||
$len = \min(static::safeStrlen($left), static::safeStrlen($right));
|
||||
|
||||
$status = 0;
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
|
||||
}
|
||||
$status |= (static::safeStrlen($left) ^ static::safeStrlen($right));
|
||||
|
||||
return ($status === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a JSON error.
|
||||
*
|
||||
* @param int $errno An error number from json_last_error()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function handleJsonError($errno)
|
||||
{
|
||||
$messages = array(
|
||||
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
||||
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
||||
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
||||
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
|
||||
);
|
||||
throw new DomainException(
|
||||
isset($messages[$errno])
|
||||
? $messages[$errno]
|
||||
: 'Unknown JSON error: ' . $errno
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes in cryptographic strings.
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private static function safeStrlen($str)
|
||||
{
|
||||
if (\function_exists('mb_strlen')) {
|
||||
return \mb_strlen($str, '8bit');
|
||||
}
|
||||
return \strlen($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an ECDSA signature to an ASN.1 DER sequence
|
||||
*
|
||||
* @param string $sig The ECDSA signature to convert
|
||||
* @return string The encoded DER object
|
||||
*/
|
||||
private static function signatureToDER($sig)
|
||||
{
|
||||
// Separate the signature into r-value and s-value
|
||||
list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
|
||||
|
||||
// Trim leading zeros
|
||||
$r = \ltrim($r, "\x00");
|
||||
$s = \ltrim($s, "\x00");
|
||||
|
||||
// Convert r-value and s-value from unsigned big-endian integers to
|
||||
// signed two's complement
|
||||
if (\ord($r[0]) > 0x7f) {
|
||||
$r = "\x00" . $r;
|
||||
}
|
||||
if (\ord($s[0]) > 0x7f) {
|
||||
$s = "\x00" . $s;
|
||||
}
|
||||
|
||||
return self::encodeDER(
|
||||
self::ASN1_SEQUENCE,
|
||||
self::encodeDER(self::ASN1_INTEGER, $r) .
|
||||
self::encodeDER(self::ASN1_INTEGER, $s)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a value into a DER object.
|
||||
*
|
||||
* @param int $type DER tag
|
||||
* @param string $value the value to encode
|
||||
* @return string the encoded object
|
||||
*/
|
||||
private static function encodeDER($type, $value)
|
||||
{
|
||||
$tag_header = 0;
|
||||
if ($type === self::ASN1_SEQUENCE) {
|
||||
$tag_header |= 0x20;
|
||||
}
|
||||
|
||||
// Type
|
||||
$der = \chr($tag_header | $type);
|
||||
|
||||
// Length
|
||||
$der .= \chr(\strlen($value));
|
||||
|
||||
return $der . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes signature from a DER object.
|
||||
*
|
||||
* @param string $der binary signature in DER format
|
||||
* @param int $keySize the number of bits in the key
|
||||
* @return string the signature
|
||||
*/
|
||||
private static function signatureFromDER($der, $keySize)
|
||||
{
|
||||
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
|
||||
list($offset, $_) = self::readDER($der);
|
||||
list($offset, $r) = self::readDER($der, $offset);
|
||||
list($offset, $s) = self::readDER($der, $offset);
|
||||
|
||||
// Convert r-value and s-value from signed two's compliment to unsigned
|
||||
// big-endian integers
|
||||
$r = \ltrim($r, "\x00");
|
||||
$s = \ltrim($s, "\x00");
|
||||
|
||||
// Pad out r and s so that they are $keySize bits long
|
||||
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||
|
||||
return $r . $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads binary DER-encoded data and decodes into a single object
|
||||
*
|
||||
* @param string $der the binary data in DER format
|
||||
* @param int $offset the offset of the data stream containing the object
|
||||
* to decode
|
||||
* @return array [$offset, $data] the new offset and the decoded object
|
||||
*/
|
||||
private static function readDER($der, $offset = 0)
|
||||
{
|
||||
$pos = $offset;
|
||||
$size = \strlen($der);
|
||||
$constructed = (\ord($der[$pos]) >> 5) & 0x01;
|
||||
$type = \ord($der[$pos++]) & 0x1f;
|
||||
|
||||
// Length
|
||||
$len = \ord($der[$pos++]);
|
||||
if ($len & 0x80) {
|
||||
$n = $len & 0x1f;
|
||||
$len = 0;
|
||||
while ($n-- && $pos < $size) {
|
||||
$len = ($len << 8) | \ord($der[$pos++]);
|
||||
}
|
||||
}
|
||||
|
||||
// Value
|
||||
if ($type == self::ASN1_BIT_STRING) {
|
||||
$pos++; // Skip the first contents octet (padding indicator)
|
||||
$data = \substr($der, $pos, $len - 1);
|
||||
$pos += $len - 1;
|
||||
} elseif (!$constructed) {
|
||||
$data = \substr($der, $pos, $len);
|
||||
$pos += $len;
|
||||
} else {
|
||||
$data = null;
|
||||
}
|
||||
|
||||
return array($pos, $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use OpenSSLAsymmetricKey;
|
||||
|
||||
class Key
|
||||
{
|
||||
/** @var string $algorithm */
|
||||
private $algorithm;
|
||||
|
||||
/** @var string|resource|OpenSSLAsymmetricKey $keyMaterial */
|
||||
private $keyMaterial;
|
||||
|
||||
/**
|
||||
* @param string|resource|OpenSSLAsymmetricKey $keyMaterial
|
||||
* @param string $algorithm
|
||||
*/
|
||||
public function __construct($keyMaterial, $algorithm)
|
||||
{
|
||||
if (
|
||||
!is_string($keyMaterial)
|
||||
&& !is_resource($keyMaterial)
|
||||
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
|
||||
) {
|
||||
throw new InvalidArgumentException('Type error: $keyMaterial must be a string, resource, or OpenSSLAsymmetricKey');
|
||||
}
|
||||
|
||||
if (empty($keyMaterial)) {
|
||||
throw new InvalidArgumentException('Type error: $keyMaterial must not be empty');
|
||||
}
|
||||
|
||||
if (!is_string($algorithm)|| empty($keyMaterial)) {
|
||||
throw new InvalidArgumentException('Type error: $algorithm must be a string');
|
||||
}
|
||||
|
||||
$this->keyMaterial = $keyMaterial;
|
||||
$this->algorithm = $algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the algorithm valid for this key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlgorithm()
|
||||
{
|
||||
return $this->algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|resource|OpenSSLAsymmetricKey
|
||||
*/
|
||||
public function getKeyMaterial()
|
||||
{
|
||||
return $this->keyMaterial;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class SignatureInvalidException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
// This file is automatically generated at:2021-12-31 01:32:01
|
||||
// This file is automatically generated at:2022-01-03 02:16:55
|
||||
declare (strict_types = 1);
|
||||
return array (
|
||||
0 => 'think\\captcha\\CaptchaService',
|
||||
|
|
|
|||
|
|
@ -26,6 +26,17 @@ class Env implements ArrayAccess
|
|||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* 数据转换映射
|
||||
* @var array
|
||||
*/
|
||||
protected $convert = [
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'off' => false,
|
||||
'on' => true,
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->data = $_ENV;
|
||||
|
|
@ -57,9 +68,14 @@ class Env implements ArrayAccess
|
|||
}
|
||||
|
||||
$name = strtoupper(str_replace('.', '_', $name));
|
||||
|
||||
if (isset($this->data[$name])) {
|
||||
return $this->data[$name];
|
||||
$result = $this->data[$name];
|
||||
|
||||
if (is_string($result) && isset($this->convert[$result])) {
|
||||
return $this->convert[$result];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->getEnv($name, $default);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
|
||||
jobs:
|
||||
phpcs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup PHP environment
|
||||
uses: shivammathur/setup-php@v2
|
||||
- name: Install dependencies
|
||||
run: composer install
|
||||
- name: PHPCSFixer check
|
||||
run: composer check-style
|
||||
phpunit:
|
||||
strategy:
|
||||
matrix:
|
||||
php_version: [7.3, 7.4, 8.0]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup PHP environment
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php_version }}
|
||||
coverage: xdebug
|
||||
- name: Install dependencies
|
||||
run: composer install
|
||||
- name: PHPUnit check
|
||||
run: ./vendor/bin/phpunit --coverage-text
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
name: PHP Composer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 3.0 ]
|
||||
pull_request:
|
||||
branches: [ 3.0 ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate --strict
|
||||
|
||||
- name: Cache Composer packages
|
||||
id: composer-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: vendor
|
||||
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-php-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress
|
||||
|
||||
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
|
||||
# Docs: https://getcomposer.org/doc/articles/scripts.md
|
||||
|
||||
- name: Run test suite
|
||||
run: composer test
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
/vendor/
|
||||
/.idea/
|
||||
composer.lock
|
||||
composer.lock
|
||||
.phpunit.result.cache
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
基于PHP7.1+
|
||||
|
||||
[](https://github.com/larvatecn/think-helper/actions/workflows/php.yml)
|
||||
|
||||
> 以下类库都在`\\think\\helper`命名空间下
|
||||
|
||||
## Str
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
],
|
||||
"require": {
|
||||
"php": ">=7.1.0"
|
||||
},
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\": "src"
|
||||
|
|
@ -18,5 +21,16 @@
|
|||
"files": [
|
||||
"src/helper.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./vendor/bin/phpunit --colors"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"test": "Run all tests."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Application Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
|
|
@ -20,6 +20,7 @@ use JsonSerializable;
|
|||
use think\contract\Arrayable;
|
||||
use think\contract\Jsonable;
|
||||
use think\helper\Arr;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* 数据集管理类
|
||||
|
|
@ -142,7 +143,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
|||
public function intersect($items, string $indexKey = null)
|
||||
{
|
||||
if ($this->isEmpty() || is_scalar($this->items[0])) {
|
||||
return new static(array_diff($this->items, $this->convertToArray($items)));
|
||||
return new static(array_intersect($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
$intersect = [];
|
||||
|
|
@ -579,16 +580,19 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
|||
}
|
||||
|
||||
// ArrayAccess
|
||||
public function offsetExists($offset)
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset) : bool
|
||||
{
|
||||
return array_key_exists($offset, $this->items);
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items[$offset];
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
|
|
@ -598,24 +602,27 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
|||
}
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
//Countable
|
||||
public function count()
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
//IteratorAggregate
|
||||
public function getIterator()
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->items);
|
||||
}
|
||||
|
||||
//JsonSerializable
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
|
|
@ -627,7 +634,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
|||
* @param integer $options json参数
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(int $options = JSON_UNESCAPED_UNICODE) : string
|
||||
public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
|
||||
{
|
||||
return json_encode($this->toArray(), $options);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ class Str
|
|||
}
|
||||
|
||||
if (!ctype_lower($value)) {
|
||||
$value = preg_replace('/\s+/u', '', $value);
|
||||
$value = preg_replace('/\s+/u', '', ucwords($value));
|
||||
|
||||
$value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,342 @@
|
|||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use stdClass;
|
||||
use think\Collection;
|
||||
use think\helper\Arr;
|
||||
|
||||
class ArrTest extends TestCase
|
||||
{
|
||||
public function testAdd()
|
||||
{
|
||||
$array = Arr::add(['name' => 'ThinkPHP'], 'price', 100);
|
||||
$this->assertSame(['name' => 'ThinkPHP', 'price' => 100], $array);
|
||||
}
|
||||
|
||||
public function testCrossJoin()
|
||||
{
|
||||
// Single dimension
|
||||
$this->assertSame(
|
||||
[[1, 'a'], [1, 'b'], [1, 'c']],
|
||||
Arr::crossJoin([1], ['a', 'b', 'c'])
|
||||
);
|
||||
// Square matrix
|
||||
$this->assertSame(
|
||||
[[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']],
|
||||
Arr::crossJoin([1, 2], ['a', 'b'])
|
||||
);
|
||||
// Rectangular matrix
|
||||
$this->assertSame(
|
||||
[[1, 'a'], [1, 'b'], [1, 'c'], [2, 'a'], [2, 'b'], [2, 'c']],
|
||||
Arr::crossJoin([1, 2], ['a', 'b', 'c'])
|
||||
);
|
||||
// 3D matrix
|
||||
$this->assertSame(
|
||||
[
|
||||
[1, 'a', 'I'], [1, 'a', 'II'], [1, 'a', 'III'],
|
||||
[1, 'b', 'I'], [1, 'b', 'II'], [1, 'b', 'III'],
|
||||
[2, 'a', 'I'], [2, 'a', 'II'], [2, 'a', 'III'],
|
||||
[2, 'b', 'I'], [2, 'b', 'II'], [2, 'b', 'III'],
|
||||
],
|
||||
Arr::crossJoin([1, 2], ['a', 'b'], ['I', 'II', 'III'])
|
||||
);
|
||||
// With 1 empty dimension
|
||||
$this->assertSame([], Arr::crossJoin([], ['a', 'b'], ['I', 'II', 'III']));
|
||||
$this->assertSame([], Arr::crossJoin([1, 2], [], ['I', 'II', 'III']));
|
||||
$this->assertSame([], Arr::crossJoin([1, 2], ['a', 'b'], []));
|
||||
// With empty arrays
|
||||
$this->assertSame([], Arr::crossJoin([], [], []));
|
||||
$this->assertSame([], Arr::crossJoin([], []));
|
||||
$this->assertSame([], Arr::crossJoin([]));
|
||||
// Not really a proper usage, still, test for preserving BC
|
||||
$this->assertSame([[]], Arr::crossJoin());
|
||||
}
|
||||
|
||||
public function testDivide()
|
||||
{
|
||||
list($keys, $values) = Arr::divide(['name' => 'ThinkPHP']);
|
||||
$this->assertSame(['name'], $keys);
|
||||
$this->assertSame(['ThinkPHP'], $values);
|
||||
}
|
||||
|
||||
public function testDot()
|
||||
{
|
||||
$array = Arr::dot(['foo' => ['bar' => 'baz']]);
|
||||
$this->assertSame(['foo.bar' => 'baz'], $array);
|
||||
$array = Arr::dot([]);
|
||||
$this->assertSame([], $array);
|
||||
$array = Arr::dot(['foo' => []]);
|
||||
$this->assertSame(['foo' => []], $array);
|
||||
$array = Arr::dot(['foo' => ['bar' => []]]);
|
||||
$this->assertSame(['foo.bar' => []], $array);
|
||||
}
|
||||
|
||||
public function testExcept()
|
||||
{
|
||||
$array = ['name' => 'ThinkPHP', 'price' => 100];
|
||||
$array = Arr::except($array, ['price']);
|
||||
$this->assertSame(['name' => 'ThinkPHP'], $array);
|
||||
}
|
||||
|
||||
public function testExists()
|
||||
{
|
||||
$this->assertTrue(Arr::exists([1], 0));
|
||||
$this->assertTrue(Arr::exists([null], 0));
|
||||
$this->assertTrue(Arr::exists(['a' => 1], 'a'));
|
||||
$this->assertTrue(Arr::exists(['a' => null], 'a'));
|
||||
$this->assertFalse(Arr::exists([1], 1));
|
||||
$this->assertFalse(Arr::exists([null], 1));
|
||||
$this->assertFalse(Arr::exists(['a' => 1], 0));
|
||||
}
|
||||
|
||||
public function testFirst()
|
||||
{
|
||||
$array = [100, 200, 300];
|
||||
$value = Arr::first($array, function ($value) {
|
||||
return $value >= 150;
|
||||
});
|
||||
$this->assertSame(200, $value);
|
||||
$this->assertSame(100, Arr::first($array));
|
||||
|
||||
$this->assertSame('default', Arr::first([], null, 'default'));
|
||||
|
||||
$this->assertSame('default', Arr::first([], function () {
|
||||
return false;
|
||||
}, 'default'));
|
||||
}
|
||||
|
||||
public function testLast()
|
||||
{
|
||||
$array = [100, 200, 300];
|
||||
$last = Arr::last($array, function ($value) {
|
||||
return $value < 250;
|
||||
});
|
||||
$this->assertSame(200, $last);
|
||||
$last = Arr::last($array, function ($value, $key) {
|
||||
return $key < 2;
|
||||
});
|
||||
$this->assertSame(200, $last);
|
||||
$this->assertSame(300, Arr::last($array));
|
||||
}
|
||||
|
||||
public function testFlatten()
|
||||
{
|
||||
// Flat arrays are unaffected
|
||||
$array = ['#foo', '#bar', '#baz'];
|
||||
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten(['#foo', '#bar', '#baz']));
|
||||
// Nested arrays are flattened with existing flat items
|
||||
$array = [['#foo', '#bar'], '#baz'];
|
||||
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
|
||||
// Flattened array includes "null" items
|
||||
$array = [['#foo', null], '#baz', null];
|
||||
$this->assertSame(['#foo', null, '#baz', null], Arr::flatten($array));
|
||||
// Sets of nested arrays are flattened
|
||||
$array = [['#foo', '#bar'], ['#baz']];
|
||||
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
|
||||
// Deeply nested arrays are flattened
|
||||
$array = [['#foo', ['#bar']], ['#baz']];
|
||||
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
|
||||
// Nested arrays are flattened alongside arrays
|
||||
$array = [new Collection(['#foo', '#bar']), ['#baz']];
|
||||
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
|
||||
// Nested arrays containing plain arrays are flattened
|
||||
$array = [new Collection(['#foo', ['#bar']]), ['#baz']];
|
||||
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
|
||||
// Nested arrays containing arrays are flattened
|
||||
$array = [['#foo', new Collection(['#bar'])], ['#baz']];
|
||||
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
|
||||
// Nested arrays containing arrays containing arrays are flattened
|
||||
$array = [['#foo', new Collection(['#bar', ['#zap']])], ['#baz']];
|
||||
$this->assertSame(['#foo', '#bar', '#zap', '#baz'], Arr::flatten($array));
|
||||
}
|
||||
|
||||
public function testFlattenWithDepth()
|
||||
{
|
||||
// No depth flattens recursively
|
||||
$array = [['#foo', ['#bar', ['#baz']]], '#zap'];
|
||||
$this->assertSame(['#foo', '#bar', '#baz', '#zap'], Arr::flatten($array));
|
||||
// Specifying a depth only flattens to that depth
|
||||
$array = [['#foo', ['#bar', ['#baz']]], '#zap'];
|
||||
$this->assertSame(['#foo', ['#bar', ['#baz']], '#zap'], Arr::flatten($array, 1));
|
||||
$array = [['#foo', ['#bar', ['#baz']]], '#zap'];
|
||||
$this->assertSame(['#foo', '#bar', ['#baz'], '#zap'], Arr::flatten($array, 2));
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$array = ['products.item' => ['price' => 100]];
|
||||
$this->assertSame(['price' => 100], Arr::get($array, 'products.item'));
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
$value = Arr::get($array, 'products.item');
|
||||
$this->assertSame(['price' => 100], $value);
|
||||
// Test null array values
|
||||
$array = ['foo' => null, 'bar' => ['baz' => null]];
|
||||
$this->assertNull(Arr::get($array, 'foo', 'default'));
|
||||
$this->assertNull(Arr::get($array, 'bar.baz', 'default'));
|
||||
// Test null key returns the whole array
|
||||
$array = ['foo', 'bar'];
|
||||
$this->assertSame($array, Arr::get($array, null));
|
||||
// Test $array is empty and key is null
|
||||
$this->assertSame([], Arr::get([], null));
|
||||
$this->assertSame([], Arr::get([], null, 'default'));
|
||||
}
|
||||
|
||||
public function testHas()
|
||||
{
|
||||
$array = ['products.item' => ['price' => 100]];
|
||||
$this->assertTrue(Arr::has($array, 'products.item'));
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
$this->assertTrue(Arr::has($array, 'products.item'));
|
||||
$this->assertTrue(Arr::has($array, 'products.item.price'));
|
||||
$this->assertFalse(Arr::has($array, 'products.foo'));
|
||||
$this->assertFalse(Arr::has($array, 'products.item.foo'));
|
||||
$array = ['foo' => null, 'bar' => ['baz' => null]];
|
||||
$this->assertTrue(Arr::has($array, 'foo'));
|
||||
$this->assertTrue(Arr::has($array, 'bar.baz'));
|
||||
$array = ['foo', 'bar'];
|
||||
$this->assertFalse(Arr::has($array, null));
|
||||
$this->assertFalse(Arr::has([], null));
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
$this->assertTrue(Arr::has($array, ['products.item']));
|
||||
$this->assertTrue(Arr::has($array, ['products.item', 'products.item.price']));
|
||||
$this->assertTrue(Arr::has($array, ['products', 'products']));
|
||||
$this->assertFalse(Arr::has($array, ['foo']));
|
||||
$this->assertFalse(Arr::has($array, []));
|
||||
$this->assertFalse(Arr::has($array, ['products.item', 'products.price']));
|
||||
$this->assertFalse(Arr::has([], [null]));
|
||||
}
|
||||
|
||||
public function testIsAssoc()
|
||||
{
|
||||
$this->assertTrue(Arr::isAssoc(['a' => 'a', 0 => 'b']));
|
||||
$this->assertTrue(Arr::isAssoc([1 => 'a', 0 => 'b']));
|
||||
$this->assertTrue(Arr::isAssoc([1 => 'a', 2 => 'b']));
|
||||
$this->assertFalse(Arr::isAssoc([0 => 'a', 1 => 'b']));
|
||||
$this->assertFalse(Arr::isAssoc(['a', 'b']));
|
||||
}
|
||||
|
||||
public function testOnly()
|
||||
{
|
||||
$array = ['name' => 'ThinkPHP', 'price' => 100, 'orders' => 10];
|
||||
$array = Arr::only($array, ['name', 'price']);
|
||||
$this->assertSame(['name' => 'ThinkPHP', 'price' => 100], $array);
|
||||
}
|
||||
|
||||
public function testPrepend()
|
||||
{
|
||||
$array = Arr::prepend(['one', 'two', 'three', 'four'], 'zero');
|
||||
$this->assertSame(['zero', 'one', 'two', 'three', 'four'], $array);
|
||||
$array = Arr::prepend(['one' => 1, 'two' => 2], 0, 'zero');
|
||||
$this->assertSame(['zero' => 0, 'one' => 1, 'two' => 2], $array);
|
||||
}
|
||||
|
||||
public function testPull()
|
||||
{
|
||||
$array = ['name' => 'ThinkPHP', 'price' => 100];
|
||||
$name = Arr::pull($array, 'name');
|
||||
$this->assertSame('ThinkPHP', $name);
|
||||
$this->assertSame(['price' => 100], $array);
|
||||
// Only works on first level keys
|
||||
$array = ['i@example.com' => 'Joe', 'jack@localhost' => 'Jane'];
|
||||
$name = Arr::pull($array, 'i@example.com');
|
||||
$this->assertSame('Joe', $name);
|
||||
$this->assertSame(['jack@localhost' => 'Jane'], $array);
|
||||
// Does not work for nested keys
|
||||
$array = ['emails' => ['i@example.com' => 'Joe', 'jack@localhost' => 'Jane']];
|
||||
$name = Arr::pull($array, 'emails.i@example.com');
|
||||
$this->assertNull($name);
|
||||
$this->assertSame(['emails' => ['i@example.com' => 'Joe', 'jack@localhost' => 'Jane']], $array);
|
||||
}
|
||||
|
||||
public function testRandom()
|
||||
{
|
||||
$randomValue = Arr::random(['foo', 'bar', 'baz']);
|
||||
$this->assertContains($randomValue, ['foo', 'bar', 'baz']);
|
||||
$randomValues = Arr::random(['foo', 'bar', 'baz'], 1);
|
||||
$this->assertIsArray($randomValues);
|
||||
$this->assertCount(1, $randomValues);
|
||||
$this->assertContains($randomValues[0], ['foo', 'bar', 'baz']);
|
||||
$randomValues = Arr::random(['foo', 'bar', 'baz'], 2);
|
||||
$this->assertIsArray($randomValues);
|
||||
$this->assertCount(2, $randomValues);
|
||||
$this->assertContains($randomValues[0], ['foo', 'bar', 'baz']);
|
||||
$this->assertContains($randomValues[1], ['foo', 'bar', 'baz']);
|
||||
}
|
||||
|
||||
public function testSet()
|
||||
{
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
Arr::set($array, 'products.item.price', 200);
|
||||
Arr::set($array, 'goods.item.price', 200);
|
||||
$this->assertSame(['products' => ['item' => ['price' => 200]], 'goods' => ['item' => ['price' => 200]]], $array);
|
||||
}
|
||||
|
||||
public function testWhere()
|
||||
{
|
||||
$array = [100, '200', 300, '400', 500];
|
||||
$array = Arr::where($array, function ($value, $key) {
|
||||
return is_string($value);
|
||||
});
|
||||
$this->assertSame([1 => '200', 3 => '400'], $array);
|
||||
}
|
||||
|
||||
public function testWhereKey()
|
||||
{
|
||||
$array = ['10' => 1, 'foo' => 3, 20 => 2];
|
||||
$array = Arr::where($array, function ($value, $key) {
|
||||
return is_numeric($key);
|
||||
});
|
||||
$this->assertSame(['10' => 1, 20 => 2], $array);
|
||||
}
|
||||
|
||||
public function testForget()
|
||||
{
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
Arr::forget($array, null);
|
||||
$this->assertSame(['products' => ['item' => ['price' => 100]]], $array);
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
Arr::forget($array, []);
|
||||
$this->assertSame(['products' => ['item' => ['price' => 100]]], $array);
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
Arr::forget($array, 'products.item');
|
||||
$this->assertSame(['products' => []], $array);
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
Arr::forget($array, 'products.item.price');
|
||||
$this->assertSame(['products' => ['item' => []]], $array);
|
||||
$array = ['products' => ['item' => ['price' => 100]]];
|
||||
Arr::forget($array, 'products.final.price');
|
||||
$this->assertSame(['products' => ['item' => ['price' => 100]]], $array);
|
||||
$array = ['shop' => ['cart' => [150 => 0]]];
|
||||
Arr::forget($array, 'shop.final.cart');
|
||||
$this->assertSame(['shop' => ['cart' => [150 => 0]]], $array);
|
||||
$array = ['products' => ['item' => ['price' => ['original' => 50, 'taxes' => 60]]]];
|
||||
Arr::forget($array, 'products.item.price.taxes');
|
||||
$this->assertSame(['products' => ['item' => ['price' => ['original' => 50]]]], $array);
|
||||
$array = ['products' => ['item' => ['price' => ['original' => 50, 'taxes' => 60]]]];
|
||||
Arr::forget($array, 'products.item.final.taxes');
|
||||
$this->assertSame(['products' => ['item' => ['price' => ['original' => 50, 'taxes' => 60]]]], $array);
|
||||
$array = ['products' => ['item' => ['price' => 50], null => 'something']];
|
||||
Arr::forget($array, ['products.amount.all', 'products.item.price']);
|
||||
$this->assertSame(['products' => ['item' => [], null => 'something']], $array);
|
||||
// Only works on first level keys
|
||||
$array = ['i@example.com' => 'Joe', 'i@thinkphp.com' => 'Jane'];
|
||||
Arr::forget($array, 'i@example.com');
|
||||
$this->assertSame(['i@thinkphp.com' => 'Jane'], $array);
|
||||
// Does not work for nested keys
|
||||
$array = ['emails' => ['i@example.com' => ['name' => 'Joe'], 'jack@localhost' => ['name' => 'Jane']]];
|
||||
Arr::forget($array, ['emails.i@example.com', 'emails.jack@localhost']);
|
||||
$this->assertSame(['emails' => ['i@example.com' => ['name' => 'Joe']]], $array);
|
||||
}
|
||||
|
||||
public function testWrap()
|
||||
{
|
||||
$string = 'a';
|
||||
$array = ['a'];
|
||||
$object = new stdClass();
|
||||
$object->value = 'a';
|
||||
$this->assertSame(['a'], Arr::wrap($string));
|
||||
$this->assertSame($array, Arr::wrap($array));
|
||||
$this->assertSame([$object], Arr::wrap($object));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use think\Collection;
|
||||
|
||||
class CollectionTest extends TestCase
|
||||
{
|
||||
public function testMerge()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello']);
|
||||
$this->assertSame(['name' => 'Hello', 'id' => 1], $c->merge(['id' => 1])->all());
|
||||
}
|
||||
|
||||
public function testFirst()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello', 'age' => 25]);
|
||||
|
||||
$this->assertSame('Hello', $c->first());
|
||||
}
|
||||
|
||||
public function testLast()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello', 'age' => 25]);
|
||||
|
||||
$this->assertSame(25, $c->last());
|
||||
}
|
||||
|
||||
public function testToArray()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello', 'age' => 25]);
|
||||
|
||||
$this->assertSame(['name' => 'Hello', 'age' => 25], $c->toArray());
|
||||
}
|
||||
|
||||
public function testToJson()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello', 'age' => 25]);
|
||||
|
||||
$this->assertSame(json_encode(['name' => 'Hello', 'age' => 25]), $c->toJson());
|
||||
$this->assertSame(json_encode(['name' => 'Hello', 'age' => 25]), (string) $c);
|
||||
$this->assertSame(json_encode(['name' => 'Hello', 'age' => 25]), json_encode($c));
|
||||
}
|
||||
|
||||
public function testSerialize()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello', 'age' => 25]);
|
||||
|
||||
$sc = serialize($c);
|
||||
$c = unserialize($sc);
|
||||
|
||||
$this->assertSame(['name' => 'Hello', 'age' => 25], $c->all());
|
||||
}
|
||||
|
||||
public function testGetIterator()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello', 'age' => 25]);
|
||||
|
||||
$this->assertInstanceOf(\ArrayIterator::class, $c->getIterator());
|
||||
|
||||
$this->assertSame(['name' => 'Hello', 'age' => 25], $c->getIterator()->getArrayCopy());
|
||||
}
|
||||
|
||||
public function testCount()
|
||||
{
|
||||
$c = new Collection(['name' => 'Hello', 'age' => 25]);
|
||||
|
||||
$this->assertCount(2, $c);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
namespace Tests;
|
||||
|
||||
use think\helper\Str;
|
||||
|
||||
class StrTest extends TestCase
|
||||
{
|
||||
public function testCamel()
|
||||
{
|
||||
$this->assertSame('fooBar', Str::camel('FooBar'));
|
||||
$this->assertSame('fooBar', Str::camel('FooBar'));
|
||||
$this->assertSame('fooBar', Str::camel('foo_bar'));
|
||||
$this->assertSame('fooBar', Str::camel('_foo_bar'));
|
||||
$this->assertSame('fooBar', Str::camel('_foo_bar_'));
|
||||
}
|
||||
|
||||
public function testStudly()
|
||||
{
|
||||
$this->assertSame('FooBar', Str::studly('fooBar'));
|
||||
$this->assertSame('FooBar', Str::studly('_foo_bar'));
|
||||
$this->assertSame('FooBar', Str::studly('_foo_bar_'));
|
||||
$this->assertSame('FooBar', Str::studly('_foo_bar_'));
|
||||
}
|
||||
|
||||
public function testSnake()
|
||||
{
|
||||
$this->assertSame('think_p_h_p_framework', Str::snake('ThinkPHPFramework'));
|
||||
$this->assertSame('think_php_framework', Str::snake('ThinkPhpFramework'));
|
||||
$this->assertSame('think php framework', Str::snake('ThinkPhpFramework', ' '));
|
||||
$this->assertSame('think_php_framework', Str::snake('Think Php Framework'));
|
||||
$this->assertSame('think_php_framework', Str::snake('Think Php Framework '));
|
||||
// ensure cache keys don't overlap
|
||||
$this->assertSame('think__php__framework', Str::snake('ThinkPhpFramework', '__'));
|
||||
$this->assertSame('think_php_framework_', Str::snake('ThinkPhpFramework_', '_'));
|
||||
$this->assertSame('think_php_framework', Str::snake('think php Framework'));
|
||||
$this->assertSame('think_php_frame_work', Str::snake('think php FrameWork'));
|
||||
// prevent breaking changes
|
||||
$this->assertSame('foo-bar', Str::snake('foo-bar'));
|
||||
$this->assertSame('foo-_bar', Str::snake('Foo-Bar'));
|
||||
$this->assertSame('foo__bar', Str::snake('Foo_Bar'));
|
||||
$this->assertSame('żółtałódka', Str::snake('ŻółtaŁódka'));
|
||||
}
|
||||
|
||||
public function testTitle()
|
||||
{
|
||||
$this->assertSame('Welcome Back', Str::title('welcome back'));
|
||||
}
|
||||
|
||||
public function testRandom()
|
||||
{
|
||||
$this->assertIsString(Str::random(10));
|
||||
}
|
||||
|
||||
public function testUpper()
|
||||
{
|
||||
$this->assertSame('USERNAME', Str::upper('username'));
|
||||
$this->assertSame('USERNAME', Str::upper('userNaMe'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||
|
||||
/**
|
||||
* Class TestCase
|
||||
* @author Tongle Xu <xutongle@gmail.com>
|
||||
*/
|
||||
class TestCase extends BaseTestCase
|
||||
{
|
||||
}
|
||||
|
|
@ -250,12 +250,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
|
|||
}
|
||||
|
||||
// 预载入查询
|
||||
if (!empty($options['with'])) {
|
||||
if (empty($options['is_resultSet']) && !empty($options['with'])) {
|
||||
$result->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false);
|
||||
}
|
||||
|
||||
// JOIN预载入查询
|
||||
if (!empty($options['with_join'])) {
|
||||
if (empty($options['is_resultSet']) && !empty($options['with_join'])) {
|
||||
$result->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -442,6 +442,7 @@ trait ModelRelationQuery
|
|||
protected function resultToModel(array &$result, array $options = [], bool $resultSet = false): void
|
||||
{
|
||||
$options['with_relation_attr'] = $this->getWithRelationAttr();
|
||||
$options['is_resultSet'] = $resultSet;
|
||||
|
||||
// JSON 数据处理
|
||||
if (!empty($options['json'])) {
|
||||
|
|
|
|||
|
|
@ -361,9 +361,7 @@ trait WhereQuery
|
|||
$field = $this->options['via'] . '.' . $field;
|
||||
}
|
||||
|
||||
if ($field instanceof Raw) {
|
||||
return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
|
||||
} elseif ($strict) {
|
||||
if ($strict) {
|
||||
// 使用严格模式查询
|
||||
if ('=' == $op) {
|
||||
$where = $this->whereEq($field, $condition);
|
||||
|
|
|
|||
Loading…
Reference in New Issue