仓库初始化

This commit is contained in:
2025-08-13 10:43:56 +08:00
commit e8f9b46680
5180 changed files with 859303 additions and 0 deletions

265
extend/fast/Auth.php Normal file
View File

@@ -0,0 +1,265 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: luofei614 <weibo.com/luofei614>
// +----------------------------------------------------------------------
// | 修改者: anuo (本权限类在原3.2.3的基础上修改过来的)
// +----------------------------------------------------------------------
namespace fast;
use think\Db;
use think\Config;
use think\Session;
use think\Request;
/**
* 权限认证类
* 功能特性:
* 1是对规则进行认证不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。
* $auth=new Auth(); $auth->check('规则名称','用户id')
* 2可以同时对多条规则进行认证并设置多条规则的关系or或者and
* $auth=new Auth(); $auth->check('规则1,规则2','用户id','and')
* 第三个参数为and时表示用户需要同时具有规则1和规则2的权限。 当第三个参数为or时表示用户值需要具备其中一个条件即可。默认为or
* 3一个用户可以属于多个用户组(think_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(think_auth_group 定义了用户组权限)
* 4支持规则表达式。
* 在think_auth_rule 表中定义一条规则condition字段就可以定义规则表达式。 如定义{score}>5 and {score}<100
* 表示用户的分数在5-100之间时这条规则才会通过。
*/
class Auth
{
/**
* @var object 对象实例
*/
protected static $instance;
protected $rules = [];
/**
* 当前请求实例
* @var Request
*/
protected $request;
//默认配置
protected $config = [
'auth_on' => 1, // 权限开关
'auth_type' => 1, // 认证方式1为实时认证2为登录认证。
'auth_group' => 'auth_group', // 用户组数据表名
'auth_group_access' => 'auth_group_access', // 用户-用户组关系表
'auth_rule' => 'auth_rule', // 权限规则表
'auth_user' => 'user', // 用户信息表
];
public function __construct()
{
if ($auth = Config::get('auth')) {
$this->config = array_merge($this->config, $auth);
}
// 初始化request
$this->request = Request::instance();
}
/**
* 初始化
* @access public
* @param array $options 参数
* @return Auth
*/
public static function instance($options = [])
{
if (is_null(self::$instance)) {
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 检查权限
* @param string|array $name 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
* @param int $uid 认证用户的id
* @param string $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
* @param string $mode 执行验证的模式,可分为url,normal
* @return bool 通过验证返回true;失败返回false
*/
public function check($name, $uid, $relation = 'or', $mode = 'url')
{
if (!$this->config['auth_on']) {
return true;
}
// 获取用户需要验证的所有有效规则列表
$rulelist = $this->getRuleList($uid);
if (in_array('*', $rulelist)) {
return true;
}
if (is_string($name)) {
$name = strtolower($name);
if (strpos($name, ',') !== false) {
$name = explode(',', $name);
} else {
$name = [$name];
}
}
$list = []; //保存验证通过的规则名
if ('url' == $mode) {
$REQUEST = unserialize(strtolower(serialize($this->request->param())));
}
foreach ($rulelist as $rule) {
$query = preg_replace('/^.+\?/U', '', $rule);
if ('url' == $mode && $query != $rule) {
parse_str($query, $param); //解析规则中的param
$intersect = array_intersect_assoc($REQUEST, $param);
$rule = preg_replace('/\?.*$/U', '', $rule);
if (in_array($rule, $name) && $intersect == $param) {
//如果节点相符且url参数满足
$list[] = $rule;
}
} else {
if (in_array($rule, $name)) {
$list[] = $rule;
}
}
}
if ('or' == $relation && !empty($list)) {
return true;
}
$diff = array_diff($name, $list);
if ('and' == $relation && empty($diff)) {
return true;
}
return false;
}
/**
* 根据用户id获取用户组,返回值为数组
* @param int $uid 用户id
* @return array 用户所属的用户组 array(
* array('uid'=>'用户id','group_id'=>'用户组id','name'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
* ...)
*/
public function getGroups($uid)
{
static $groups = [];
if (isset($groups[$uid])) {
return $groups[$uid];
}
// 执行查询
$user_groups = Db::name($this->config['auth_group_access'])
->alias('aga')
->join('__' . strtoupper($this->config['auth_group']) . '__ ag', 'aga.group_id = ag.id', 'LEFT')
->field('aga.uid,aga.group_id,ag.id,ag.pid,ag.name,ag.rules')
->where("aga.uid='{$uid}' and ag.status='normal'")
->select();
$groups[$uid] = $user_groups ?: [];
return $groups[$uid];
}
/**
* 获得权限规则列表
* @param int $uid 用户id
* @return array
*/
public function getRuleList($uid)
{
static $_rulelist = []; //保存用户验证通过的权限列表
if (isset($_rulelist[$uid])) {
return $_rulelist[$uid];
}
if (2 == $this->config['auth_type'] && Session::has('_rule_list_' . $uid)) {
return Session::get('_rule_list_' . $uid);
}
// 读取用户规则节点
$ids = $this->getRuleIds($uid);
if (empty($ids)) {
$_rulelist[$uid] = [];
return [];
}
// 筛选条件
$where = [
'status' => 'normal'
];
if (!in_array('*', $ids)) {
$where['id'] = ['in', $ids];
}
//读取用户组所有权限规则
$this->rules = Db::name($this->config['auth_rule'])->where($where)->field('id,pid,condition,icon,name,title,ismenu')->select();
//循环规则,判断结果。
$rulelist = []; //
if (in_array('*', $ids)) {
$rulelist[] = "*";
}
foreach ($this->rules as $rule) {
//超级管理员无需验证condition
if (!empty($rule['condition']) && !in_array('*', $ids)) {
//根据condition进行验证
$user = $this->getUserInfo($uid); //获取用户信息,一维数组
$nums = 0;
$condition = str_replace(['&&', '||'], "\r\n", $rule['condition']);
$condition = preg_replace('/\{(\w*?)\}/', '\\1', $condition);
$conditionArr = explode("\r\n", $condition);
foreach ($conditionArr as $index => $item) {
preg_match("/^(\w+)\s?([\>\<\=]+)\s?(.*)$/", trim($item), $matches);
if ($matches && isset($user[$matches[1]]) && version_compare($user[$matches[1]], $matches[3], $matches[2])) {
$nums++;
}
}
if ($conditionArr && ((stripos($rule['condition'], "||") !== false && $nums > 0) || count($conditionArr) == $nums)) {
$rulelist[$rule['id']] = strtolower($rule['name']);
}
} else {
//只要存在就记录
$rulelist[$rule['id']] = strtolower($rule['name']);
}
}
$_rulelist[$uid] = $rulelist;
//登录验证则需要保存规则列表
if (2 == $this->config['auth_type']) {
//规则列表结果保存到session
Session::set('_rule_list_' . $uid, $rulelist);
}
return array_unique($rulelist);
}
public function getRuleIds($uid)
{
//读取用户所属用户组
$groups = $this->getGroups($uid);
$ids = []; //保存用户所属用户组设置的所有权限规则id
foreach ($groups as $g) {
$ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
}
$ids = array_unique($ids);
return $ids;
}
/**
* 获得用户资料
* @param int $uid 用户id
* @return mixed
*/
protected function getUserInfo($uid)
{
static $user_info = [];
$user = Db::name($this->config['auth_user']);
// 获取用户表主键
$_pk = is_string($user->getPk()) ? $user->getPk() : 'uid';
if (!isset($user_info[$uid])) {
$user_info[$uid] = $user->where($_pk, $uid)->find();
}
return $user_info[$uid];
}
}

230
extend/fast/Date.php Normal file
View File

@@ -0,0 +1,230 @@
<?php
namespace fast;
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) ? 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));
}
}
}

1289
extend/fast/Form.php Normal file

File diff suppressed because it is too large Load Diff

185
extend/fast/Http.php Normal file
View File

@@ -0,0 +1,185 @@
<?php
namespace fast;
/**
* Http 请求类
*/
class Http
{
/**
* 发送一个POST请求
* @param string $url 请求URL
* @param array $params 请求参数
* @param array $options 扩展参数
* @return mixed|string
*/
public static function post($url, $params = [], $options = [])
{
$req = self::sendRequest($url, $params, 'POST', $options);
return $req['ret'] ? $req['msg'] : '';
}
/**
* 发送一个GET请求
* @param string $url 请求URL
* @param array $params 请求参数
* @param array $options 扩展参数
* @return mixed|string
*/
public static function get($url, $params = [], $options = [])
{
$req = self::sendRequest($url, $params, 'GET', $options);
return $req['ret'] ? $req['msg'] : '';
}
/**
* CURL发送Request请求,含POST和REQUEST
* @param string $url 请求的链接
* @param mixed $params 传递的参数
* @param string $method 请求的方法
* @param mixed $options CURL的参数
* @return array
*/
public static function sendRequest($url, $params = [], $method = 'POST', $options = [])
{
$method = strtoupper($method);
$protocol = substr($url, 0, 5);
$query_string = is_array($params) ? http_build_query($params) : $params;
$ch = curl_init();
$defaults = [];
if ('GET' == $method) {
$geturl = $query_string ? $url . (stripos($url, "?") !== false ? "&" : "?") . $query_string : $url;
$defaults[CURLOPT_URL] = $geturl;
} else {
$defaults[CURLOPT_URL] = $url;
if ($method == 'POST') {
$defaults[CURLOPT_POST] = 1;
} else {
$defaults[CURLOPT_CUSTOMREQUEST] = $method;
}
$defaults[CURLOPT_POSTFIELDS] = is_array($params) && count(array_filter($params, 'is_array')) > 0 ? $query_string : $params;
}
$defaults[CURLOPT_HEADER] = false;
$defaults[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36";
$defaults[CURLOPT_FOLLOWLOCATION] = true;
$defaults[CURLOPT_RETURNTRANSFER] = true;
$defaults[CURLOPT_CONNECTTIMEOUT] = 10;
$defaults[CURLOPT_TIMEOUT] = 10;
// disable 100-continue
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
if ('https' == $protocol) {
$defaults[CURLOPT_SSL_VERIFYPEER] = false;
$defaults[CURLOPT_SSL_VERIFYHOST] = false;
}
curl_setopt_array($ch, (array)$options + $defaults);
$ret = curl_exec($ch);
$err = curl_error($ch);
if (false === $ret || !empty($err)) {
$errno = curl_errno($ch);
$info = curl_getinfo($ch);
curl_close($ch);
return [
'ret' => false,
'errno' => $errno,
'msg' => $err,
'info' => $info,
];
}
curl_close($ch);
return [
'ret' => true,
'msg' => $ret,
];
}
/**
* 异步发送一个请求
* @param string $url 请求的链接
* @param mixed $params 请求的参数
* @param string $method 请求的方法
* @return boolean TRUE
*/
public static function sendAsyncRequest($url, $params = [], $method = 'POST')
{
$method = strtoupper($method);
$method = $method == 'POST' ? 'POST' : 'GET';
//构造传递的参数
if (is_array($params)) {
$post_params = [];
foreach ($params as $k => &$v) {
if (is_array($v)) {
$v = implode(',', $v);
}
$post_params[] = $k . '=' . urlencode($v);
}
$post_string = implode('&', $post_params);
} else {
$post_string = $params;
}
$parts = parse_url($url);
//构造查询的参数
if ($method == 'GET' && $post_string) {
$parts['query'] = isset($parts['query']) ? $parts['query'] . '&' . $post_string : $post_string;
$post_string = '';
}
$parts['query'] = isset($parts['query']) && $parts['query'] ? '?' . $parts['query'] : '';
//发送socket请求,获得连接句柄
$fp = fsockopen($parts['host'], $parts['port'] ?? 80, $errno, $errstr, 10);
if (!$fp) {
return false;
}
//设置超时时间
stream_set_timeout($fp, 10);
$out = "{$method} {$parts['path']}{$parts['query']} HTTP/1.1\r\n";
$out .= "Host: {$parts['host']}\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
$out .= "Content-Length: " . strlen($post_string) . "\r\n";
$out .= "Connection: Close\r\n\r\n";
if ($post_string !== '') {
$out .= $post_string;
}
fwrite($fp, $out);
//不用关心服务器返回结果
//echo fread($fp, 1024);
fclose($fp);
return true;
}
/**
* 发送文件到客户端
* @param string $file
* @param bool $delaftersend
* @param bool $exitaftersend
*/
public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true)
{
if (file_exists($file) && is_readable($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment;filename = ' . basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check = 0, pre-check = 0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
if ($delaftersend) {
unlink($file);
}
if ($exitaftersend) {
exit;
}
}
}
}

36
extend/fast/Pinyin.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
namespace fast;
/**
* 中文转拼音类
*/
class Pinyin
{
/**
* 获取文字的拼音
* @param string $chinese 中文汉字
* @param boolean $onlyfirst 是否只返回拼音首字母
* @param string $delimiter 分隔符
* @param bool $ucfirst 是否首字母大写
* @return string
*/
public static function get($chinese, $onlyfirst = false, $delimiter = '', $ucfirst = false)
{
$pinyin = new \Overtrue\Pinyin\Pinyin();
if ($onlyfirst) {
$result = $pinyin->abbr($chinese, $delimiter);
} else {
$result = $pinyin->permalink($chinese, $delimiter);
}
if ($ucfirst) {
$pinyinArr = explode($delimiter, $result);
$result = implode($delimiter, array_map('ucfirst', $pinyinArr));
}
return $result;
}
}

110
extend/fast/Random.php Normal file
View File

@@ -0,0 +1,110 @@
<?php
namespace fast;
/**
* 随机生成类
*/
class Random
{
/**
* 生成数字和字母
*
* @param int $len 长度
* @return string
*/
public static function alnum(int $len = 6): string
{
return self::build('alnum', $len);
}
/**
* 仅生成字符
*
* @param int $len 长度
* @return string
*/
public static function alpha(int $len = 6): string
{
return self::build('alpha', $len);
}
/**
* 生成指定长度的随机数字
*
* @param int $len 长度
* @return string
*/
public static function numeric(int $len = 4): string
{
return self::build('numeric', $len);
}
/**
* 生成指定长度的无0随机数字
*
* @param int $len 长度
* @return string
*/
public static function nozero(int $len = 4): string
{
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(string $type = 'alnum', int $len = 8): string
{
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(): string
{
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)
);
}
}

179
extend/fast/Rsa.php Normal file
View File

@@ -0,0 +1,179 @@
<?php
namespace fast;
/**
* RSA签名类
*/
class Rsa
{
public $publicKey = '';
public $privateKey = '';
private $_privKey;
/**
* * private key
*/
private $_pubKey;
/**
* * public key
*/
private $_keyPath;
/**
* * the keys saving path
*/
/**
* * the construtor,the param $path is the keys saving path
* @param string $publicKey 公钥
* @param string $privateKey 私钥
*/
public function __construct($publicKey = null, $privateKey = null)
{
$this->setKey($publicKey, $privateKey);
}
/**
* 设置公钥和私钥
* @param string $publicKey 公钥
* @param string $privateKey 私钥
*/
public function setKey($publicKey = null, $privateKey = null)
{
if (!is_null($publicKey)) {
$this->publicKey = $publicKey;
}
if (!is_null($privateKey)) {
$this->privateKey = $privateKey;
}
}
/**
* * setup the private key
*/
private function setupPrivKey()
{
if (is_resource($this->_privKey)) {
return true;
}
$pem = chunk_split($this->privateKey, 64, "\n");
$pem = "-----BEGIN PRIVATE KEY-----\n" . $pem . "-----END PRIVATE KEY-----\n";
$this->_privKey = openssl_pkey_get_private($pem);
return true;
}
/**
* * setup the public key
*/
private function setupPubKey()
{
if (is_resource($this->_pubKey)) {
return true;
}
$pem = chunk_split($this->publicKey, 64, "\n");
$pem = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----\n";
$this->_pubKey = openssl_pkey_get_public($pem);
return true;
}
/**
* * encrypt with the private key
*/
public function privEncrypt($data)
{
if (!is_string($data)) {
return null;
}
$this->setupPrivKey();
$r = openssl_private_encrypt($data, $encrypted, $this->_privKey);
if ($r) {
return base64_encode($encrypted);
}
return null;
}
/**
* * decrypt with the private key
*/
public function privDecrypt($encrypted)
{
if (!is_string($encrypted)) {
return null;
}
$this->setupPrivKey();
$encrypted = base64_decode($encrypted);
$r = openssl_private_decrypt($encrypted, $decrypted, $this->_privKey);
if ($r) {
return $decrypted;
}
return null;
}
/**
* * encrypt with public key
*/
public function pubEncrypt($data)
{
if (!is_string($data)) {
return null;
}
$this->setupPubKey();
$r = openssl_public_encrypt($data, $encrypted, $this->_pubKey);
if ($r) {
return base64_encode($encrypted);
}
return null;
}
/**
* * decrypt with the public key
*/
public function pubDecrypt($crypted)
{
if (!is_string($crypted)) {
return null;
}
$this->setupPubKey();
$crypted = base64_decode($crypted);
$r = openssl_public_decrypt($crypted, $decrypted, $this->_pubKey);
if ($r) {
return $decrypted;
}
return null;
}
/**
* 构造签名
* @param string $dataString 被签名数据
* @return string
*/
public function sign($dataString)
{
$this->setupPrivKey();
$signature = false;
openssl_sign($dataString, $signature, $this->_privKey);
return base64_encode($signature);
}
/**
* 验证签名
* @param string $dataString 被签名数据
* @param string $signString 已经签名的字符串
* @return number 1签名正确 0签名错误
*/
public function verify($dataString, $signString)
{
$this->setupPubKey();
$signature = base64_decode($signString);
$flg = openssl_verify($dataString, $signature, $this->_pubKey);
return $flg;
}
public function __destruct()
{
is_resource($this->_privKey) && @openssl_free_key($this->_privKey);
is_resource($this->_pubKey) && @openssl_free_key($this->_pubKey);
}
}

334
extend/fast/Tencentst.php Normal file
View File

@@ -0,0 +1,334 @@
<?php
namespace fast;
if ( version_compare( PHP_VERSION, '5.1.2' ) < 0 ) {
trigger_error( 'need php 5.1.2 or newer', E_USER_ERROR );
}
class Tencentst
{
private $key = false;
private $sdkappid = 0;
/**
*【功能说明】用于签发 TRTC 和 IM 服务中必须要使用的 UserSig 鉴权票据
*
*【参数说明】
* @param string userid - 用户id限制长度为32字节只允许包含大小写英文字母a-zA-Z、数字0-9及下划线和连词符。
* @param string expire - UserSig 票据的过期时间,单位是秒,比如 86400 代表生成的 UserSig 票据在一天后就无法再使用了。
* @return string 签名字符串
* @throws \Exception
*/
public function genUserSig( $userid, $expire = 86400*180 ) {
return $this->__genSig( $userid, $expire, '', false );
}
/**
*【功能说明】
* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。
* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:
* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。
* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。
* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】=>【应用管理】=>【应用信息】中打开“启动权限密钥”开关。
*
*【参数说明】
* @param userid - 用户id限制长度为32字节只允许包含大小写英文字母a-zA-Z、数字0-9及下划线和连词符。
* @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。
* @param roomid - 房间号,用于指定该 userid 可以进入的房间号
* @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:
* - 第 1 位0000 0001 = 1创建房间的权限
* - 第 2 位0000 0010 = 2加入房间的权限
* - 第 3 位0000 0100 = 4发送语音的权限
* - 第 4 位0000 1000 = 8接收语音的权限
* - 第 5 位0001 0000 = 16发送视频的权限
* - 第 6 位0010 0000 = 32接收视频的权限
* - 第 7 位0100 0000 = 64发送辅路也就是屏幕分享视频的权限
* - 第 8 位1000 0000 = 200接收辅路也就是屏幕分享视频的权限
* - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。
* - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。
*/
public function genPrivateMapKey( $userid, $expire, $roomid, $privilegeMap ) {
$userbuf = $this->__genUserBuf( $userid, $roomid, $expire, $privilegeMap, 0, '' );
return $this->__genSig( $userid, $expire, $userbuf, true );
}
/**
*【功能说明】
* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。
* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:
* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。
* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。
* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】=>【应用管理】=>【应用信息】中打开“启动权限密钥”开关。
*
*【参数说明】
* @param userid - 用户id限制长度为32字节只允许包含大小写英文字母a-zA-Z、数字0-9及下划线和连词符。
* @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。
* @param roomstr - 房间号,用于指定该 userid 可以进入的房间号
* @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:
* - 第 1 位0000 0001 = 1创建房间的权限
* - 第 2 位0000 0010 = 2加入房间的权限
* - 第 3 位0000 0100 = 4发送语音的权限
* - 第 4 位0000 1000 = 8接收语音的权限
* - 第 5 位0001 0000 = 16发送视频的权限
* - 第 6 位0010 0000 = 32接收视频的权限
* - 第 7 位0100 0000 = 64发送辅路也就是屏幕分享视频的权限
* - 第 8 位1000 0000 = 200接收辅路也就是屏幕分享视频的权限
* - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。
* - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。
*/
public function genPrivateMapKeyWithStringRoomID( $userid, $expire, $roomstr, $privilegeMap ) {
$userbuf = $this->__genUserBuf( $userid, 0, $expire, $privilegeMap, 0, $roomstr );
return $this->__genSig( $userid, $expire, $userbuf, true );
}
public function __construct( $sdkappid, $key ) {
$this->sdkappid = $sdkappid;
$this->key = $key;
}
/**
* 用于 url 的 base64 encode
* '+' => '*', '/' => '-', '=' => '_'
* @param string $string 需要编码的数据
* @return string 编码后的base64串失败返回false
* @throws \Exception
*/
private function base64_url_encode( $string ) {
static $replace = Array( '+' => '*', '/' => '-', '=' => '_' );
$base64 = base64_encode( $string );
if ( $base64 === false ) {
throw new \Exception( 'base64_encode error' );
}
return str_replace( array_keys( $replace ), array_values( $replace ), $base64 );
}
/**
* 用于 url 的 base64 decode
* '+' => '*', '/' => '-', '=' => '_'
* @param string $base64 需要解码的base64串
* @return string 解码后的数据失败返回false
* @throws \Exception
*/
private function base64_url_decode( $base64 ) {
static $replace = Array( '+' => '*', '/' => '-', '=' => '_' );
$string = str_replace( array_values( $replace ), array_keys( $replace ), $base64 );
$result = base64_decode( $string );
if ( $result == false ) {
throw new \Exception( 'base64_url_decode error' );
}
return $result;
}
/**
* TRTC业务进房权限加密串使用用户定义的userbuf
* @brief 生成 userbuf
* @param account 用户名
* @param dwSdkappid sdkappid
* @param dwAuthID 数字房间号
* @param dwExpTime 过期时间:该权限加密串的过期时间. 过期时间 = now+dwExpTime
* @param dwPrivilegeMap 用户权限255表示所有权限
* @param dwAccountType 用户类型, 默认为0
* @param roomStr 字符串房间号
* @return userbuf string 返回的userbuf
*/
private function __genUserBuf( $account, $dwAuthID, $dwExpTime, $dwPrivilegeMap, $dwAccountType,$roomStr ) {
//cVer unsigned char/1 版本号填0
if($roomStr == '')
$userbuf = pack( 'C1', '0' );
else
$userbuf = pack( 'C1', '1' );
$userbuf .= pack( 'n', strlen( $account ) );
//wAccountLen unsigned short /2 第三方自己的帐号长度
$userbuf .= pack( 'a'.strlen( $account ), $account );
//buffAccount wAccountLen 第三方自己的帐号字符
$userbuf .= pack( 'N', $this->sdkappid );
//dwSdkAppid unsigned int/4 sdkappid
$userbuf .= pack( 'N', $dwAuthID );
//dwAuthId unsigned int/4 群组号码/音视频房间号
$expire = $dwExpTime + time();
$userbuf .= pack( 'N', $expire );
//dwExpTime unsigned int/4 过期时间 (当前时间 + 有效期单位建议300秒
$userbuf .= pack( 'N', $dwPrivilegeMap );
//dwPrivilegeMap unsigned int/4 权限位
$userbuf .= pack( 'N', $dwAccountType );
//dwAccountType unsigned int/4
if($roomStr != '')
{
$userbuf .= pack( 'n', strlen( $roomStr ) );
//roomStrLen unsigned short /2 字符串房间号长度
$userbuf .= pack( 'a'.strlen( $roomStr ), $roomStr );
//roomStr roomStrLen 字符串房间号
}
return $userbuf;
}
/**
* 使用 hmac sha256 生成 sig 字段内容,经过 base64 编码
* @param $identifier 用户名utf-8 编码
* @param $curr_time 当前生成 sig 的 unix 时间戳
* @param $expire 有效期,单位秒
* @param $base64_userbuf base64 编码后的 userbuf
* @param $userbuf_enabled 是否开启 userbuf
* @return string base64 后的 sig
*/
private function hmacsha256( $identifier, $curr_time, $expire, $base64_userbuf, $userbuf_enabled ) {
$content_to_be_signed = 'TLS.identifier:' . $identifier . "\n"
. 'TLS.sdkappid:' . $this->sdkappid . "\n"
. 'TLS.time:' . $curr_time . "\n"
. 'TLS.expire:' . $expire . "\n";
if ( true == $userbuf_enabled ) {
$content_to_be_signed .= 'TLS.userbuf:' . $base64_userbuf . "\n";
}
return base64_encode( hash_hmac( 'sha256', $content_to_be_signed, $this->key, true ) );
}
/**
* 生成签名。
*
* @param $identifier 用户账号
* @param int $expire 过期时间,单位秒,默认 180 天
* @param $userbuf base64 编码后的 userbuf
* @param $userbuf_enabled 是否开启 userbuf
* @return string 签名字符串
* @throws \Exception
*/
private function __genSig( $identifier, $expire, $userbuf, $userbuf_enabled ) {
$curr_time = time();
$sig_array = Array(
'TLS.ver' => '2.0',
'TLS.identifier' => strval( $identifier ),
'TLS.sdkappid' => intval( $this->sdkappid ),
'TLS.expire' => intval( $expire ),
'TLS.time' => intval( $curr_time )
);
$base64_userbuf = '';
if ( true == $userbuf_enabled ) {
$base64_userbuf = base64_encode( $userbuf );
$sig_array['TLS.userbuf'] = strval( $base64_userbuf );
}
$sig_array['TLS.sig'] = $this->hmacsha256( $identifier, $curr_time, $expire, $base64_userbuf, $userbuf_enabled );
if ( $sig_array['TLS.sig'] === false ) {
throw new \Exception( 'base64_encode error' );
}
$json_str_sig = json_encode( $sig_array );
if ( $json_str_sig === false ) {
throw new \Exception( 'json_encode error' );
}
$compressed = gzcompress( $json_str_sig );
if ( $compressed === false ) {
throw new \Exception( 'gzcompress error' );
}
return $this->base64_url_encode( $compressed );
}
/**
* 验证签名。
*
* @param string $sig 签名内容
* @param string $identifier 需要验证用户名utf-8 编码
* @param int $init_time 返回的生成时间unix 时间戳
* @param int $expire_time 返回的有效期,单位秒
* @param string $userbuf 返回的用户数据
* @param string $error_msg 失败时的错误信息
* @return boolean 验证是否成功
* @throws \Exception
*/
private function __verifySig( $sig, $identifier, &$init_time, &$expire_time, &$userbuf, &$error_msg ) {
try {
$error_msg = '';
$compressed_sig = $this->base64_url_decode( $sig );
$pre_level = error_reporting( E_ERROR );
$uncompressed_sig = gzuncompress( $compressed_sig );
error_reporting( $pre_level );
if ( $uncompressed_sig === false ) {
throw new \Exception( 'gzuncompress error' );
}
$sig_doc = json_decode( $uncompressed_sig );
if ( $sig_doc == false ) {
throw new \Exception( 'json_decode error' );
}
$sig_doc = ( array )$sig_doc;
if ( $sig_doc['TLS.identifier'] !== $identifier ) {
throw new \Exception( "identifier dosen't match" );
}
if ( $sig_doc['TLS.sdkappid'] != $this->sdkappid ) {
throw new \Exception( "sdkappid dosen't match" );
}
$sig = $sig_doc['TLS.sig'];
if ( $sig == false ) {
throw new \Exception( 'sig field is missing' );
}
$init_time = $sig_doc['TLS.time'];
$expire_time = $sig_doc['TLS.expire'];
$curr_time = time();
if ( $curr_time > $init_time+$expire_time ) {
throw new \Exception( 'sig expired' );
}
$userbuf_enabled = false;
$base64_userbuf = '';
if ( isset( $sig_doc['TLS.userbuf'] ) ) {
$base64_userbuf = $sig_doc['TLS.userbuf'];
$userbuf = base64_decode( $base64_userbuf );
$userbuf_enabled = true;
}
$sigCalculated = $this->hmacsha256( $identifier, $init_time, $expire_time, $base64_userbuf, $userbuf_enabled );
if ( $sig != $sigCalculated ) {
throw new \Exception( 'verify failed' );
}
return true;
} catch ( \Exception $ex ) {
$error_msg = $ex->getMessage();
return false;
}
}
/**
* 带 userbuf 验证签名。
*
* @param string $sig 签名内容
* @param string $identifier 需要验证用户名utf-8 编码
* @param int $init_time 返回的生成时间unix 时间戳
* @param int $expire_time 返回的有效期,单位秒
* @param string $error_msg 失败时的错误信息
* @return boolean 验证是否成功
* @throws \Exception
*/
public function verifySig( $sig, $identifier, &$init_time, &$expire_time, &$error_msg ) {
$userbuf = '';
return $this->__verifySig( $sig, $identifier, $init_time, $expire_time, $userbuf, $error_msg );
}
/**
* 验证签名
* @param string $sig 签名内容
* @param string $identifier 需要验证用户名utf-8 编码
* @param int $init_time 返回的生成时间unix 时间戳
* @param int $expire_time 返回的有效期,单位秒
* @param string $userbuf 返回的用户数据
* @param string $error_msg 失败时的错误信息
* @return boolean 验证是否成功
* @throws \Exception
*/
public function verifySigWithUserBuf( $sig, $identifier, &$init_time, &$expire_time, &$userbuf, &$error_msg ) {
return $this->__verifySig( $sig, $identifier, $init_time, $expire_time, $userbuf, $error_msg );
}
}

438
extend/fast/Tree.php Normal file
View File

@@ -0,0 +1,438 @@
<?php
namespace fast;
use think\Config;
/**
* 通用的树型类
* @author XiaoYao <476552238li@gmail.com>
*/
class Tree
{
protected static $instance;
//默认配置
protected $config = [];
public $options = [];
/**
* 生成树型结构所需要的2维数组
* @var array
*/
public $arr = [];
/**
* 生成树型结构所需修饰符号,可以换成图片
* @var array
*/
public $icon = array('│', '├', '└');
public $nbsp = "&nbsp;";
public $pidname = 'pid';
public function __construct($options = [])
{
if ($config = Config::get('tree')) {
$this->options = array_merge($this->config, $config);
}
$this->options = array_merge($this->config, $options);
}
/**
* 初始化
* @access public
* @param array $options 参数
* @return Tree
*/
public static function instance($options = [])
{
if (is_null(self::$instance)) {
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 初始化方法
* @param array $arr 2维数组例如
* array(
* 1 => array('id'=>'1','pid'=>0,'name'=>'一级栏目一'),
* 2 => array('id'=>'2','pid'=>0,'name'=>'一级栏目二'),
* 3 => array('id'=>'3','pid'=>1,'name'=>'二级栏目一'),
* 4 => array('id'=>'4','pid'=>1,'name'=>'二级栏目二'),
* 5 => array('id'=>'5','pid'=>2,'name'=>'二级栏目三'),
* 6 => array('id'=>'6','pid'=>3,'name'=>'三级栏目一'),
* 7 => array('id'=>'7','pid'=>3,'name'=>'三级栏目二')
* )
* @param string $pidname 父字段名称
* @param string $nbsp 空格占位符
* @return Tree
*/
public function init($arr = [], $pidname = null, $nbsp = null)
{
$this->arr = $arr;
if (!is_null($pidname)) {
$this->pidname = $pidname;
}
if (!is_null($nbsp)) {
$this->nbsp = $nbsp;
}
return $this;
}
/**
* 得到子级数组
* @param int
* @return array
*/
public function getChild($myid)
{
$newarr = [];
foreach ($this->arr as $value) {
if (!isset($value['id'])) {
continue;
}
if ($value[$this->pidname] == $myid) {
$newarr[$value['id']] = $value;
}
}
return $newarr;
}
/**
* 读取指定节点的所有孩子节点
* @param int $myid 节点ID
* @param boolean $withself 是否包含自身
* @return array
*/
public function getChildren($myid, $withself = false)
{
$newarr = [];
foreach ($this->arr as $value) {
if (!isset($value['id'])) {
continue;
}
if ((string)$value[$this->pidname] == (string)$myid) {
$newarr[] = $value;
$newarr = array_merge($newarr, $this->getChildren($value['id']));
} elseif ($withself && (string)$value['id'] == (string)$myid) {
$newarr[] = $value;
}
}
return $newarr;
}
/**
* 读取指定节点的所有孩子节点ID
* @param int $myid 节点ID
* @param boolean $withself 是否包含自身
* @return array
*/
public function getChildrenIds($myid, $withself = false)
{
$childrenlist = $this->getChildren($myid, $withself);
$childrenids = [];
foreach ($childrenlist as $k => $v) {
$childrenids[] = $v['id'];
}
return $childrenids;
}
/**
* 得到当前位置父辈数组
* @param int
* @return array
*/
public function getParent($myid)
{
$pid = 0;
$newarr = [];
foreach ($this->arr as $value) {
if (!isset($value['id'])) {
continue;
}
if ($value['id'] == $myid) {
$pid = $value[$this->pidname];
break;
}
}
if ($pid) {
foreach ($this->arr as $value) {
if ($value['id'] == $pid) {
$newarr[] = $value;
break;
}
}
}
return $newarr;
}
/**
* 得到当前位置所有父辈数组
* @param int
* @param bool $withself 是否包含自己
* @return array
*/
public function getParents($myid, $withself = false)
{
$pid = 0;
$newarr = [];
foreach ($this->arr as $value) {
if (!isset($value['id'])) {
continue;
}
if ($value['id'] == $myid) {
if ($withself) {
$newarr[] = $value;
}
$pid = $value[$this->pidname];
break;
}
}
if ($pid) {
$arr = $this->getParents($pid, true);
$newarr = array_merge($arr, $newarr);
}
return $newarr;
}
/**
* 读取指定节点所有父类节点ID
* @param int $myid
* @param boolean $withself
* @return array
*/
public function getParentsIds($myid, $withself = false)
{
$parentlist = $this->getParents($myid, $withself);
$parentsids = [];
foreach ($parentlist as $k => $v) {
$parentsids[] = $v['id'];
}
return $parentsids;
}
/**
* 树型结构Option
* @param int $myid 表示获得这个ID下的所有子级
* @param string $itemtpl 条目模板 如:"<option value=@id @selected @disabled>@spacer@name</option>"
* @param mixed $selectedids 被选中的ID比如在做树型下拉框的时候需要用到
* @param mixed $disabledids 被禁用的ID比如在做树型下拉框的时候需要用到
* @param string $itemprefix 每一项前缀
* @param string $toptpl 顶级栏目的模板
* @return string
*/
public function getTree($myid, $itemtpl = "<option value=@id @selected @disabled>@spacer@name</option>", $selectedids = '', $disabledids = '', $itemprefix = '', $toptpl = '')
{
$ret = '';
$number = 1;
$childs = $this->getChild($myid);
if ($childs) {
$total = count($childs);
foreach ($childs as $value) {
$id = $value['id'];
$j = $k = '';
if ($number == $total) {
$j .= $this->icon[2];
$k = $itemprefix ? $this->nbsp : '';
} else {
$j .= $this->icon[1];
$k = $itemprefix ? $this->icon[0] : '';
}
$spacer = $itemprefix ? $itemprefix . $j : '';
$selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
$value = array_combine(array_map(function ($k) {
return '@' . $k;
}, array_keys($value)), $value);
$nstr = strtr((($value["@{$this->pidname}"] == 0 || $this->getChild($id)) && $toptpl ? $toptpl : $itemtpl), $value);
$ret .= $nstr;
$ret .= $this->getTree($id, $itemtpl, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp, $toptpl);
$number++;
}
}
return $ret;
}
/**
* 树型结构UL
* @param int $myid 表示获得这个ID下的所有子级
* @param string $itemtpl 条目模板 如:"<li value=@id @selected @disabled>@name @childlist</li>"
* @param string $selectedids 选中的ID
* @param string $disabledids 禁用的ID
* @param string $wraptag 子列表包裹标签
* @param string $wrapattr 子列表包裹属性
* @return string
*/
public function getTreeUl($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '')
{
$str = '';
$childs = $this->getChild($myid);
if ($childs) {
foreach ($childs as $value) {
$id = $value['id'];
unset($value['child']);
$selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
$value = array_combine(array_map(function ($k) {
return '@' . $k;
}, array_keys($value)), $value);
$nstr = strtr($itemtpl, $value);
$childdata = $this->getTreeUl($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr);
$childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
$str .= strtr($nstr, array('@childlist' => $childlist));
}
}
return $str;
}
/**
* 菜单数据
* @param int $myid
* @param string $itemtpl
* @param mixed $selectedids
* @param mixed $disabledids
* @param string $wraptag
* @param string $wrapattr
* @param int $deeplevel
* @return string
*/
public function getTreeMenu($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '', $deeplevel = 0)
{
$str = '';
$childs = $this->getChild($myid);
if ($childs) {
foreach ($childs as $value) {
$id = $value['id'];
unset($value['child']);
$selected = in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
$value = array_combine(array_map(function ($k) {
return '@' . $k;
}, array_keys($value)), $value);
$bakvalue = array_intersect_key($value, array_flip(['@url', '@caret', '@class']));
$value = array_diff_key($value, $bakvalue);
$nstr = strtr($itemtpl, $value);
$value = array_merge($value, $bakvalue);
$childdata = $this->getTreeMenu($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr, $deeplevel + 1);
$childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
$childlist = strtr($childlist, array('@class' => $childdata ? 'last' : ''));
$value = array(
'@childlist' => $childlist,
'@url' => $childdata || !isset($value['@url']) ? "javascript:;" : $value['@url'],
'@addtabs' => $childdata || !isset($value['@url']) ? "" : (stripos($value['@url'], "?") !== false ? "&" : "?") . "ref=addtabs",
'@caret' => ($childdata && (!isset($value['@badge']) || !$value['@badge']) ? '<i class="fa fa-angle-left"></i>' : ''),
'@badge' => $value['@badge'] ?? '',
'@class' => ($selected ? ' active' : '') . ($disabled ? ' disabled' : '') . ($childdata ? ' treeview' . (config('fastadmin.show_submenu') ? ' treeview-open' : '') : ''),
);
$str .= strtr($nstr, $value);
}
}
return $str;
}
/**
* 特殊
* @param integer $myid 要查询的ID
* @param string $itemtpl1 第一种HTML代码方式
* @param string $itemtpl2 第二种HTML代码方式
* @param mixed $selectedids 默认选中
* @param mixed $disabledids 禁用
* @param string $itemprefix 前缀
* @return string
*/
public function getTreeSpecial($myid, $itemtpl1, $itemtpl2, $selectedids = 0, $disabledids = 0, $itemprefix = '')
{
$ret = '';
$number = 1;
$childs = $this->getChild($myid);
if ($childs) {
$total = count($childs);
foreach ($childs as $id => $value) {
$j = $k = '';
if ($number == $total) {
$j .= $this->icon[2];
$k = $itemprefix ? $this->nbsp : '';
} else {
$j .= $this->icon[1];
$k = $itemprefix ? $this->icon[0] : '';
}
$spacer = $itemprefix ? $itemprefix . $j : '';
$selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
$value = array_combine(array_map(function ($k) {
return '@' . $k;
}, array_keys($value)), $value);
$nstr = strtr(!isset($value['@disabled']) || !$value['@disabled'] ? $itemtpl1 : $itemtpl2, $value);
$ret .= $nstr;
$ret .= $this->getTreeSpecial($id, $itemtpl1, $itemtpl2, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp);
$number++;
}
}
return $ret;
}
/**
*
* 获取树状数组
* @param string $myid 要查询的ID
* @param string $itemprefix 前缀
* @return array
*/
public function getTreeArray($myid, $itemprefix = '')
{
$childs = $this->getChild($myid);
$n = 0;
$data = [];
$number = 1;
if ($childs) {
$total = count($childs);
foreach ($childs as $id => $value) {
$j = $k = '';
if ($number == $total) {
$j .= $this->icon[2];
$k = $itemprefix ? $this->nbsp : '';
} else {
$j .= $this->icon[1];
$k = $itemprefix ? $this->icon[0] : '';
}
$spacer = $itemprefix ? $itemprefix . $j : '';
$value['spacer'] = $spacer;
$data[$n] = $value;
$data[$n]['childlist'] = $this->getTreeArray($id, $itemprefix . $k . $this->nbsp);
$n++;
$number++;
}
}
return $data;
}
/**
* 将getTreeArray的结果返回为二维数组
* @param array $data
* @param string $field
* @return array
*/
public function getTreeList($data = [], $field = 'name')
{
$arr = [];
foreach ($data as $k => $v) {
$childlist = $v['childlist'] ?? [];
unset($v['childlist']);
$v[$field] = $v['spacer'] . ' ' . $v[$field];
$v['haschild'] = $childlist ? 1 : 0;
if ($v['id']) {
$arr[] = $v;
}
if ($childlist) {
$arr = array_merge($arr, $this->getTreeList($childlist, $field));
}
}
return $arr;
}
}

79
extend/fast/Version.php Normal file
View File

@@ -0,0 +1,79 @@
<?php
namespace fast;
/**
* 版本检测和对比
*/
class Version
{
/**
* 检测版本是否的版本要求的数据中
*
* @param string $version
* @param array $data
* @return bool
*/
public static function check($version, $data = [])
{
//版本号以.分隔
$data = is_array($data) ? $data : [$data];
if ($data) {
if (in_array("*", $data) || in_array($version, $data)) {
return true;
}
$ver = explode('.', $version);
if ($ver) {
$versize = count($ver);
//验证允许的版本
foreach ($data as $m) {
$c = explode('.', $m);
if (!$c || $versize != count($c)) {
continue;
}
$i = 0;
foreach ($c as $a => $k) {
if (!self::compare($ver[$a], $k)) {
continue 2;
} else {
$i++;
}
}
if ($i == $versize) {
return true;
}
}
}
}
return false;
}
/**
* 比较两个版本号
*
* @param string $v1
* @param string $v2
* @return boolean
*/
public static function compare($v1, $v2)
{
if ($v2 == "*" || $v1 == $v2) {
return true;
} else {
$values = [];
$k = explode(',', $v2);
foreach ($k as $v) {
if (strpos($v, '-') !== false) {
list($start, $stop) = explode('-', $v);
for ($i = $start; $i <= $stop; $i++) {
$values[] = $i;
}
} else {
$values[] = $v;
}
}
return in_array($v1, $values) ? true : false;
}
}
}