This commit is contained in:
2025-11-21 01:42:54 +08:00
parent ff026c6f32
commit f89196c73c
1953 changed files with 9 additions and 15246 deletions
+230
View File
@@ -0,0 +1,230 @@
<?php
namespace support;
use DateTime;
use DateTimeZone;
/**
* 日期时间处理类
*/
class Date
{
const YEAR = 31536000;
const MONTH = 2592000;
const WEEK = 604800;
const DAY = 86400;
const HOUR = 3600;
const MINUTE = 60;
/**
* 计算两个时区间相差的时长,单位为秒
*
* $seconds = self::offset('America/Chicago', 'GMT');
*
* [!!] A list of time zones that PHP supports can be found at
* <http://php.net/timezones>.
*
* @param string $remote timezone that to find the offset of
* @param string $local timezone used as the baseline
* @param mixed $now UNIX timestamp or date string
* @return integer
*/
public static function offset($remote, $local = null, $now = null)
{
if ($local === null) {
// Use the default timezone
$local = date_default_timezone_get();
}
if (is_int($now)) {
// Convert the timestamp into a string
$now = date(DateTime::RFC2822, $now);
}
// Create timezone objects
$zone_remote = new DateTimeZone($remote);
$zone_local = new DateTimeZone($local);
// Create date objects from timezones
$time_remote = new DateTime($now, $zone_remote);
$time_local = new DateTime($now, $zone_local);
// Find the offset
$offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
return $offset;
}
/**
* 计算两个时间戳之间相差的时间
*
* $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
* $span = self::span(60, 182, 'minutes'); // 2
*
* @param int $remote timestamp to find the span of
* @param int $local timestamp to use as the baseline
* @param string $output formatting string
* @return string when only a single output is requested
* @return array associative list of all outputs requested
* @from https://github.com/kohana/ohanzee-helpers/blob/master/src/Date.php
*/
public static function span($remote, $local = null, $output = 'years,months,weeks,days,hours,minutes,seconds')
{
// Normalize output
$output = trim(strtolower((string)$output));
if (!$output) {
// Invalid output
return false;
}
// Array with the output formats
$output = preg_split('/[^a-z]+/', $output);
// Convert the list of outputs to an associative array
$output = array_combine($output, array_fill(0, count($output), 0));
// Make the output values into keys
extract(array_flip($output), EXTR_SKIP);
if ($local === null) {
// Calculate the span from the current time
$local = time();
}
// Calculate timespan (seconds)
$timespan = abs($remote - $local);
if (isset($output['years'])) {
$timespan -= self::YEAR * ($output['years'] = (int)floor($timespan / self::YEAR));
}
if (isset($output['months'])) {
$timespan -= self::MONTH * ($output['months'] = (int)floor($timespan / self::MONTH));
}
if (isset($output['weeks'])) {
$timespan -= self::WEEK * ($output['weeks'] = (int)floor($timespan / self::WEEK));
}
if (isset($output['days'])) {
$timespan -= self::DAY * ($output['days'] = (int)floor($timespan / self::DAY));
}
if (isset($output['hours'])) {
$timespan -= self::HOUR * ($output['hours'] = (int)floor($timespan / self::HOUR));
}
if (isset($output['minutes'])) {
$timespan -= self::MINUTE * ($output['minutes'] = (int)floor($timespan / self::MINUTE));
}
// Seconds ago, 1
if (isset($output['seconds'])) {
$output['seconds'] = $timespan;
}
if (count($output) === 1) {
// Only a single output was requested, return it
return array_pop($output);
}
// Return array
return $output;
}
/**
* 格式化 UNIX 时间戳为人易读的字符串
*
* @param int Unix 时间戳
* @param mixed $local 本地时间
*
* @return string 格式化的日期字符串
*/
public static function human($remote, $local = null)
{
$time_diff = (is_null($local) || $local ? time() : $local) - $remote;
$tense = $time_diff < 0 ? 'after' : 'ago';
$time_diff = abs($time_diff);
$chunks = [
[60 * 60 * 24 * 365, 'year'],
[60 * 60 * 24 * 30, 'month'],
[60 * 60 * 24 * 7, 'week'],
[60 * 60 * 24, 'day'],
[60 * 60, 'hour'],
[60, 'minute'],
[1, 'second']
];
$name = 'second';
$count = 0;
for ($i = 0, $j = count($chunks); $i < $j; $i++) {
$seconds = $chunks[$i][0];
$name = $chunks[$i][1];
if (($count = floor($time_diff / $seconds)) != 0) {
break;
}
}
return __("%d $name%s $tense", $count, ($count > 1 ? 's' : ''));
}
/**
* 获取一个基于时间偏移的Unix时间戳
*
* @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
* @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
* @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front)end
* @param int $year 基准年,默认为null,即以当前年为基准
* @param int $month 基准月,默认为null,即以当前月为基准
* @param int $day 基准天,默认为null,即以当前天为基准
* @param int $hour 基准小时,默认为null,即以当前年小时基准
* @param int $minute 基准分钟,默认为null,即以当前分钟为基准
* @return int 处理后的Unix时间戳
*/
public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null)
{
$year = is_null($year) ? date('Y') : $year;
$month = is_null($month) ? date('m') : $month;
$day = is_null($day) ? date('d') : $day;
$hour = is_null($hour) ? date('H') : $hour;
$minute = is_null($minute) ? date('i') : $minute;
$position = in_array($position, array('begin', 'start', 'first', 'front'));
$baseTime = mktime(0, 0, 0, $month, $day, $year);
switch ($type) {
case 'minute':
$time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
break;
case 'hour':
$time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
break;
case 'day':
$time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
break;
case 'week':
$weekIndex = date("w", $baseTime);
$time = $position ?
strtotime($offset . " weeks", strtotime(date('Y-m-d', strtotime("-" . ($weekIndex ? $weekIndex - 1 : 6) . " days", $baseTime)))) :
strtotime($offset . " weeks", strtotime(date('Y-m-d 23:59:59', strtotime("+" . (6 - ($weekIndex ? $weekIndex - 1 : 6)) . " days", $baseTime))));
break;
case 'month':
$_timestamp = mktime(0, 0, 0, $month + $offset, 1, $year);
$time = $position ? $_timestamp : mktime(23, 59, 59, $month + $offset, self::days_in_month(date("m", $_timestamp), date("Y", $_timestamp)), $year);
break;
case 'quarter':
$quarter = ceil(date('n', $baseTime) / 3) + $offset;
$month = $quarter * 3;
$offset_year = ceil($month/12) - 1;
$year = $year + $offset_year;
$month = $month - ($offset_year * 12);
$time = $position ?
mktime(0, 0, 0, $month-2, 1, $year) :
mktime(23, 59, 59, $month, self::days_in_month($month, $year), $year);
break;
case 'year':
$time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
break;
default:
$time = mktime($hour, $minute, 0, $month, $day, $year);
break;
}
return $time;
}
/**
* 获取指定年月拥有的天数
* @param int $month
* @param int $year
* @return false|int|string
*/
public static function days_in_month($month, $year)
{
if (function_exists("cal_days_in_month")) {
return cal_days_in_month(CAL_GREGORIAN, $month, $year);
} else {
return date('t', mktime(0, 0, 0, $month, 1, $year));
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace support;
use DateTime;
use DateTimeZone;
/**
* 日期时间处理类
*/
class Env
{
static function get($name='',$default=null){
$envfn = run_path().'/.env';
$env = parse_ini_file($envfn,true);
$ret = $env;
if(!$name){
return $env;
}
$names = explode('.',$name);
if(!isset($ret[$names[0]])){
return $default;
}
foreach($names as $_name){
if(isset($ret[$_name])){
$ret = $ret[$_name];
}else{
return $default;
}
}
return $ret;
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
/**
* @desc ExceptionHandler
* @author Tinywan(ShaoBo Wan)
* @email 756684177@qq.com
* @date 2022/3/6 14:08
*/
declare(strict_types=1);
namespace support;
use FastRoute\BadRouteException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use Throwable;
use Tinywan\ExceptionHandler\Event\DingTalkRobotEvent;
use Tinywan\ExceptionHandler\Exception\BaseException;
use Tinywan\ExceptionHandler\Exception\ServerErrorHttpException;
use Tinywan\Jwt\Exception\JwtRefreshTokenExpiredException;
use Tinywan\Jwt\Exception\JwtTokenException;
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
use Tinywan\Validate\Exception\ValidateException;
use Webman\Exception\ExceptionHandler;
use Webman\Http\Request;
use Webman\Http\Response;
class Exception extends ExceptionHandler
{
/**
* @param Request $request
* @param Throwable $exception
* @return Response
*/
public function render(Request $request, Throwable $exception): Response
{
$code = $exception->getCode();
$debug = $this->_debug ?? $this->debug;
if ($request->expectsJson()) {
$json = ['code' => $code ?: 500, 'msg' => $debug ? $exception->getMessage() : 'Server internal error'];
$debug && $json['traces'] = (string)$exception;
return new Response($code, ['Content-Type' => 'application/json'],
\json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
$error = $debug ? \nl2br((string)$exception) : 'Server internal error';
return new Response($code, [], $error);
// $header = array_merge(['Content-Type' => 'application/json;charset=utf-8'], $this->header);
// return new Response($this->statusCode, $header, json_encode($responseBody));
}
}
+528
View File
@@ -0,0 +1,528 @@
<?php
namespace support;
use app\model\User;
use app\model\UserRule;
use support\think\Db;
class Jwt
{
protected static $instance = null;
protected static $_error = '';
protected static $_logined = false;
protected static $_user = null;
protected static $_token = '';
//Token默认有效时长
protected static $keeptime = 2592000;
protected static $requestUri = '';
protected static $rules = [];
//默认配置
protected static $config = [];
protected static $options = [];
protected static $disallowFields = [
'password','trade_password','totp_secret',//安全信息
'created_at','updated_at','loginfailure',//系统信息
//'last_time','last_ip','join_time','join_ip',最近登录信息
//'score',
'level',
];
/**
* 注册用户
*
* @param string $username 用户名
* @param string $password 密码
* @param string $email 邮箱
* @param string $mobile 手机号
* @param array $extend 扩展参数
* @return boolean
*/
public static function register($username, $password, $email = '', $mobile = '', $extend = []): array|bool
{
// 检测用户名、昵称、邮箱、手机号是否存在
if (User::getByUsername($username)) {
self::setError('Username already exist');
return false;
}
if (User::getByNickname($username)) {
self::setError('Nickname already exist');
return false;
}
if ($email && User::getByEmail($email)) {
self::setError('Email already exist');
return false;
}
if ($mobile && User::getByMobile($mobile)) {
self::setError('Mobile already exist');
return false;
}
$request = request();
if($request){
$ip = getRealIp();
}else{
$ip = '127.0.0.1';
}
$time = time();
$data = [
'username' => $username,
'password' => $password,
'email' => $email,
'mobile' => $mobile,
'level' => 1,
'score' => 0,
'avatar' => '',
];
$params = array_merge($data, [
'nickname' => preg_match("/^1[3-9]{1}\d{9}$/", $username) ? substr_replace($username, '****', 3, 4) : $username,
//'salt' => Random::alnum(),
'join_time' => $time,
'join_ip' => $ip,
'last_time' => $time,
'last_ip' => $ip,
'status' => '1'
]);
$params['password'] = \plugin\admin\app\common\Util::passwordHash($password);
$params = array_merge($params, $extend);
//账号注册时需要开启事务,避免出现垃圾数据
Db::startTrans();
try {
$user = User::create($params,[], true);
/**
* @var User $user
* */
$user->role_id;
$user = User::where('id',$user->id)->find();
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_WEB;
if(Request()->client == 'win'){
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_PC;
\support\Jwt\JwtToken::clear(\support\Jwt\JwtToken::TOKEN_CLIENT_PC);
}
$_token = \support\Jwt\JwtToken::generateToken($user->toArray());
$user->token = $_token['access_token'];
Db::commit();
//注册成功的事件
Hook("user.register_successed", $user);
return self::getUserinfo($user);
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
}
/**
* 用户登录
*
* @param string $account 账号,用户名、邮箱、手机号
* @param string $password 密码
* @return boolean
*/
public static function login($account, $password,$type=''): array|bool
{
if($type){
$field = $type;
}else{
$field = strpos('@',$account)!==false ? 'email' : 'username';
}
/**
* @var User $user
* */
if($field=='mobile'){
$user = User::where($field , $account)->where('region',Request()->post('region'))->find();
}else{
$user = User::where($field , $account)->find();
}
if (!$user) {
self::setError('Account is incorrect');
return false;
}
if ($user->status != 1) {
self::setError('Account is locked');
return false;
}
//if ($user->username!='' && !\plugin\admin\app\common\Util::passwordVerify($password,$user->password)) {
if (!\plugin\admin\app\common\Util::passwordVerify($password,$user->password)) {
self::setError('Password is incorrect');
return false;
}
//直接登录会员
return self::direct($user->id);
}
/**
* 退出
*
* @return boolean
*/
public static function logout()
{
//退出成功的事件
Hook("user.logout_successed");
return true;
}
/**
* 修改密码
* @param string $newpassword 新密码
* @param string $oldpassword 旧密码
* @param bool $ignoreoldpassword 忽略旧密码
* @return boolean
*/
public static function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
{
if (!self::isLogin()) {
self::setError('You are not logged in');
return false;
}
$user = self::getUser();
//判断旧密码是否正确
if (!\plugin\admin\app\common\Util::passwordVerify($oldpassword,$user->password) || $ignoreoldpassword) {
Db::startTrans();
try {
$salt = Random::alnum();
User::where('id',$user->id)->save([
'loginfailure' => 0,
'password' => \plugin\admin\app\common\Util::passwordHash($newpassword)
]);
//修改密码成功的事件
Hook("user.changepwd_successed", $user);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
return true;
} else {
self::setError('Password is incorrect');
return false;
}
}
/**
* 修改交易密码
* @param string $newpassword 新密码
* @param string $oldpassword 旧密码
* @param bool $ignoreoldpassword 忽略旧密码
* @return boolean
*/
public static function change_trade_pwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
{
$user = self::getUser();
if (!self::isLogin()) {
self::setError('You are not logged in');
return false;
}
//判断旧密码是否正确
if (!$user->trade_password || $ignoreoldpassword || \plugin\admin\app\common\Util::passwordVerify($oldpassword,$user->trade_password)) {
Db::startTrans();
try {
$salt = Random::alnum();
User::where('id',$user->id)->save(['trade_password' => \plugin\admin\app\common\Util::passwordHash($newpassword)]);
//修改密码成功的事件
Hook("user.change_trade_pwd_successed", $user);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
return true;
} else {
self::setError('Old password is incorrect');
return false;
}
}
static function verify_trade_password($password){
$user = self::getUser();
if(is_null($user->trade_password)){
throw new \Exception(__('Please set the trade password first'),200);
}
if (!\plugin\admin\app\common\Util::passwordVerify($password,$user->trade_password)) {
throw new \Exception(__('Trade password is incorrect'),200);
}
}
/**
* 直接登录账号
* @param int $user_id
* @return boolean
*/
public static function direct($user_id): array|bool
{
$user = User::where('id',$user_id)->find();
if ($user) {
Db::startTrans();
try {
$ip = getRealIp();
$time = time();
//记录本次登录的IP和时间
$user->last_ip = $ip;
$user->last_time = $time;
//重置登录失败次数
$user->loginfailure = 0;
$user->save();
Db::commit();
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_WEB;
if(Request()->client == 'win'){
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_PC;
//\support\Jwt\JwtToken::clear(\support\Jwt\JwtToken::TOKEN_CLIENT_PC);
}
$_user = $user->toArray();
$_user = [
'id' => $_user['id'],
'username' => $_user['username'],
'client' => $_user['client'],
];
$_token = \support\Jwt\JwtToken::generateToken($_user);
$user->token = $_token['access_token'];
//登录成功的事件
Hook("user.login_successed", $user);
return self::getUserinfo($user);
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
} else {
return false;
}
}
/**
* 检测是否是否有对应权限
* @param string $path 控制器/方法
* @param string $module 模块 默认为当前模块
* @return boolean
*/
public static function check($path = null, $module = null)
{
if (!self::isLogin()) {
return false;
}
$ruleList = self::getRuleList();
$rules = [];
foreach ($ruleList as $k => $v) {
$rules[] = $v['key'];
}
$url = ($module ? $module : request()->app) . '/' . (is_null($path) ? self::getRequestUri() : $path);
$url = strtolower(str_replace('.', '/', $url));
return in_array($url, $rules);
}
/**
* 判断是否登录
* @return boolean
*/
public static function isLogin()
{
$user_id = \support\Jwt\JwtToken::getCurrentId();
if ($user_id) {
return true;
}
return false;
}
/**
* 获取User模型
* @return User
*/
public static function getUser($user=null)
{
$user = $user ? $user : \support\Jwt\JwtToken::getUser();
return $user;
}
/**
* 获取会员基本信息
*/
public static function getUserinfo($user=null)
{
$data = self::getUser($user)->toArray();
$disallowFields = self::getDisallowFields();
$userinfo = array_diff_key($data, array_flip($disallowFields));
$userinfo['has_trade_password'] = $data['trade_password'] ? true: false;
return $userinfo;
}
/**
* 获取会员组别规则列表
* @return array|bool|\PDOStatement|string|\think\Collection
*/
public static function getRuleList()
{
$user = self::getUser();
$role_id = $user->role_id;
if (!$role_id) {
return [];
}
if (cache('group_rules_'.$role_id)) {
return cache('group_rules_'.$role_id);
}
$rules = explode(',', $role_id);
$rules = UserRule::where('status', 'normal')->where('id', 'in', $rules)->field('id,pid,key,title,type')->select();
cache('group_rules_'.$role_id,$rules);
return $rules;
}
/**
* 获取当前请求的URI
* @return string
*/
public static function getRequestUri()
{
return self::$requestUri;
}
/**
* 设置当前请求的URI
* @param string $uri
*/
public static function setRequestUri($uri)
{
self::$requestUri = $uri;
}
/**
* 获取允许输出的字段
* @return array
*/
public static function getDisallowFields()
{
return self::$disallowFields;
}
/**
* 设置允许输出的字段
* @param array $fields
*/
public static function setDisallowFields($fields)
{
self::$disallowFields = $fields;
}
/**
* 删除一个指定会员
* @param int $user_id 会员ID
* @return boolean
*/
public static function delete($user_id)
{
$user = User::where('id',$user_id)->find();
if (!$user) {
return false;
}
Db::startTrans();
try {
// 删除会员
User::destroy($user_id);
Hook("user.delete_successed", $user);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
return true;
}
/**
* 获取密码加密后的字符串
* @param string $password 密码
* @param string $salt 密码盐
* @return string
*/
public function getEncryptPassword($password, $salt = '')
{
return md5(md5($password) . $salt);
}
/**
* 检测当前控制器和方法是否匹配传递的数组
*
* @param array $arr 需要验证权限的数组
* @return boolean
*/
public static function match($arr = [])
{
$request = Request();
$arr = is_array($arr) ? $arr : explode(',', $arr);
if (!$arr) {
return false;
}
$arr = array_map('strtolower', $arr);
// 是否存在
if (in_array(strtolower($request->action), $arr) || in_array('*', $arr)) {
return true;
}
// 没找到匹配
return false;
}
/**
* 渲染用户数据
* @param array $datalist 二维数组
* @param mixed $fields 加载的字段列表
* @param string $fieldkey 渲染的字段
* @param string $renderkey 结果字段
* @return array
*/
public function render(&$datalist, $fields = [], $fieldkey = 'user_id', $renderkey = 'userinfo')
{
$fields = !$fields ? ['id', 'nickname', 'level', 'avatar'] : (is_array($fields) ? $fields : explode(',', $fields));
$ids = [];
foreach ($datalist as $k => $v) {
if (!isset($v[$fieldkey])) {
continue;
}
$ids[] = $v[$fieldkey];
}
$list = [];
if ($ids) {
if (!in_array('id', $fields)) {
$fields[] = 'id';
}
$ids = array_unique($ids);
$selectlist = User::where('id', 'in', $ids)->column($fields);
foreach ($selectlist as $k => $v) {
$list[$v['id']] = $v;
}
}
foreach ($datalist as $k => &$v) {
$v[$renderkey] = $list[$v[$fieldkey]] ?? null;
}
unset($v);
return $datalist;
}
/**
* 设置错误信息
*
* @param string $error 错误信息
* @return void
*/
public static function setError($error)
{
self::$_error = $error;
abort($error);
}
/**
* 获取错误信息
* @return string
*/
public static function getError()
{
return self::$_error ? __(self::$_error) : '';
}
}
@@ -0,0 +1,14 @@
<?php
/**
* @desc JwtCacheTokenExveption.php 描述信息
* @author Tinywan(ShaoBo Wan)
* @date 2022/3/18 18:15
*/
declare(strict_types=1);
namespace support\Jwt\Exception;
class JwtCacheTokenException extends \RuntimeException
{
}
@@ -0,0 +1,15 @@
<?php
/**
* @desc JwtConfigException
* @author Tinywan(ShaoBo Wan)
* @date 2022/2/21 9:53
*/
declare(strict_types=1);
namespace support\Jwt\Exception;
class JwtConfigException extends \RuntimeException
{
}
@@ -0,0 +1,15 @@
<?php
/**
* @desc JwtRefreshTokenExpiredException
* @author Tinywan(ShaoBo Wan)
* @date 2022/2/21 9:53
*/
declare(strict_types=1);
namespace support\Jwt\Exception;
class JwtRefreshTokenExpiredException extends \RuntimeException
{
}
@@ -0,0 +1,14 @@
<?php
/**
* @desc JwtTokenException.php 描述信息
* @author Tinywan(ShaoBo Wan)
* @date 2022/2/21 9:53
*/
declare(strict_types=1);
namespace support\Jwt\Exception;
class JwtTokenException extends \RuntimeException
{
}
@@ -0,0 +1,15 @@
<?php
/**
* @desc JwtTokenExpiredException
* @author Tinywan(ShaoBo Wan)
* @date 2022/2/21 9:53
*/
declare(strict_types=1);
namespace support\Jwt\Exception;
class JwtTokenExpiredException extends \RuntimeException
{
}
+394
View File
@@ -0,0 +1,394 @@
<?php
/**
* @desc JwtToken.php 描述信息
* @date 2022/2/21 9:45
*/
declare(strict_types=1);
namespace support\Jwt;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\SignatureInvalidException;
use support\Jwt\Exception\JwtCacheTokenException;
use support\Jwt\Exception\JwtRefreshTokenExpiredException;
use support\Jwt\Exception\JwtTokenException;
use support\Jwt\Exception\JwtConfigException;
use support\Jwt\Exception\JwtTokenExpiredException;
use UnexpectedValueException;
class JwtToken
{
/**
* access_token.
*/
private const ACCESS_TOKEN = 1;
/**
* refresh_token.
*/
private const REFRESH_TOKEN = 2;
/** WEB Client. */
public const TOKEN_CLIENT_WEB = 'WEB';
/** Mobile Client. */
public const TOKEN_CLIENT_MOBILE = 'MOBILE';
/** PC Client. */
public const TOKEN_CLIENT_PC = 'PC';
/**
* @desc: 获取当前登录ID
* @return mixed
* @throws JwtTokenException
*/
public static function getCurrentId()
{
return self::getExtendVal('id') ?? 0;
}
/**
* @desc: 获取当前用户信息
* @return mixed
*/
public static function getUser()
{
$config = self::_getConfig();
if (is_callable($config['user_model'])) {
return $config['user_model'](self::getCurrentId());
}
return [];
}
/**
* @desc: 获取指定令牌扩展内容字段的值
*
* @param string $val
* @return mixed|string
* @throws JwtTokenException
*/
public static function getExtendVal(string $val)
{
return self::getTokenExtend()[$val] ?? '';
}
/**
* @desc 获取指定令牌扩展内容
* @return array
* @throws JwtTokenException
*/
public static function getExtend(): array
{
return self::getTokenExtend();
}
/**
* @desc: 刷新令牌
* @param array $_extend 拓展数据
* @return array|string[]
* @throws JwtTokenException
*/
public static function refreshToken(&$_extend = []): array
{
$token = self::getTokenFromHeaders();
$config = self::_getConfig();
try {
$extend = self::verifyToken($token, self::REFRESH_TOKEN);
if (!empty($extend['extend'])){
$_extend = $extend['extend'];
}
} catch (SignatureInvalidException $signatureInvalidException) {
throw new JwtRefreshTokenExpiredException('Refresh token is invalid.',401021);
} catch (BeforeValidException $beforeValidException) {
throw new JwtRefreshTokenExpiredException('The refresh token has not yet taken effect.',401022);
} catch (ExpiredException $expiredException) {
throw new JwtRefreshTokenExpiredException('The refresh token session has expired, please log in again!',401023);
} catch (UnexpectedValueException $unexpectedValueException) {
throw new JwtRefreshTokenExpiredException('The extended field obtained from the refresh token does not exist.',401024);
} catch (JwtCacheTokenException | \Exception $exception) {
throw new JwtRefreshTokenExpiredException($exception->getMessage(),401025);
}
$payload = self::generatePayload($config, $extend['extend']);
$secretKey = self::getPrivateKey($config);
$extend['exp'] = time() + $config['access_exp'];
$newToken['access_token'] = self::makeToken($extend, $secretKey, $config['algorithms']);
if (!isset($config['refresh_disable']) || ($config['refresh_disable'] === false)) {
$refreshSecretKey = self::getPrivateKey($config, self::REFRESH_TOKEN);
$newToken['refresh_token'] = self::makeToken($payload['refreshPayload'], $refreshSecretKey, $config['algorithms']);
}
if ($config['is_single_device']) {
$client = $extend['extend']['client'] ?? self::TOKEN_CLIENT_WEB;
RedisHandler::generateToken($config['cache_token_pre'], (string)$client, (string)$extend['extend']['id'], $config['access_exp'], $newToken['access_token']);
RedisHandler::refreshToken($config["cache_refresh_token_pre"], (string)$client, (string)$extend['extend']['id'], $config['refresh_exp'], $newToken['refresh_token']);
}
return $newToken;
}
/**
* @desc: 生成令牌.
* @param array $extend
* @return array
* @throws JwtConfigException
*/
public static function generateToken(array $extend): array
{
if (!isset($extend['id'])) {
throw new JwtTokenException('Missing globally unique field: ID.');
}
$config = self::_getConfig();
$config['access_exp'] = $extend['access_exp'] ?? $config['access_exp'];
$config['refresh_exp'] = $extend['refresh_exp'] ?? $config['refresh_exp'];
$payload = self::generatePayload($config, $extend);
$secretKey = self::getPrivateKey($config);
$token = [
'token_type' => 'Bearer',
'expires_in' => $config['access_exp'],
'access_token' => self::makeToken($payload['accessPayload'], $secretKey, $config['algorithms'])
];
if (!isset($config['refresh_disable']) || ($config['refresh_disable'] === false)) {
$refreshSecretKey = self::getPrivateKey($config, self::REFRESH_TOKEN);
$token['refresh_token'] = self::makeToken($payload['refreshPayload'], $refreshSecretKey, $config['algorithms']);
}
if ($config['is_single_device']) {
$client = $extend['client'] ?? self::TOKEN_CLIENT_WEB;
RedisHandler::generateToken($config['cache_token_pre'], (string)$client, (string)$extend['id'], $config['access_exp'], $token['access_token']);
if (!isset($config['refresh_disable']) || ($config['refresh_disable'] === false)) {
if (isset($config["cache_refresh_token_pre"])) {
RedisHandler::generateToken($config["cache_refresh_token_pre"], (string)$client, (string)$extend['id'], $config['refresh_exp'], $token['refresh_token']);
}
}
}
return $token;
}
/**
* @desc: 验证令牌
* @param int $tokenType
* @param string|null $token
* @return array
* @throws JwtTokenException
*/
public static function verify(int $tokenType = self::ACCESS_TOKEN, string $token = null): array
{
$token = $token ?? self::getTokenFromHeaders();
try {
return self::verifyToken($token, $tokenType);
} catch (SignatureInvalidException $signatureInvalidException) {
throw new JwtTokenException('Authentication token is invalid.',401011);
} catch (BeforeValidException $beforeValidException) {
throw new JwtTokenException('The authentication token has not yet taken effect.',401012);
} catch (ExpiredException $expiredException) {
throw new JwtTokenExpiredException('The authentication session has expired, please log in again!',401013);
} catch (UnexpectedValueException $unexpectedValueException) {
throw new JwtTokenException('The obtained extended field does not exist.',401014);
} catch (JwtCacheTokenException | \Exception $exception) {
throw new JwtTokenException($exception->getMessage(),401015);
}
}
/**
* @desc: 获取扩展字段.
* @return array
* @throws JwtTokenException
*/
private static function getTokenExtend(): array
{
return (array)self::verify()['extend'];
}
/**
* @desc: 获令牌有效期剩余时长.
* @param int $tokenType
* @return int
*/
public static function getTokenExp(int $tokenType = self::ACCESS_TOKEN): int
{
return (int)self::verify($tokenType)['exp'] - time();
}
/**
* @desc: 获取Header头部authorization令牌
*
* @throws JwtTokenException
*/
private static function getTokenFromHeaders(): string
{
$authorization = request()->header('token',request()->header('authorization'));
if (!$authorization || 'undefined' == $authorization) {
$config = self::_getConfig();
if (!isset($config['is_support_get_token']) || false === $config['is_support_get_token']) {
throw new JwtTokenException('The request did not carry authorization information.',401000);
}
$authorization = request()->get($config['is_support_get_token_key']);
if (empty($authorization)) {
throw new JwtTokenException('The request did not carry authorization information.',401000);
}
}
if(substr($authorization,0,6) != 'Bearer'){
$authorization = 'Bearer '.$authorization;
}
if (self::REFRESH_TOKEN != substr_count($authorization, '.')) {
throw new JwtTokenException('Illegal authorization information.'.$authorization,401001);
}
if (2 != count(explode(' ', $authorization))) {
throw new JwtTokenException('Invalid token.',401000);
}
[$type, $token] = explode(' ', $authorization);
if ('Bearer' !== $type) {
throw new JwtTokenException('Invalid token.',401000);
}
if (!$token || 'undefined' === $token) {
throw new JwtTokenException('The Authorization information attempted to retrieve does not exist.',401000);
}
return $token;
}
/**
* @desc: 校验令牌
* @param string $token
* @param int $tokenType
* @return array
*/
private static function verifyToken(string $token, int $tokenType): array
{
$config = self::_getConfig();
$publicKey = self::ACCESS_TOKEN == $tokenType ? self::getPublicKey($config['algorithms']) : self::getPublicKey($config['algorithms'], self::REFRESH_TOKEN);
JWT::$leeway = $config['leeway'];
$decoded = JWT::decode($token, new Key($publicKey, $config['algorithms']));
$decodeToken = json_decode(json_encode($decoded), true);
if ($config['is_single_device']) {
$cacheTokenPre = $config['cache_token_pre'];
if ($tokenType == self::REFRESH_TOKEN) {
$cacheTokenPre = $config['cache_refresh_token_pre'];
}
$client = $decodeToken['extend']['client'] ?? self::TOKEN_CLIENT_WEB;
RedisHandler::verifyToken($cacheTokenPre, $client, (string)$decodeToken['extend']['id'], $token);
}
return $decodeToken;
}
/**
* @desc: 生成令牌.
*
* @param array $payload 载荷信息
* @param string $secretKey 签名key
* @param string $algorithms 算法
* @return string
*/
private static function makeToken(array $payload, string $secretKey, string $algorithms): string
{
return JWT::encode($payload, $secretKey, $algorithms);
}
/**
* @desc: 获取加密载体.
*
* @param array $config 配置文件
* @param array $extend 扩展加密字段
* @return array
*/
private static function generatePayload(array $config, array $extend): array
{
$basePayload = [
'iss' => $config['iss'], // 签发者
'aud' => $config['iss'], // 接收该JWT的一方
'iat' => time(), // 签发时间
'nbf' => time() + ($config['nbf'] ?? 0), // 某个时间点后才能访问
'exp' => time() + $config['access_exp'], // 过期时间
'extend' => $extend // 自定义扩展信息
];
$resPayLoad['accessPayload'] = $basePayload;
$basePayload['exp'] = time() + $config['refresh_exp'];
$resPayLoad['refreshPayload'] = $basePayload;
return $resPayLoad;
}
/**
* @desc: 根据签名算法获取【公钥】签名值
* @param string $algorithm 算法
* @param int $tokenType 类型
* @return string
* @throws JwtConfigException
*/
private static function getPublicKey(string $algorithm, int $tokenType = self::ACCESS_TOKEN): string
{
$config = self::_getConfig();
switch ($algorithm) {
case 'HS256':
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_secret_key'] : $config['refresh_secret_key'];
break;
case 'RS512':
case 'RS256':
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_public_key'] : $config['refresh_public_key'];
break;
default:
$key = $config['access_secret_key'];
}
return $key;
}
/**
* @desc: 根据签名算法获取【私钥】签名值
* @param array $config 配置文件
* @param int $tokenType 令牌类型
* @return string
*/
private static function getPrivateKey(array $config, int $tokenType = self::ACCESS_TOKEN): string
{
switch ($config['algorithms']) {
case 'HS256':
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_secret_key'] : $config['refresh_secret_key'];
break;
case 'RS512':
case 'RS256':
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_private_key'] : $config['refresh_private_key'];
break;
default:
$key = $config['access_secret_key'];
}
return $key;
}
/**
* @desc: 获取配置文件
* @return array
* @throws JwtConfigException
*/
private static function _getConfig(): array
{
$config = config('jwt.jwt');
if (empty($config)) {
throw new JwtConfigException('Invalid config.');
}
return $config;
}
/**
* @desc: 注销令牌
* @param string $client
* @return bool
*/
public static function clear(string $client = self::TOKEN_CLIENT_WEB): bool
{
$config = self::_getConfig();
if ($config['is_single_device']) {
$clearCacheRefreshTokenPre = RedisHandler::clearToken($config['cache_refresh_token_pre'], $client, (string)self::getCurrentId());
$clearCacheTokenPre = RedisHandler::clearToken($config['cache_token_pre'], $client, (string)self::getCurrentId());
return $clearCacheTokenPre && $clearCacheRefreshTokenPre;
}
return true;
}
}
+90
View File
@@ -0,0 +1,90 @@
<?php
/**
* @desc RedisHanle.php 描述信息
* @author Tinywan(ShaoBo Wan)
* @date 2022/3/18 17:13
*/
declare(strict_types=1);
namespace support\Jwt;
use support\Redis;
use support\Jwt\Exception\JwtCacheTokenException;
class RedisHandler
{
/**
* @desc: 生成缓存令牌
* (1)登录时,判断该账号是否在其它设备登录,如果有,就请空之前key清除,
* (2)重新设置key,然后存储用户信息在redis当中
* @param string $pre
* @param string $client
* @param string $uid
* @param int $ttl
* @param string $token
* @author Tinywan(ShaoBo Wan)
*/
public static function generateToken(string $pre, string $client, string $uid, int $ttl, string $token): void
{
$cacheKey = $pre . $client. ':'. $uid;
Redis::del($cacheKey);
Redis::setex($cacheKey, $ttl, $token);
}
/**
* @desc: 刷新存储的缓存令牌
* @param string $pre
* @param string $client
* @param string $uid
* @param int $ttl
* @param string $token
* @return void
*/
public static function refreshToken(string $pre, string $client, string $uid, int $ttl, string $token): void
{
$cacheKey = $pre . $client . ':' . $uid;
$isExists = Redis::exists($cacheKey);
if ($isExists) {
$ttl = Redis::ttl($cacheKey);
}
Redis::setex($cacheKey, $ttl, $token);
}
/**
* @desc: 检查设备缓存令牌
* @param string $pre
* @param string $client
* @param string $uid
* @param string $token
* @return bool
* @author Tinywan(ShaoBo Wan)
*/
public static function verifyToken(string $pre, string $client, string $uid, string $token): bool
{
$cacheKey = $pre . $client. ':'. $uid;
if (!Redis::exists($cacheKey)) {
throw new JwtCacheTokenException('The account has been logged in on another device and forced to log out.');
}
if (Redis::get($cacheKey) != $token) {
throw new JwtCacheTokenException('Identity verification session has expired, please log in again!');
}
return true;
}
/**
* @desc: 清理缓存令牌
* @param string $pre
* @param string $client
* @param string $uid
* @return bool
* @author Tinywan(ShaoBo Wan)
*/
public static function clearToken(string $pre, string $client, string $uid): bool
{
Redis::del($pre . $client. ':'. $uid);
return true;
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
return [
'enable' => true,
'jwt' => [
/** 算法类型 HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、Ed25519 */
'algorithms' => 'HS256',
/** access令牌秘钥 */
'access_secret_key' => '2022d3d3LmJq',
/** access令牌过期时间,单位:秒。默认 2 小时 */
'access_exp' => 7200,
/** refresh令牌秘钥 */
'refresh_secret_key' => '2022KTxigxc9o50c',
/** refresh令牌过期时间,单位:秒。默认 7 天 */
'refresh_exp' => 604800,
/** refresh 令牌是否禁用,默认不禁用 false */
'refresh_disable' => false,
/** 令牌签发者 */
'iss' => 'webman.tinywan.cn',
/** 某个时间点后才能访问,单位秒。(如:30 表示当前时间30秒后才能使用) */
'nbf' => 0,
/** 时钟偏差冗余时间,单位秒。建议这个余地应该不大于几分钟 */
'leeway' => 60,
/** 是否允许单设备登录,默认不允许 false */
'is_single_device' => false,
/** 缓存令牌时间,单位:秒。默认 7 天 */
'cache_token_ttl' => 604800,
/** 缓存令牌前缀,默认 JWT:TOKEN: */
'cache_token_pre' => 'JWT:TOKEN:',
/** 缓存刷新令牌前缀,默认 JWT:REFRESH_TOKEN: */
'cache_refresh_token_pre' => 'JWT:REFRESH_TOKEN:',
/** 用户信息模型 */
'user_model' => function ($uid) {
return [];
},
/** 是否支持 get 请求获取令牌 */
'is_support_get_token' => false,
/** GET 请求获取令牌请求key */
'is_support_get_token_key' => 'authorization',
/** access令牌私钥 */
'access_private_key' => <<<EOD
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
EOD,
/** access令牌公钥 */
'access_public_key' => <<<EOD
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOD,
/** refresh令牌私钥 */
'refresh_private_key' => <<<EOD
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
EOD,
/** refresh令牌公钥 */
'refresh_public_key' => <<<EOD
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOD,
],
];
+110
View File
@@ -0,0 +1,110 @@
<?php
namespace support;
/**
* 随机生成类
*/
class Random
{
/**
* 生成数字和字母
*
* @param int $len 长度
* @return string
*/
public static function alnum($len = 6)
{
return self::build('alnum', $len);
}
/**
* 仅生成字符
*
* @param int $len 长度
* @return string
*/
public static function alpha($len = 6)
{
return self::build('alpha', $len);
}
/**
* 生成指定长度的随机数字
*
* @param int $len 长度
* @return string
*/
public static function numeric($len = 4)
{
return self::build('numeric', $len);
}
/**
* 生成指定长度的无0随机数字
*
* @param int $len 长度
* @return string
*/
public static function nozero($len = 4)
{
return self::build('nozero', $len);
}
/**
* 能用的随机数生成
* @param string $type 类型 alpha/alnum/numeric/nozero/unique/md5/encrypt/sha1
* @param int $len 长度
* @return string
*/
public static function build($type = 'alnum', $len = 8)
{
switch ($type) {
case 'alpha':
case 'alnum':
case 'numeric':
case 'nozero':
switch ($type) {
case 'alpha':
$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'alnum':
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'numeric':
$pool = '0123456789';
break;
case 'nozero':
$pool = '123456789';
break;
}
return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);
case 'unique':
case 'md5':
return md5(uniqid(mt_rand()));
case 'encrypt':
case 'sha1':
return sha1(uniqid(mt_rand(), true));
}
}
/**
* 获取全球唯一标识
* @return string
*/
public static function uuid()
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
}
}
+160
View File
@@ -0,0 +1,160 @@
<?php
namespace support\TagLib;
class Html extends \think\template\TagLib{
/**
* 定义标签列表
*/
protected $tags = [
'switch' => ['attr' => 'name,yes,no,value', 'close' => 0],
'radio' => ['attr' => 'name,options,value', 'close' => 0],
'checkbox' => ['attr' => 'name,options,value', 'close' => 0],
'upload' => ['attr' => 'name,value,mimetype,multiple,url,maxsize,maxcount,params', 'close' => 0]
];
/**
* 上传
* @param mixed $tag
* @return string
*/
public function tagUpload($tag)
{
$value = isset($tag['value'])?$tag['value']:'';unset($tag['value']);
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
$mimetype = isset($tag['mimetype'])?$tag['mimetype']:'';unset($tag['mimetype']);
$multiple = isset($tag['multiple'])?$tag['multiple']:'';unset($tag['multiple']);
$maxcount = isset($tag['maxcount'])?$tag['maxcount']:'';unset($tag['maxcount']);
$maxsize = isset($tag['maxsize'])?$tag['maxsize']:'';unset($tag['maxsize']);
$url = isset($tag['url'])?$tag['url']:'';unset($tag['url']);
// 支持用函数传数组
$parseStr = '';
$name = $this->buildvar($name,$parseStr,'name');
$value = $this->buildvar($value,$parseStr,'value');
$mimetype = $this->buildvar($mimetype,$mimetype,'mimetype');
$multiple = $this->buildvar($multiple,$multiple,'multiple');
$maxcount = $this->buildvar($maxcount,$maxcount,'maxcount');
$maxsize = $this->buildvar($maxsize,$maxsize,'maxsize');
$url = $this->buildvar($url,$url,'url');
$extendData = $this->buildExtendData($tag);
$parseStr .= '<input id="c-{'.$name.'}" class="form-control" size="50" name="{'.$name.'}" type="hidden" value="{'.$value.'}">';
$parseStr .= '<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-{'.$name.'}">';
$parseStr .= ' <li nodelete class="col-xs-4 col-sm-3 col-md-2">';
$parseStr .= ' <a class="pic-add faupload" style="height: auto;border: 0;"';
$parseStr .= ' permission="app.admin.upload.avatar" id="add-pic-btn" href="#!" title="点击上传"';
$parseStr .= ' data-input-id="c-{'.$name.'}"';
$parseStr .= ' data-mimetype="{'.$mimetype.'}"';
$parseStr .= ' data-multiple="{'.$multiple.'}" ';
$parseStr .= ' data-maxsize="{'.$maxsize.'}" ';
$parseStr .= ' data-maxcount="{'.$maxcount.'}" ';
$parseStr .= ' data-url="{'.$url.'}" ';
$parseStr .= ' '.$extendData.' ';
$parseStr .= ' data-preview-id="p-{'.$name.'}"></a>';
$parseStr .= ' <a class="pic-add fachoose" style="height: auto;border: 0;"';
$parseStr .= ' permission="app.admin.upload.attachment" ';
$parseStr .= ' id="choose-pic-btn" href="#!" title="选择文件"';
$parseStr .= ' data-input-id="c-{'.$name.'}" ';
$parseStr .= ' data-mimetype="{'.$mimetype.'}" ';
$parseStr .= ' data-multiple="{'.$multiple.'}" ';
$parseStr .= ' data-maxsize="{'.$maxsize.'}" ';
$parseStr .= ' data-maxcount="{'.$maxcount.'}" ';
$parseStr .= ' data-url="{'.$url.'}" ';
$parseStr .= ' '.$extendData.' ';
$parseStr .= ' data-preview-id="p-{'.$name.'}"></a>';
$parseStr .= ' </li>';
$parseStr .= '</ul>';
return $parseStr;
}
/**
* switch
*/
public function tagSwitch($tag)
{
$yes = isset($tag['yes'])?$tag['yes']:1;unset($tag['yes']);
$no = isset($tag['no'])?$tag['no']:0;unset($tag['no']);
$value = isset($tag['value'])?$tag['value']:1;unset($tag['value']);
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
// 支持用函数传数组
$parseStr = '';
$name = $this->buildvar($name,$parseStr,'name');
$value = $this->buildvar($value,$parseStr,'value');
$no = $this->buildvar($no,$parseStr,'no',0);
$yes = $this->buildvar($yes,$parseStr,'yes',1);
$extendData = $this->buildExtendData($tag);
$parseStr .= '<?php ';
$parseStr .= $yes.'='.$yes.'??1;'.PHP_EOL;
$parseStr .= $no.'='.$no.'??0;'.PHP_EOL;
$parseStr .= $value.'='.$yes.'=='.$value .' ? '.$yes.' : '.$no.';'.PHP_EOL;
$parseStr .= '?><label data-role="switcher" class="lyear-switch switch-success switch-light switch-outline">';
$parseStr .= ' <input type="hidden" data-yes="{'.$yes.'}" data-no="{'.$no.'}" id="c-{'.$name.'}" name="{'.$name.'}" value="{'.$value.'}"/>';
$parseStr .= ' <input id="c-{'.$name.'}-switch" type="checkbox" {if '.$yes .'=='. $value.'}checked{/if} '.$extendData.' />';
$parseStr .= ' <span></span>';
$parseStr .= '</label>';
return $parseStr;
}
/**
* radio
*/
public function tagRadio($tag)
{
$value = isset($tag['value'])?$tag['value']:1;unset($tag['value']);
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
$option = isset($tag['options'])?$tag['options']:[];unset($tag['options']);
// 支持用函数传数组
$parseStr = '';
$name = $this->buildvar($name,$parseStr,'name');
$value = $this->buildvar($value,$parseStr,'value');
$options = $this->buildvar($option,$parseStr,'option');
$extendData = $this->buildExtendData($tag);
$parseStr .= '<?php foreach ('.$options. ' as $_key=>$_val){?>';
$parseStr .= '<label class="lyear-radio radio-inline radio-primary">';
$parseStr .= ' <input name="{'.$name.'}" value="{$_key}" id="c-{'.$name.'}-radio" type="radio" {if $_key=='.$value.'}checked{/if} '.$extendData.' />';
$parseStr .= ' <span>{$_val}</span>';
$parseStr .= '</label>';
$parseStr .= '<?php }?>';
return $parseStr;
}
/**
* checkbox
*/
public function tagCheckbox($tag)
{
$value = isset($tag['value'])?$tag['value']:1;unset($tag['value']);
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
$option = isset($tag['options'])?$tag['options']:[];unset($tag['options']);
// 支持用函数传数组
$parseStr = '';
$name = $this->buildvar($name,$parseStr,'name');
$value = $this->buildvar($value,$parseStr,'value');
$options = $this->buildvar($option,$parseStr,'option');
$extendData = $this->buildExtendData($tag);
$parseStr .= '<?php foreach ('.$options. ' as $_key=>$_val){?>';
$parseStr .= '<label class="lyear-checkbox checkbox-inline checkbox-primary">';
$parseStr .= ' <input name="{'.$name.'}[]" value="{$_key}" id="c-{'.$name.'}-{$_key}" type="checkbox" {if in_array($_key,'.$value.')}checked{/if} '.$extendData.' />';
$parseStr .= ' <span>{$_val}</span>';
$parseStr .= '</label>';
$parseStr .= '<?php }?>';
return $parseStr;
}
protected function buildvar($name='',&$parseStr='',$defaultName='',$default=''){
$var = '$'.$defaultName.'_' . uniqid();
$parseStr .= '<?php ';
if(null == $name){
$parseStr .= $var . '="'.$default.'"; '.PHP_EOL;
}elseif (in_array(substr($name, 0, 1),[':','$'])) {
$name = $this->autoBuildVar($name);
$parseStr .= $var . '=' . $name . '; '.PHP_EOL;
}else{
$parseStr .= $var . '="' . $name . '"; '.PHP_EOL;
}
$parseStr .='?>';
return $var;
}
protected function buildExtendData($arr=[]){
$parseStr = '';
foreach($arr as $key=>$val){
$parseStr .= $key . '="' . $val . '"';
}
return $parseStr;
}
}
+139
View File
@@ -0,0 +1,139 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Dotenv\Dotenv;
use support\Log;
use Webman\Bootstrap;
use Webman\Config;
use Webman\Middleware;
use Webman\Route;
use Webman\Util;
use Workerman\Events\Select;
use Workerman\Worker;
$worker = $worker ?? null;
if (empty(Worker::$eventLoopClass)) {
Worker::$eventLoopClass = Select::class;
}
set_error_handler(function ($level, $message, $file = '', $line = 0) {
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
});
if ($worker) {
register_shutdown_function(function ($startTime) {
if (time() - $startTime <= 0.1) {
sleep(1);
}
}, time());
}
if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) {
if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) {
Dotenv::createUnsafeMutable(base_path(false))->load();
} else {
Dotenv::createMutable(base_path(false))->load();
}
}
Config::clear();
support\App::loadAllConfig(['route']);
if ($timezone = config('app.default_timezone')) {
date_default_timezone_set($timezone);
}
foreach (config('autoload.files', []) as $file) {
include_once $file;
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['autoload']['files'] ?? [] as $file) {
include_once $file;
}
}
foreach ($projects['autoload']['files'] ?? [] as $file) {
include_once $file;
}
}
Middleware::load(config('middleware', []));
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project) || $name === 'static') {
continue;
}
Middleware::load($project['middleware'] ?? []);
}
Middleware::load($projects['middleware'] ?? [], $firm);
if ($staticMiddlewares = config("plugin.$firm.static.middleware")) {
Middleware::load(['__static__' => $staticMiddlewares], $firm);
}
}
Middleware::load(['__static__' => config('static.middleware', [])]);
foreach (config('bootstrap', []) as $className) {
if (!class_exists($className)) {
$log = "Warning: Class $className setting in config/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $className */
$className::start($worker);
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['bootstrap'] ?? [] as $className) {
if (!class_exists($className)) {
$log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $className */
$className::start($worker);
}
}
foreach ($projects['bootstrap'] ?? [] as $className) {
/** @var string $className */
if (!class_exists($className)) {
$log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $className */
$className::start($worker);
}
}
$directory = base_path() . '/plugin';
$paths = [config_path()];
foreach (Util::scanDir($directory) as $path) {
if (is_dir($path = "$path/config")) {
$paths[] = $path;
}
}
Route::load($paths);
+4
View File
@@ -0,0 +1,4 @@
<?php
// This file is generated by Webman, please don't modify it.
use support\Request;
use support\Response;