更新文件

This commit is contained in:
liyukun 2022-01-03 02:22:24 +08:00
parent ae98df7961
commit c72547f73e
69 changed files with 974 additions and 1806 deletions

View File

@ -1,14 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
return [
// 模板后缀
'view_suffix' => 'html',
// 视图输出字符串内容替换
'tpl_replace_string' => [
'__PUBLIC_PATH__' => '/', //public 目录
'__ADMIN_PATH__' => '/static/admin/', //全局静态目录
]
];

View File

@ -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("操作失败");
}
}

View File

@ -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("操作失败");
}
}

View File

@ -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();
}

View File

@ -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");
}
/**

View File

@ -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)
{

View File

@ -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("操作失败");
}

View File

@ -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');

View File

@ -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');

View File

@ -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');

View File

@ -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');

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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');

View File

@ -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 () {

View File

@ -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}';

View File

@ -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>

View File

@ -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'),

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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>
/**
* 选择文件

View File

@ -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({

View File

@ -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>
/**
* 选择文件

View File

@ -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

View File

@ -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

View File

@ -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", " ", "&nbsp;"], '', htmlspecialchars_decode($str))));
public static function HtmlToMbStr($str){
return trim(strip_tags(str_replace(["\n","\t","\r"," ","&nbsp;"],'',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]);
}
/**

View File

@ -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/"

View File

@ -27,7 +27,7 @@ return [
// 默认的路由变量规则
'default_route_pattern' => '[\w\.]+',
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
'request_cache_key' => false,
'request_cache' => false,
// 请求缓存有效期
'request_cache_expire' => null,
// 全局请求缓存排除规则

View File

@ -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;
}

2
vendor/autoload.php vendored
View File

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit179ad4390eaa61356c3a52b9b610e467::getLoader();
return ComposerAutoloaderInit4b57298e8d0e895486f3307a354a7e1a::getLoader();

View File

@ -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',
);

View File

@ -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'),
);

View File

@ -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;

View 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);
}

View File

@ -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"
},

View File

@ -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(

View File

@ -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.

View File

@ -1,289 +0,0 @@
[![Build Status](https://travis-ci.org/firebase/php-jwt.png?branch=master)](https://travis-ci.org/firebase/php-jwt)
[![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt)
[![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt)
[![License](https://poser.pugx.org/firebase/php-jwt/license)](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).

View File

@ -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"
}
}

View File

@ -1,7 +0,0 @@
<?php
namespace Firebase\JWT;
class BeforeValidException extends \UnexpectedValueException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace Firebase\JWT;
class ExpiredException extends \UnexpectedValueException
{
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -1,7 +0,0 @@
<?php
namespace Firebase\JWT;
class SignatureInvalidException extends \UnexpectedValueException
{
}

2
vendor/services.php vendored
View File

@ -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',

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,4 @@
/vendor/
/.idea/
composer.lock
composer.lock
.phpunit.result.cache

View File

@ -2,6 +2,8 @@
基于PHP7.1+
[![PHP Composer](https://github.com/larvatecn/think-helper/actions/workflows/php.yml/badge.svg)](https://github.com/larvatecn/think-helper/actions/workflows/php.yml)
> 以下类库都在`\\think\\helper`命名空间下
## Str

View File

@ -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."
}
}

View File

@ -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>

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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'));
}
}

View File

@ -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
{
}

View File

@ -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);
}

View File

@ -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'])) {

View File

@ -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);