代码功能更新
This commit is contained in:
Executable
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
/**
|
||||
* 常用函数
|
||||
* Class CommonFun
|
||||
* @package laytp\library
|
||||
*/
|
||||
class CommonFun
|
||||
{
|
||||
/**
|
||||
* 统一处理post数据
|
||||
* @param $post
|
||||
* @return mixed
|
||||
*/
|
||||
public static function filterPostData($post)
|
||||
{
|
||||
if (!$post) {
|
||||
return [];
|
||||
}
|
||||
//处理数组
|
||||
foreach ($post as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
$post[$k] = implode(',', $v);
|
||||
}
|
||||
}
|
||||
return $post;
|
||||
}
|
||||
}
|
||||
Executable
+358
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
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 $remote
|
||||
* @param null $local
|
||||
* @param null $now
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
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)
|
||||
{
|
||||
$timediff = (is_null($local) || $local ? time() : $local) - $remote;
|
||||
$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'],
|
||||
];
|
||||
|
||||
for ($i = 0, $j = count($chunks); $i < $j; $i++) {
|
||||
$seconds = $chunks[$i][0];
|
||||
$name = $chunks[$i][1];
|
||||
if (($count = floor($timediff / $seconds)) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return __("%d {$name}%s ago", $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, ['begin', 'start', 'first', 'front']);
|
||||
|
||||
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':
|
||||
$time = $position ?
|
||||
mktime(0, 0, 0, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 1 - 7 * (-$offset), $year) :
|
||||
mktime(23, 59, 59, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 7 - 7 * (-$offset), $year);
|
||||
break;
|
||||
case 'month':
|
||||
$time = $position ? mktime(0, 0, 0, $month + $offset, 1, $year) : mktime(23, 59, 59, $month + $offset, cal_days_in_month(CAL_GREGORIAN, $month + $offset, $year), $year);
|
||||
break;
|
||||
case 'quarter':
|
||||
$time = $position ?
|
||||
mktime(0, 0, 0, 1 + ((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) - 1) * 3, 1, $year) :
|
||||
mktime(23, 59, 59, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, cal_days_in_month(CAL_GREGORIAN, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $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 $date
|
||||
* @return false|int|string
|
||||
*/
|
||||
public static function getWeekNum($date='')
|
||||
{
|
||||
$date = $date ? $date : date('Y-m-d');
|
||||
|
||||
$year = date('Y', strtotime($date));
|
||||
|
||||
$yearBegin = strtotime($year.'-1-1');
|
||||
|
||||
$month = intval(date('m', strtotime($date)));
|
||||
|
||||
if( date('W', $yearBegin) == 1 ){
|
||||
return date('W', strtotime($date));
|
||||
}else if($month == 1 && date('W', $yearBegin) > 50){
|
||||
return 1;
|
||||
}else{
|
||||
return date('W', strtotime($date)) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据日期,获取周信息
|
||||
* @param $now string 日期,举例: 2021-01-01
|
||||
* @return array
|
||||
*/
|
||||
public static function getWeekInfo($now)
|
||||
{
|
||||
$str = [];
|
||||
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
|
||||
$str['year'] = date('Y', strtotime($now));
|
||||
$first = 1;
|
||||
//当日在整年中的第几周
|
||||
$str['week'] = date('W', strtotime($now));
|
||||
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
|
||||
$w = date('w', strtotime($now));
|
||||
//获取本周开始日期,如果$w是0,则表示周日,减去 6 天
|
||||
$weekStart = date('Y-m-d', strtotime("$now -" . ($w ? $w - $first : 6) . ' days'));
|
||||
$str['week_start'] = $weekStart;
|
||||
//本周结束日期
|
||||
$weekEnd = date('Y-m-d', strtotime("$weekStart +6 days"));
|
||||
$str['week_end'] = $weekEnd;
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 某去某年第几周的周信息
|
||||
* @param $year
|
||||
* @param int $week
|
||||
* @return mixed
|
||||
*/
|
||||
public static function weekDay($year, $week=1){
|
||||
$yearStart = mktime(0,0,0,1,1, $year);
|
||||
$yearEnd = mktime(0,0,0,12,31, $year);
|
||||
|
||||
$start = $yearStart;//把第一天做为第一周的开始
|
||||
$end = strtotime('+1 sunday', $yearStart);//把第一个周日作为第一周的结束
|
||||
|
||||
$lastStart = strtotime('-1 monday', $yearStart);//把最后一个周一作为最后一周的开始
|
||||
$lastEnd = $yearEnd;//把最后一天作为最后一周的结束
|
||||
|
||||
$totalWeekNum = intval(date('W', $yearStart));
|
||||
|
||||
if($week == 1){
|
||||
$weekday['begin'] = $start;//把第一天做为第一周的开始
|
||||
$weekday['begin_date'] = date('Y-m-d', $start);//把第一天做为第一周的开始
|
||||
$weekday['end'] = $end;//把第一个周日作为第一周的结束
|
||||
$weekday['end_date'] = date('Y-m-d', $end);//把第一个周日作为第一周的结束
|
||||
}else if($week == $totalWeekNum){
|
||||
$weekday['begin'] = $lastStart;//把最后一个周一作为最后一周的开始
|
||||
$weekday['begin_date'] = date('Y-m-d', $lastStart);//把第一天做为第一周的开始
|
||||
$weekday['end'] = $lastEnd;//把第一个周日作为第一周的结束
|
||||
$weekday['end_date'] = date('Y-m-d', $lastEnd);//把第一个周日作为第一周的结束
|
||||
}else if($week > 1 && $week < $totalWeekNum){
|
||||
$weekday['begin'] = strtotime('+' . ($week-1) . ' monday', $end);
|
||||
$weekday['begin_date'] = date('Y-m-d', $weekday['begin']);
|
||||
$weekday['end'] = strtotime('+' . ($week-1) . ' sunday', $end + 24 * 60 * 60);
|
||||
$weekday['end_date'] = date('Y-m-d', $weekday['end']);
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
|
||||
$weekday['week'] = $week;
|
||||
$weekday['totalWeekNum'] = $totalWeekNum;
|
||||
|
||||
return $weekday;
|
||||
}
|
||||
|
||||
/**
|
||||
* 友好的时间显示
|
||||
*
|
||||
* @param int $sTime 待显示的时间
|
||||
* @param string $type 类型. normal | mohu | full | ymd | other
|
||||
* @param string $alt 已失效
|
||||
* @return string
|
||||
*/
|
||||
public static function friendlyDate($sTime,$type = 'default',$alt = 'false') {
|
||||
//sTime=源时间,cTime=当前时间,dTime=时间差
|
||||
$cTime = time();
|
||||
$dTime = $cTime - $sTime;
|
||||
$dDay = intval(date("z",$cTime)) - intval(date("z",$sTime));
|
||||
//$dDay = intval($dTime/3600/24);
|
||||
$dYear = intval(date("Y",$cTime)) - intval(date("Y",$sTime));
|
||||
//normal:n秒前,n分钟前,n小时前,日期
|
||||
if($type=='normal'){
|
||||
if($dTime == 0){
|
||||
return '现在';
|
||||
}elseif( $dTime < 60 ){
|
||||
return $dTime."秒前";
|
||||
}elseif( $dTime < 3600 ){
|
||||
return intval($dTime/60)."分钟前";
|
||||
//今天的数据.年份相同.日期相同.
|
||||
}elseif( $dYear==0 && $dDay == 0 ){
|
||||
//return intval($dTime/3600)."小时前";
|
||||
return '今天'.date('H:i',$sTime);
|
||||
}elseif($dYear==0){
|
||||
return date("m月d日 H:i",$sTime);
|
||||
}else{
|
||||
return date("Y-m-d H:i",$sTime);
|
||||
}
|
||||
}elseif($type=='mohu'){
|
||||
if( $dTime < 60 ){
|
||||
return $dTime."秒前";
|
||||
}elseif( $dTime < 3600 ){
|
||||
return intval($dTime/60)."分钟前";
|
||||
}elseif( $dTime >= 3600 && $dDay == 0 ){
|
||||
return intval($dTime/3600)."小时前";
|
||||
}elseif( $dDay > 0 && $dDay<=7 ){
|
||||
return intval($dDay)."天前";
|
||||
}elseif( $dDay > 7 && $dDay <= 30 ){
|
||||
return intval($dDay/7) . '周前';
|
||||
}elseif( $dDay > 30 ){
|
||||
return intval($dDay/30) . '个月前';
|
||||
}
|
||||
//full: Y-m-d , H:i:s
|
||||
}elseif($type=='full'){
|
||||
return date("Y-m-d , H:i:s",$sTime);
|
||||
}elseif($type=='ymd'){
|
||||
return date("Y-m-d",$sTime);
|
||||
}else{
|
||||
if( $dTime < 60 ){
|
||||
return $dTime."秒前";
|
||||
}elseif( $dTime < 3600 ){
|
||||
return intval($dTime/60)."分钟前";
|
||||
}elseif( $dTime >= 3600 && $dDay == 0 ){
|
||||
return intval($dTime/3600)."小时前";
|
||||
}elseif($dYear==0){
|
||||
return date("Y-m-d H:i:s",$sTime);
|
||||
}else{
|
||||
return date("Y-m-d H:i:s",$sTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+171
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* 文件夹和文件处理类
|
||||
*/
|
||||
class DirFile
|
||||
{
|
||||
/**
|
||||
* 创建目录
|
||||
* @param $path
|
||||
* @param int $mode
|
||||
* @return bool
|
||||
*/
|
||||
public static function createDir($path, $mode = 0777)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
return true;
|
||||
} else {
|
||||
//如果目录不存在,则递归创建
|
||||
if (mkdir($path, $mode, true)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环遍历目录下的文件和文件夹并输出
|
||||
* @param $pathName
|
||||
* @param array $filterDir
|
||||
* @param array $filterFile 需要过滤的文件名,*表示所有文件不展示
|
||||
* @param array $selected 设置是否已经选中
|
||||
* @param array $output
|
||||
* @return array|null
|
||||
*/
|
||||
public static function recurDir($pathName, $filterDir = [], $filterFile = [], $selected = [], $output = ['id'=>'id', 'fullName' => 'fullName', 'baseName' => 'baseName'])
|
||||
{
|
||||
//将结果保存在result变量中
|
||||
$result = [];
|
||||
$temp = [];
|
||||
//判断传入的变量是否是目录
|
||||
if (!is_dir($pathName) || !is_readable($pathName)) {
|
||||
return null;
|
||||
}
|
||||
//取出目录中的文件和子目录名,使用scandir函数
|
||||
$allFiles = scandir($pathName);
|
||||
//遍历他们
|
||||
foreach ($allFiles as $fileName) {
|
||||
if (in_array($fileName, ['.', '..'])) {
|
||||
continue;
|
||||
}
|
||||
//路径加文件名
|
||||
if (mb_substr($pathName, -1, 1) == DS) {
|
||||
$fullName = $pathName . $fileName;
|
||||
} else {
|
||||
$fullName = $pathName . DS . $fileName;
|
||||
}
|
||||
$baseName = basename($fileName);
|
||||
//如果是目录的话就继续遍历这个目录
|
||||
if (is_dir($fullName)) {
|
||||
if (in_array($fullName, $filterDir)) {
|
||||
continue;
|
||||
}
|
||||
//将这个目录中的文件信息存入到数组中
|
||||
$res = [
|
||||
$output['baseName'] => $baseName,
|
||||
$output['fullName'] => $fullName,
|
||||
'type' => 'dir',
|
||||
'children' => self::recurDir($fullName, $filterDir, $filterFile, $selected, $output),
|
||||
];
|
||||
if (in_array($fullName, $selected)) {
|
||||
$res['state'] = ['selected' => true];
|
||||
} else {
|
||||
$res['state'] = ['selected' => false];
|
||||
}
|
||||
$apiDirName = Config::get('apidirname');
|
||||
if(isset($apiDirName[$fullName])){
|
||||
$res['name'] = $apiDirName[$fullName];
|
||||
}else{
|
||||
$res['name'] = '';
|
||||
}
|
||||
$res['id'] = $fullName;
|
||||
$result[] = $res;
|
||||
} else {
|
||||
if($filterFile != '*'){
|
||||
if (in_array($fullName, $filterFile)) {
|
||||
continue;
|
||||
}
|
||||
//如果是文件就先存入临时变量
|
||||
//将这个目录中的文件信息存入到数组中
|
||||
$tem = [$output['id'] => $fullName, $output['baseName'] => $baseName, $output['fullName'] => $fullName, 'type' => 'file', 'state' => ['selected' => false]];
|
||||
if (in_array($fullName, $selected)) {
|
||||
$tem['state'] = ['selected' => true];
|
||||
} else {
|
||||
$tem['state'] = ['selected' => false];
|
||||
}
|
||||
$temp[] = $tem;
|
||||
}
|
||||
}
|
||||
}
|
||||
//取出文件
|
||||
if ($temp) {
|
||||
foreach ($temp as $f) {
|
||||
$result[] = $f;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件夹
|
||||
* @param string $dirname 目录
|
||||
* @param bool $withself 是否删除自身
|
||||
* @return boolean
|
||||
*/
|
||||
public static function rmDirs($dirname, $withself = true)
|
||||
{
|
||||
if (!is_dir($dirname)) {
|
||||
return false;
|
||||
}
|
||||
$files = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dirname, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($files as $fileinfo) {
|
||||
$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
|
||||
$todo($fileinfo->getRealPath());
|
||||
}
|
||||
if ($withself) {
|
||||
@rmdir($dirname);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文件夹
|
||||
* @param string $source 源文件夹
|
||||
* @param string $dest 目标文件夹
|
||||
*/
|
||||
public static function copyDirs($source, $dest)
|
||||
{
|
||||
if (!is_dir($dest)) {
|
||||
mkdir($dest, 0755, true);
|
||||
}
|
||||
foreach (
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
) as $item
|
||||
) {
|
||||
if ($item->isDir()) {
|
||||
$sontDir = $dest . DS . $iterator->getSubPathName();
|
||||
if (!is_dir($sontDir)) {
|
||||
mkdir($sontDir, 0755, true);
|
||||
}
|
||||
} else {
|
||||
copy($item, $dest . DS . $iterator->getSubPathName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+191
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use think\facade\Config;
|
||||
|
||||
class Email
|
||||
{
|
||||
|
||||
/**
|
||||
* 单例对象
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* phpmailer对象
|
||||
*/
|
||||
protected $mail = [];
|
||||
|
||||
/**
|
||||
* 错误内容
|
||||
*/
|
||||
protected $_error = '';
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
public $options = [
|
||||
'charset' => 'utf-8', //编码格式
|
||||
'debug' => 0, //调式模式
|
||||
];
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param array $options 参数
|
||||
* @return Email
|
||||
*/
|
||||
public static function instance($options = [])
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new static($options);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if ($config = Config::get('laytp.email')) {
|
||||
$this->options = array_merge($this->options, $config);
|
||||
}
|
||||
$this->options = array_merge($this->options, $options);
|
||||
$securArr = [1 => 'tls', 2 => 'ssl'];
|
||||
|
||||
$this->mail = new PHPMailer(true);
|
||||
$this->mail->CharSet = $this->options['charset'];
|
||||
$this->mail->SMTPDebug = false;
|
||||
$this->mail->isSMTP();
|
||||
$this->mail->SMTPAuth = true;
|
||||
$this->mail->Host = $this->options['smtp_host'];
|
||||
$this->mail->Username = $this->options['smtp_user'];
|
||||
$this->mail->Password = $this->options['smtp_password'];
|
||||
$this->mail->SMTPSecure = isset($securArr[$this->options['verify_type']]) ? $securArr[$this->options['verify_type']] : '';
|
||||
$this->mail->Port = $this->options['smtp_port'];
|
||||
|
||||
//设置发件人
|
||||
$this->from($this->options['from'], $this->options['from_name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置邮件主题
|
||||
* @param string $subject
|
||||
* @return $this
|
||||
*/
|
||||
public function subject($subject)
|
||||
{
|
||||
$this->options['subject'] = $subject;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置发件人
|
||||
* @param string $email
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function from($email, $name = '')
|
||||
{
|
||||
$this->options['from'] = $email;
|
||||
$this->options['from_name'] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置收件人
|
||||
* @param string $email
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function to($email, $name = '')
|
||||
{
|
||||
$this->options['to'] = $email;
|
||||
$this->options['to_name'] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置邮件正文
|
||||
* @param string $body
|
||||
* @param boolean $ishtml
|
||||
* @return $this
|
||||
*/
|
||||
public function message($body, $ishtml = true)
|
||||
{
|
||||
$this->options['body'] = $body;
|
||||
$this->options['ishtml'] = $ishtml;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后产生的错误
|
||||
* @return string
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置错误
|
||||
* @param string $error 信息信息
|
||||
*/
|
||||
protected function setError($error)
|
||||
{
|
||||
$this->_error = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
* @return boolean
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
$result = false;
|
||||
switch ($this->options['send_type']) {
|
||||
case 'smtp':
|
||||
//使用phpmailer发送
|
||||
$this->mail->setFrom($this->options['from'], $this->options['from_name']);
|
||||
$this->mail->addAddress($this->options['to'], $this->options['to_name']);
|
||||
$this->mail->Subject = $this->options['subject'];
|
||||
if ($this->options['ishtml']) {
|
||||
$this->mail->msgHTML($this->options['body']);
|
||||
} else {
|
||||
$this->mail->Body = $this->options['body'];
|
||||
}
|
||||
try {
|
||||
$result = $this->mail->send();
|
||||
if ($result) {
|
||||
return $result;
|
||||
} else {
|
||||
$this->setError($this->mail->ErrorInfo);
|
||||
}
|
||||
} catch (\phpmailerException $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'mail':
|
||||
//使用mail方法发送邮件
|
||||
$headers = 'MIME-Version: 1.0' . "\r\n";
|
||||
$headers .= "Content-type: text/html; charset=" . $this->options['charset'] . "\r\n";
|
||||
$headers .= "To: {$this->options['to_name']} <{$this->options['to']}>\r\n"; //收件人
|
||||
$headers .= "From: {$this->options['from_name']} <{$this->options['from']}>\r\n"; //发件人
|
||||
$result = mail($this->options['to'], $this->options['subject'], $this->options['body'], $headers);
|
||||
$this->setError($result ? '' : error_get_last()['message']);
|
||||
break;
|
||||
default:
|
||||
//邮件功能已关闭
|
||||
$this->setError('邮件功能已关闭');
|
||||
break;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+206
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
/**
|
||||
* 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] = $query_string;
|
||||
}
|
||||
|
||||
$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] = 3;
|
||||
$defaults[CURLOPT_TIMEOUT] = 3;
|
||||
|
||||
// 方便外部设置CURLOPT_HTTPHEADER,做此修改
|
||||
if(isset($options['CURLOPT_HTTPHEADER']) && is_array($options['CURLOPT_HTTPHEADER'])){
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $options['CURLOPT_HTTPHEADER']);
|
||||
unset($options['CURLOPT_HTTPHEADER']);
|
||||
}
|
||||
// disable 100-continue
|
||||
// curl_setopt($ch, CURLOPT_HTTPHEADER, ['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'], isset($parts['port']) ? $parts['port'] : 80, $errno, $errstr, 3);
|
||||
if (!$fp) {
|
||||
return false;
|
||||
}
|
||||
//设置超时时间
|
||||
stream_set_timeout($fp, 3);
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取远程https的图片保存到本地
|
||||
* @param $imgUrl string 图片地址
|
||||
* @param $saveToPath string 本地保存路径
|
||||
*/
|
||||
public static function saveImageFromHttps($imgUrl, $saveToPath){
|
||||
$arrContextOptions = array(
|
||||
"ssl"=>array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
),
|
||||
);
|
||||
$file_contents = file_get_contents($imgUrl, false, stream_context_create($arrContextOptions));
|
||||
file_put_contents($saveToPath, $file_contents);
|
||||
}
|
||||
}
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
/**
|
||||
* Class Output
|
||||
*/
|
||||
class Output extends \think\console\Output
|
||||
{
|
||||
|
||||
protected $message = [];
|
||||
|
||||
public function __construct($driver = 'console')
|
||||
{
|
||||
parent::__construct($driver);
|
||||
}
|
||||
|
||||
protected function block($style, $message)
|
||||
{
|
||||
$this->message[] = $message;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+183
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
use think\exception\HttpException;
|
||||
use think\facade\Config;
|
||||
use think\facade\Middleware;
|
||||
use think\facade\Request;
|
||||
use think\Response;
|
||||
use think\Route;
|
||||
|
||||
class PluginRoute extends Route
|
||||
{
|
||||
protected $middleware;
|
||||
protected $actionName;
|
||||
|
||||
/**
|
||||
* 插件路由
|
||||
* 路由访问规则,http(s)://yourDomain/plugin/[插件名称]/[插件controller目录下的类名,多级目录以.号分割]/[方法名]/[参数列表]
|
||||
* @param null $plugin
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute($plugin = null)
|
||||
{
|
||||
$this->middleware = Middleware::instance();
|
||||
$this->request = Request::instance();
|
||||
$url = $this->request->url();
|
||||
$urlArr = array_filter(explode('/', $url));
|
||||
|
||||
$controller = isset($urlArr[3]) ? $urlArr[3] : 'Index';
|
||||
$plugin = $plugin ? trim(strtolower($plugin)) : $plugin;
|
||||
if (!defined('LT_PLUGIN')) {
|
||||
define('LT_PLUGIN', $plugin);
|
||||
}
|
||||
$controller = $controller ? str_replace('.', '\\', trim($controller)) : 'Index';
|
||||
$classAndAction = $this->getPluginClassAndAction($plugin, $controller);
|
||||
|
||||
$this->request->setController($controller)->setAction($classAndAction['action']);
|
||||
|
||||
$class = $classAndAction['class'];
|
||||
$action = $classAndAction['action'];
|
||||
$instance = app()->make($class, [], true);
|
||||
$this->actionName = $action;
|
||||
|
||||
try {
|
||||
$this->registerControllerMiddleware($instance);
|
||||
} catch (\ReflectionException $e) {
|
||||
throw new HttpException(500, $e->getMessage());
|
||||
}
|
||||
|
||||
return $this->middleware->pipeline('controller')
|
||||
->send($this->request)
|
||||
->then(function () use ($instance) {
|
||||
// 获取当前操作名
|
||||
$suffix = Config::get('route.action_suffix');
|
||||
$action = $this->actionName . $suffix;
|
||||
|
||||
if (is_callable([$instance, $action])) {
|
||||
$vars = $this->request->param();
|
||||
try {
|
||||
$reflect = new \ReflectionMethod($instance, $action);
|
||||
// 严格获取当前操作方法名
|
||||
$actionName = $reflect->getName();
|
||||
if ($suffix) {
|
||||
$actionName = substr($actionName, 0, -strlen($suffix));
|
||||
}
|
||||
|
||||
$this->request->setAction($actionName);
|
||||
} catch (\Exception $e) {
|
||||
$reflect = new \ReflectionMethod($instance, '__call');
|
||||
$vars = [$action, $vars];
|
||||
$this->request->setAction($action);
|
||||
}
|
||||
} else {
|
||||
// 操作不存在
|
||||
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
|
||||
}
|
||||
|
||||
$data = $this->app->invokeReflectMethod($instance, $reflect, $vars);
|
||||
|
||||
return $this->autoResponse($data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到插件完整类名
|
||||
* @param $plugin
|
||||
* @param $controller
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginClassAndAction($plugin, $controller)
|
||||
{
|
||||
$request = Request::instance();
|
||||
$url = $request->url();
|
||||
$urlArr = array_filter(explode('/', $url));
|
||||
|
||||
$controllerArr = explode("\\", $controller);
|
||||
$controllerArr[count($controllerArr) - 1] = ucfirst($controllerArr[count($controllerArr) - 1]);
|
||||
$controller = implode("\\", $controllerArr);
|
||||
|
||||
$class = 'plugin\\' . $plugin . '\\controller\\' . $controller;
|
||||
|
||||
if (!class_exists($class)) {
|
||||
throw new HttpException(404, $class . '类不存在');
|
||||
}
|
||||
|
||||
$class = 'plugin\\' . $plugin . '\\controller\\' . $controller;
|
||||
if (isset($urlArr[4])) {
|
||||
$action_param = explode('?', $urlArr[4]);
|
||||
$action = $action_param[0];
|
||||
} else {
|
||||
$action = 'index';
|
||||
}
|
||||
$action = $action ? str_replace('.' . Config::get("route.url_html_suffix"), '', trim($action)) : 'index';
|
||||
|
||||
if (!method_exists($class, $action)) {
|
||||
throw new HttpException(404, $class . '->' . $action . '()' . '方法不存在');
|
||||
}
|
||||
|
||||
return ['class' => $class, 'action' => $action];
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用反射机制注册控制器中间件
|
||||
* @param $controller
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function registerControllerMiddleware($controller): void
|
||||
{
|
||||
$class = new \ReflectionClass($controller);
|
||||
|
||||
if ($class->hasProperty('middleware')) {
|
||||
$reflectionProperty = $class->getProperty('middleware');
|
||||
$reflectionProperty->setAccessible(true);
|
||||
|
||||
$middlewares = $reflectionProperty->getValue($controller);
|
||||
|
||||
foreach ($middlewares as $key => $val) {
|
||||
if (!is_int($key)) {
|
||||
if (isset($val['only']) && !in_array($this->request->action(true), array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, is_string($val['only']) ? explode(",", $val['only']) : $val['only']))) {
|
||||
continue;
|
||||
} elseif (isset($val['except']) && in_array($this->request->action(true), array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, is_string($val['except']) ? explode(',', $val['except']) : $val['except']))) {
|
||||
continue;
|
||||
} else {
|
||||
$val = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($val) && strpos($val, ':')) {
|
||||
$val = explode(':', $val);
|
||||
if (count($val) > 1) {
|
||||
$val = [$val[0], array_slice($val, 1)];
|
||||
}
|
||||
}
|
||||
|
||||
$this->middleware->controller($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function autoResponse($data): Response
|
||||
{
|
||||
if ($data instanceof Response) {
|
||||
$response = $data;
|
||||
} elseif (!is_null($data)) {
|
||||
// 默认自动识别响应输出类型
|
||||
$type = $this->request->isJson() ? 'json' : 'html';
|
||||
$response = Response::create($data, $type);
|
||||
} else {
|
||||
$data = ob_get_clean();
|
||||
|
||||
$content = false === $data ? '' : $data;
|
||||
$status = '' === $content && $this->request->isJson() ? 204 : 200;
|
||||
$response = Response::create($content, 'html', $status);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
/**
|
||||
* 随机生成类
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字和字母组合的随机字符串
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数组元素的概率获得键名
|
||||
*
|
||||
* @param array $ps array('p1'=>20, 'p2'=>30, 'p3'=>50);
|
||||
* @param int $num 默认为1,即随机出来的数量
|
||||
* @param bool $unique 默认为true,即当num>1时,随机出的数量是否唯一
|
||||
* @return mixed 当num为1时返回键名,反之返回一维数组
|
||||
*/
|
||||
public static function lottery($ps, $num = 1, $unique = true)
|
||||
{
|
||||
if (!$ps) {
|
||||
return $num == 1 ? '' : [];
|
||||
}
|
||||
if ($num >= count($ps) && $unique) {
|
||||
$res = array_keys($ps);
|
||||
return $num == 1 ? $res[0] : $res;
|
||||
}
|
||||
$max_exp = 0;
|
||||
$res = [];
|
||||
foreach ($ps as $key => $value) {
|
||||
$value = substr($value, 0, stripos($value, ".") + 6);
|
||||
$exp = strlen(strchr($value, '.')) - 1;
|
||||
if ($exp > $max_exp) {
|
||||
$max_exp = $exp;
|
||||
}
|
||||
}
|
||||
$pow_exp = pow(10, $max_exp);
|
||||
if ($pow_exp > 1) {
|
||||
reset($ps);
|
||||
foreach ($ps as $key => $value) {
|
||||
$ps[$key] = $value * $pow_exp;
|
||||
}
|
||||
}
|
||||
$pro_sum = array_sum($ps);
|
||||
if ($pro_sum < 1) {
|
||||
return $num == 1 ? '' : [];
|
||||
}
|
||||
for ($i = 0; $i < $num; $i++) {
|
||||
$rand_num = mt_rand(1, $pro_sum);
|
||||
reset($ps);
|
||||
foreach ($ps as $key => $value) {
|
||||
if ($rand_num <= $value) {
|
||||
break;
|
||||
} else {
|
||||
$rand_num -= $value;
|
||||
}
|
||||
}
|
||||
if ($num == 1) {
|
||||
$res = $key;
|
||||
break;
|
||||
} else {
|
||||
$res[$i] = $key;
|
||||
}
|
||||
if ($unique) {
|
||||
$pro_sum -= $value;
|
||||
unset($ps[$key]);
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全球唯一标识
|
||||
* @return string
|
||||
*/
|
||||
public static function uuid()
|
||||
{
|
||||
$chars = md5(uniqid(mt_rand(), true));
|
||||
$uuid = substr ( $chars, 0, 8 ) . '-'
|
||||
. substr ( $chars, 8, 4 ) . '-'
|
||||
. substr ( $chars, 12, 4 ) . '-'
|
||||
. substr ( $chars, 16, 4 ) . '-'
|
||||
. substr ( $chars, 20, 12 );
|
||||
return $uuid ;
|
||||
}
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
use think\facade\Cache;
|
||||
|
||||
/**
|
||||
* Redis锁
|
||||
*/
|
||||
class Redis
|
||||
{
|
||||
/**
|
||||
* 得到一个redis锁,循环获取锁,直到获取到锁为止
|
||||
* @param $name string 锁名称
|
||||
* @param $ttl int 锁存在的时间,单位秒,默认60秒
|
||||
* @return bool
|
||||
*/
|
||||
public static function getLock($name, $ttl=60)
|
||||
{
|
||||
set_time_limit(0);
|
||||
$redis = Cache::store('redis')->handler();
|
||||
while(true){
|
||||
if($redis->set($name, 1, ['NX', 'EX'=>$ttl])){
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到一个redis锁,仅尝试获取一次
|
||||
*
|
||||
* @param $name string 锁名称
|
||||
* @param $ttl int 锁存在的时间,单位秒,默认60秒
|
||||
* @return bool
|
||||
*/
|
||||
public static function getOnceLock($name, $ttl=60)
|
||||
{
|
||||
set_time_limit(0);
|
||||
$redis = Cache::store('redis')->handler();
|
||||
if($redis->set($name, 1, ['NX', 'EX'=>$ttl])){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个redis锁
|
||||
* @param $name string 锁名称
|
||||
* @return bool
|
||||
*/
|
||||
public static function delLock($name)
|
||||
{
|
||||
$redis = Cache::store('redis')->handler();
|
||||
$redis->del($name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
/**
|
||||
* 字符串处理
|
||||
*/
|
||||
class Str
|
||||
{
|
||||
/**
|
||||
* 下划线转驼峰
|
||||
* @param $str
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
public static function underlineToCamel($str)
|
||||
{
|
||||
$str = preg_replace_callback('/([-_]+([a-z]{1}))/i', function ($matches) {
|
||||
return strtoupper($matches[2]);
|
||||
}, $str);
|
||||
return $str;
|
||||
}
|
||||
|
||||
// 生成密码
|
||||
public static function createPassword($password)
|
||||
{
|
||||
return password_hash(md5($password), PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
// 检验密码
|
||||
public static function checkPassword($password, $passwordHash)
|
||||
{
|
||||
return password_verify(md5($password), $passwordHash);
|
||||
}
|
||||
}
|
||||
Executable
+159
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
use laytp\library\token\Driver;
|
||||
use think\facade\Config;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* Token操作类
|
||||
*/
|
||||
class Token
|
||||
{
|
||||
/**
|
||||
* @var array Token的实例
|
||||
*/
|
||||
public static $instance = [];
|
||||
|
||||
/**
|
||||
* @var object 操作句柄
|
||||
*/
|
||||
public static $handler;
|
||||
|
||||
/**
|
||||
* 连接Token驱动
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @param bool|string $name Token连接标识 true 强制重新连接
|
||||
* @return Driver
|
||||
*/
|
||||
public static function connect(array $options = [], $name = false)
|
||||
{
|
||||
$type = !empty($options['type']) ? $options['type'] : 'File';
|
||||
|
||||
if (false === $name) {
|
||||
$name = md5(serialize($options));
|
||||
}
|
||||
|
||||
if (true === $name || !isset(self::$instance[$name])) {
|
||||
$class = false === strpos($type, '\\') ?
|
||||
'laytp\\library\\token\\driver\\' . ucwords($type) :
|
||||
$type;
|
||||
|
||||
// 记录初始化信息
|
||||
Config::get('app.app_debug') && Log::record('[ TOKEN ] INIT ' . $type, 'info');
|
||||
|
||||
if (true === $name) {
|
||||
return new $class($options);
|
||||
}
|
||||
|
||||
self::$instance[$name] = new $class($options);
|
||||
}
|
||||
|
||||
return self::$instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动初始化Token
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @return Driver
|
||||
*/
|
||||
public static function init(array $options = [])
|
||||
{
|
||||
if (is_null(self::$handler)) {
|
||||
if (empty($options) && 'complex' == Config::get('token.type')) {
|
||||
$default = Config::get('token.default');
|
||||
// 获取默认Token配置,并连接
|
||||
$options = Config::get('token.' . $default['type']) ?: $default;
|
||||
} elseif (empty($options)) {
|
||||
$options = Config::get('token');
|
||||
}
|
||||
self::$handler = self::connect($options);
|
||||
}
|
||||
|
||||
return self::$handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Token是否可用(check别名)
|
||||
* @access public
|
||||
* @param string $token Token标识
|
||||
* @param int $userId 用户ID
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($token, $userId)
|
||||
{
|
||||
return self::check($token, $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Token是否可用
|
||||
* @param string $token Token标识
|
||||
* @param int $userId 用户ID
|
||||
* @return bool
|
||||
*/
|
||||
public static function check($token, $userId)
|
||||
{
|
||||
return self::init()->check($token, $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取Token
|
||||
* @access public
|
||||
* @param string $token Token标识
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($token, $default = false)
|
||||
{
|
||||
return self::init()->get($token, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入Token
|
||||
* @access public
|
||||
* @param string $token Token标识
|
||||
* @param mixed $userId 存储数据
|
||||
* @param int|null $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
public static function set($token, $userId, $expire = null)
|
||||
{
|
||||
return self::init()->set($token, $userId, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Token(delete别名)
|
||||
* @access public
|
||||
* @param string $token Token标识
|
||||
* @return boolean
|
||||
*/
|
||||
public static function rm($token)
|
||||
{
|
||||
return self::delete($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Token
|
||||
* @param string $token 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete($token)
|
||||
{
|
||||
return self::init()->delete($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除Token
|
||||
* @access public
|
||||
* @param int $userId Token标记
|
||||
* @return boolean
|
||||
*/
|
||||
public static function clear($userId = null)
|
||||
{
|
||||
return self::init()->clear($userId);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+221
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
/**
|
||||
* 通用树型类 - laytp极速后台框架
|
||||
* @Author: JunStar
|
||||
* @Version: 1.0.0
|
||||
* @Date: 2022-1-7 21:48:38
|
||||
* @LastModified by: JunStar
|
||||
* @LastModified time: 2022-1-8 21:45:22
|
||||
*/
|
||||
class Tree
|
||||
{
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* 生成树型结构所需要的2维数组
|
||||
* @var array
|
||||
*/
|
||||
public $map = [];
|
||||
public $mapName = 'id';
|
||||
public $pidName = 'pid';
|
||||
public $childName = 'children';
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @return Tree
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new static();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化方法
|
||||
* 注意,参数一定要是数组,数据库select()->toArray();否则调用getRootTree会内存溢出
|
||||
* @param array 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'=>'三级栏目二')
|
||||
* )
|
||||
* @return $this
|
||||
*/
|
||||
public function init($arr = [])
|
||||
{
|
||||
$map = [];
|
||||
//生成以id为key的map
|
||||
foreach ($arr as &$it){
|
||||
$map[$it['id']] = &$it;
|
||||
}
|
||||
$this->map = $map;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有根树
|
||||
* @return array
|
||||
*/
|
||||
public function getRootTrees()
|
||||
{
|
||||
$map = $this->map;
|
||||
$res = [];
|
||||
$rootTree = [];
|
||||
foreach ($map as $id => &$item) {
|
||||
// 获取出每一条数据的父id
|
||||
$pid = &$item[$this->pidName];
|
||||
// 如果在map中没有设置过当前$item的pid索引, 说明$item是根节点
|
||||
if(!isset($map[$pid])){
|
||||
//将根节点的item的引用保存到$res中
|
||||
$res[] = &$item;
|
||||
}else{
|
||||
// 如果在map中有设置过当前$item的pid索引, 则将当前item加入到他父亲的叶子节点中
|
||||
// 此处关键需要理解内存地址引用,修改了$pItem,其实就是修改了$map[$pid]的值
|
||||
$pItem = &$map[$pid];
|
||||
$pItem[$this->childName][] = &$item;
|
||||
}
|
||||
}
|
||||
|
||||
// 循环res,将pid不为0的过滤掉,剩下的就是所有的根树
|
||||
if($res){
|
||||
foreach($res as $k=>$value){
|
||||
if($value[$this->pidName] == 0){
|
||||
$rootTree[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rootTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有树
|
||||
*/
|
||||
public function getTrees(){
|
||||
$map = $this->map;
|
||||
$res = [];
|
||||
foreach ($map as $id => &$item) {
|
||||
// 获取出每一条数据的父id
|
||||
$pid = &$item[$this->pidName];
|
||||
// 如果在map中没有设置过当前$item的pid索引, 说明$item是根节点
|
||||
if(!isset($map[$pid])){
|
||||
//将根节点的item的引用保存到$res中
|
||||
$res[] = &$item;
|
||||
}else{
|
||||
// 如果在map中有设置过当前$item的pid索引, 则将当前item加入到他父亲的叶子节点中
|
||||
// 此处关键需要理解内存地址引用,修改了$pItem,其实就是修改了$map[$pid]的值
|
||||
$pItem = &$map[$pid];
|
||||
$pItem[$this->childName][] = &$item;
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某棵子树
|
||||
* 思路:
|
||||
* 1.先获取当前节点的所有父级节点
|
||||
* 2.组合一个根树的map,每个key都是id值,getRootTree是自增的
|
||||
* 3.使用所有父级节点,直接在根树的map中索引得到子树
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取某ID的所有子级ID
|
||||
* @param $ids string|array 要查询子级的ID
|
||||
* @param bool $withSelf 返回的结果是否包含$ids
|
||||
* @return array
|
||||
*/
|
||||
public function getChildIds($ids, $withSelf=true){
|
||||
$map = $this->map;
|
||||
$res = [];//最终结果
|
||||
if(!is_array($ids)){
|
||||
$ids = explode(',', $ids);
|
||||
$sourceIds = $ids;
|
||||
}else{
|
||||
$sourceIds = $ids;
|
||||
}
|
||||
|
||||
$state = true;
|
||||
while($state){
|
||||
$otherIds = [];
|
||||
foreach ($ids as $id) {
|
||||
foreach ($map as $key => $value) {
|
||||
if($value[$this->pidName] == $id){
|
||||
$res[] = $value[$this->mapName];//找到我的下级立即添加到最终结果中
|
||||
$otherIds[] = $value[$this->mapName];//将我的下级id保存起来用来下轮循环他的下级
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$otherIds){
|
||||
$state = false;
|
||||
}else{
|
||||
$ids = $otherIds;//foreach中找到的我的下级集合,用来下次循环
|
||||
}
|
||||
}
|
||||
|
||||
if($withSelf){
|
||||
foreach($sourceIds as $sId){
|
||||
$res[] = intval($sId);
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某ID的所有父级ID
|
||||
* @param $ids string|array 要查询父级的ID
|
||||
* @param bool $withSelf 返回的结果是否包含$id
|
||||
* @return array
|
||||
*/
|
||||
public function getParentIds($ids, $withSelf=true){
|
||||
$map = $this->map;
|
||||
$res = [];//最终结果
|
||||
if(!is_array($ids)){
|
||||
$ids = explode(',', $ids);
|
||||
$sourceIds = $ids;
|
||||
}else{
|
||||
$sourceIds = $ids;
|
||||
}
|
||||
|
||||
$state = true;
|
||||
while($state){
|
||||
$otherIds = [];
|
||||
foreach ($ids as $id) {
|
||||
foreach ($map as $key => $value) {
|
||||
if($value[$this->mapName] == $map[$id][$this->pidName]){
|
||||
$res[] = $value[$this->mapName];//找到我的上级立即添加到最终结果中
|
||||
$otherIds[] = $value[$this->mapName];//将我的上级id保存起来用来下次轮循环他的下级
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$otherIds){
|
||||
$state = false;
|
||||
}else{
|
||||
$ids = $otherIds;//foreach中找到的我的上级集合,用来下次循环
|
||||
}
|
||||
}
|
||||
|
||||
if($withSelf){
|
||||
foreach($sourceIds as $sId){
|
||||
$res[] = intval($sId);
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
Executable
+261
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library;
|
||||
|
||||
use app\model\Files;
|
||||
use app\service\ConfServiceFacade;
|
||||
use laytp\traits\Error;
|
||||
use think\facade\Config;
|
||||
use think\facade\Env;
|
||||
|
||||
/**
|
||||
* 上传文件域名前缀处理类
|
||||
*/
|
||||
class UploadDomain
|
||||
{
|
||||
use Error;
|
||||
|
||||
/**
|
||||
* 检测上传的文件
|
||||
* @param $fileName
|
||||
* @param $fileSize
|
||||
* @param $fileExt
|
||||
* @param $fileMime
|
||||
* @return bool
|
||||
*/
|
||||
public function check($fileName, $fileSize, $fileExt, $fileMime)
|
||||
{
|
||||
$allowSize = request()->param('size', ConfServiceFacade::get('system.upload.size'));
|
||||
if (!$this->checkSize($fileSize, $allowSize)) {
|
||||
return false;
|
||||
}
|
||||
$allowExt = request()->param('mime', ConfServiceFacade::get('system.upload.mime'));
|
||||
if (!$this->checkExt($fileName, $fileExt, $allowExt)) {
|
||||
return false;
|
||||
}
|
||||
if (!$this->checkMime($fileMime)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件大小
|
||||
* @param $fileSize
|
||||
* @param string $allowSize
|
||||
* @return bool
|
||||
*/
|
||||
public function checkSize($fileSize, $allowSize = '')
|
||||
{
|
||||
$allowSize = str_replace(' ', '', $allowSize);
|
||||
$allowUploadSizeConf = ConfServiceFacade::get('system.upload.size');
|
||||
// 没有配置,也没有传入上传文件大小限制参数,即程序上不限制上传文件大小
|
||||
if (!$allowUploadSizeConf && !$allowSize) {
|
||||
return true;
|
||||
}
|
||||
$maxSize = $allowSize ? $allowSize : $allowUploadSizeConf;
|
||||
preg_match('/(\d+)(\w+)/', $maxSize, $matches);
|
||||
$type = strtolower($matches[2]);
|
||||
$typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
|
||||
$maxSize = (int)$maxSize * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0);
|
||||
if ($fileSize > $maxSize) {
|
||||
$this->setError('上传失败,文件大小超过 ' . $allowSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件后缀
|
||||
* @param $fileName
|
||||
* @param $fileExt
|
||||
* @param string $allowExt
|
||||
* @return bool
|
||||
*/
|
||||
public function checkExt($fileName, $fileExt, $allowExt = '')
|
||||
{
|
||||
$allowExtArr = explode(',', strtolower($allowExt));
|
||||
//禁止上传PHP和HTML文件
|
||||
if (in_array($fileExt, ['php', 'html', 'htm'])) {
|
||||
$this->setError('上传失败,禁止上传php和html文件');
|
||||
return false;
|
||||
}
|
||||
//验证文件后缀
|
||||
$allowExtConf = ConfServiceFacade::get('system.upload.mime');
|
||||
// 没有配置,也没有传入上传文件大小限制参数,即程序上不限制上传文件大小
|
||||
if (!$allowExtConf && !$allowExt) {
|
||||
$this->setError('上传失败,允许上传的文件后缀为空,请到系统配置 - 上传配置进行设置');
|
||||
return false;
|
||||
}
|
||||
if ($allowExt !== '*' && (!in_array($fileExt, $allowExtArr))) {
|
||||
$this->setError('上传失败,允许上传的文件后缀为' . $allowExt . ',实际上传文件[ ' . $fileName . ' ]的后缀为' . $fileExt);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件类型
|
||||
* @param $fileMime
|
||||
* @return bool
|
||||
*/
|
||||
public function checkMime($fileMime)
|
||||
{
|
||||
//禁止上传PHP和HTML文件
|
||||
if (in_array($fileMime, ['text/x-php', 'text/html'])) {
|
||||
$this->setError('上传失败,禁止上传php和html文件');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑器内容,添加上传文件的域名前缀,一般提供给数据模型层getFiledAttr方法调用
|
||||
* @param $string
|
||||
* @param $uploadType
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
static public function addUploadDomain($string, $uploadType = 'local')
|
||||
{
|
||||
$defaultType = ConfServiceFacade::get('system.upload.defaultType', 'local');
|
||||
if($uploadType == 'default') $uploadType = $defaultType;
|
||||
$uploadDomain = self::getUploadDomain($uploadType, 'via');
|
||||
//ueditor编辑器正则替换所有的图片、视频、音频
|
||||
/* $string = preg_replace("/(<img .*?src=\")^(?!http)(.*?)(\".*?>+)/is", "\${1}{$uploadDomain}\${2}\${3}", $string);*/
|
||||
preg_match_all("/(<img .*?src=\")(.*?)(\".*?>+)/is", $string, $matches);
|
||||
if ($matches && isset($matches['2'])) {
|
||||
foreach ($matches['2'] as $item) {
|
||||
if (substr($item, 0, 4) != 'http') {
|
||||
$string = str_replace($item, $uploadDomain . $item, $string);
|
||||
}
|
||||
}
|
||||
}
|
||||
$string = preg_replace("/(<video .*?src=\")(.*?)(\".*?>+)/is", "\${1}{$uploadDomain}\${2}\${3}", $string);
|
||||
$string = preg_replace("/(<source .*?src=\")(.*?)(\".*?>+)/is", "\${1}{$uploadDomain}\${2}\${3}", $string);
|
||||
|
||||
//meditor编辑器正则替换所有的图片
|
||||
$string = preg_replace("/(\!\[\]\()(.*?)(\))/is", "\${1}{$uploadDomain}\${2}\${3}", $string);
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑器内容,删除上传文件的域名前缀,一般提供给控制器添加和编辑方法调用
|
||||
* @param $string
|
||||
* @param $uploadType
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
static public function delUploadDomain($string, $uploadType = 'local')
|
||||
{
|
||||
$defaultType = ConfServiceFacade::get('system.upload.defaultType', 'local');
|
||||
if($uploadType == 'default') $uploadType = $defaultType;
|
||||
$uploadDomain = addcslashes(self::getUploadDomain($uploadType, 'via'), '/');
|
||||
//ueditor编辑器正则替换所有的图片、视频、音频
|
||||
$string = preg_replace("/(<img .*?src=\"){$uploadDomain}(.*?)(\".*?>+)/is", "\${1}\${2}\${3}", $string);
|
||||
$string = preg_replace("/(<video .*?src=\"){$uploadDomain}(.*?)(\".*?>+)/is", "\${1}\${2}\${3}", $string);
|
||||
$string = preg_replace("/(<source .*?src=\"){$uploadDomain}(.*?)(\".*?>+)/is", "\${1}\${2}\${3}", $string);
|
||||
|
||||
//meditor编辑器正则替换所有的图片
|
||||
$string = preg_replace("/(\!\[\]\(){$uploadDomain}(.*?)(\))/is", "\${1}\${2}\${3}", $string);
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件上传后访问的域名
|
||||
* @param string $uploadType 上传方式
|
||||
* @param string $viaServer 上传是否经过服务器
|
||||
* @return mixed|string
|
||||
*/
|
||||
static public function getUploadDomain($uploadType = 'local', $viaServer = 'via')
|
||||
{
|
||||
$uploadDomain = '';
|
||||
if ($uploadType === 'local') {
|
||||
$uploadDomain = Env::get('domain.static', request()->domain() . '/static');
|
||||
} else if ($uploadType === 'qiniu-kodo') {
|
||||
$uploadDomain = ConfServiceFacade::get('plugin.qiniu_kodo.domain');
|
||||
} else if ($uploadType === 'ali-oss') {
|
||||
if ($viaServer === 'via') {
|
||||
$uploadDomain = ConfServiceFacade::get('plugin.ali_oss.domain');
|
||||
} else {
|
||||
$uploadDomain = ConfServiceFacade::get('plugin.ali_oss_sts.domain');
|
||||
}
|
||||
}
|
||||
return $uploadDomain;
|
||||
}
|
||||
|
||||
// 设置上传方式为本地时的path,提供给当前类的singleAddUploadDomain方法调用
|
||||
static public function setLocalPath($data)
|
||||
{
|
||||
$uploadDomain = self::getUploadDomain($data['upload_type']);
|
||||
$uploadType = $data['upload_type'];
|
||||
$value = $data['path'];
|
||||
if($uploadType === 'local'){
|
||||
if($uploadDomain){
|
||||
$value = '/storage/' . $data['path'];
|
||||
}else{
|
||||
$value = '/static' . $value;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传控件,单个,添加域名前缀,提供给Files模型层getPathAttr方法调用
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
static public function singleAddUploadDomain($data)
|
||||
{
|
||||
$uploadDomain = self::getUploadDomain($data['upload_type'], $data['via_server']);
|
||||
$value = self::setLocalPath($data);
|
||||
return $uploadDomain . '/' . ltrim($value, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传控件,单个,保存path时,删除掉域名前缀,此方法仅FileController的add方法调用
|
||||
* @param $data
|
||||
* @return mixed
|
||||
*/
|
||||
static public function singleDelUploadDomain($data)
|
||||
{
|
||||
$uploadDomain = self::getUploadDomain($data['upload_type'], $data['via_server']);
|
||||
return str_replace($uploadDomain, '', $data['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传控件,多个,整理成前端需要的数据,以[, ]组合的字符串
|
||||
* @param $fileIds
|
||||
* @return array|boolean
|
||||
*/
|
||||
static public function multiJoin($fileIds)
|
||||
{
|
||||
try {
|
||||
$files = Files::where('id', 'in', $fileIds)->select()->toArray();
|
||||
$idArr = [];
|
||||
$pathArr = [];
|
||||
$filenameArr = [];
|
||||
foreach ($files as $v) {
|
||||
$idArr[] = $v['id'];
|
||||
$pathArr[] = $v['path'];
|
||||
$filenameArr[] = $v['name'];
|
||||
}
|
||||
return [
|
||||
'id' => $fileIds,
|
||||
'path' => join(', ', $pathArr),
|
||||
'filename' => join(', ', $filenameArr)
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认的用户头像
|
||||
* @return string
|
||||
*/
|
||||
public static function getDefaultAvatar()
|
||||
{
|
||||
$path = '/admin/images/avatar.jpg';
|
||||
$uploadDomain = Env::get('domain.static', request()->domain() . '/static');
|
||||
return $uploadDomain . $path;
|
||||
}
|
||||
}
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library\token;
|
||||
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* Token基础类
|
||||
*/
|
||||
abstract class Driver
|
||||
{
|
||||
protected $handler = null;
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* 存储Token
|
||||
* @param string $token Token
|
||||
* @param int $user_id 会员ID
|
||||
* @param int $expire 过期时长,0表示无限,单位秒
|
||||
* @return bool
|
||||
*/
|
||||
abstract function set($token, $user_id, $expire = 0);
|
||||
|
||||
/**
|
||||
* 获取Token内的信息
|
||||
* @param string $token
|
||||
* @return array
|
||||
*/
|
||||
abstract function get($token);
|
||||
|
||||
/**
|
||||
* 判断Token是否可用
|
||||
* @param string $token Token
|
||||
* @param int $user_id 会员ID
|
||||
* @return boolean
|
||||
*/
|
||||
abstract function check($token, $user_id);
|
||||
|
||||
/**
|
||||
* 删除Token
|
||||
* @param string $token
|
||||
* @return boolean
|
||||
*/
|
||||
abstract function delete($token);
|
||||
|
||||
/**
|
||||
* 删除指定用户的所有Token
|
||||
* @param int $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
abstract function clear($user_id);
|
||||
|
||||
/**
|
||||
* 返回句柄对象,可执行其它高级方法
|
||||
*
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function handler()
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密后的Token
|
||||
* @param string $token Token标识
|
||||
* @return string
|
||||
*/
|
||||
protected function getEncryptedToken($token)
|
||||
{
|
||||
$tokenConfig = Config::get('token');
|
||||
return hash_hmac($tokenConfig['hashalgo'], $token, $tokenConfig['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取过期剩余时长
|
||||
* @param $expiretime
|
||||
* @return float|int|mixed
|
||||
*/
|
||||
protected function getExpiredIn($expiretime)
|
||||
{
|
||||
return $expiretime ? max(0, $expiretime - time()) : 365 * 86400;
|
||||
}
|
||||
}
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library\token\driver;
|
||||
|
||||
use laytp\library\token\Driver;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* Token操作类
|
||||
*/
|
||||
class Mysql extends Driver
|
||||
{
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'table' => 'user_token',
|
||||
'expire' => 2592000,
|
||||
'connection' => [],
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 参数
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
if ($this->options['connection']) {
|
||||
$this->handler = Db::connect($this->options['connection'])->name($this->options['table']);
|
||||
} else {
|
||||
$this->handler = Db::name($this->options['table']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储Token
|
||||
* @param string $token Token
|
||||
* @param int $user_id 会员ID
|
||||
* @param int $expire 过期时长,0表示无限,单位秒
|
||||
* @return bool
|
||||
*/
|
||||
public function set($token, $user_id, $expire = null)
|
||||
{
|
||||
$expire_time = !is_null($expire) && $expire !== 0 ? time() + $expire : 0;
|
||||
$token = $this->getEncryptedToken($token);
|
||||
$this->handler->insert(['token' => $token, 'user_id' => $user_id, 'create_time' => time(), 'expire_time' => $expire_time]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token内的信息
|
||||
* @param string $token
|
||||
* @return array|null|\think\Model
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function get($token)
|
||||
{
|
||||
$data = $this->handler->where('token', $this->getEncryptedToken($token))->find();
|
||||
if ($data) {
|
||||
if (!$data['expire_time'] || $data['expire_time'] > time()) {
|
||||
//返回未加密的token给客户端使用
|
||||
$data['token'] = $token;
|
||||
//返回剩余有效时间
|
||||
$data['expires_in'] = $this->getExpiredIn($data['expire_time']);
|
||||
return $data;
|
||||
} else {
|
||||
self::delete($token);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Token是否可用
|
||||
* @param string $token Token
|
||||
* @param int $user_id 会员ID
|
||||
* @return boolean
|
||||
*/
|
||||
public function check($token, $user_id)
|
||||
{
|
||||
$data = $this->get($token);
|
||||
return $data && $data['user_id'] == $user_id ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Token
|
||||
* @param string $token
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete($token)
|
||||
{
|
||||
$this->handler->where('token', $this->getEncryptedToken($token))->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定用户的所有Token
|
||||
* @param int $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($user_id)
|
||||
{
|
||||
$this->handler->where('user_id', $user_id)->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+168
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace laytp\library\token\driver;
|
||||
|
||||
use laytp\library\token\Driver;
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* Token操作类
|
||||
*/
|
||||
class Redis extends Driver
|
||||
{
|
||||
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'password' => '',
|
||||
'select' => 0,
|
||||
'timeout' => 0,
|
||||
'expire' => 0,
|
||||
'persistent' => false,
|
||||
'userprefix' => 'up:',
|
||||
'tokenprefix' => 'tp:',
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('redis')) {
|
||||
throw new \BadFunctionCallException('not support: redis');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$this->handler = new \Redis;
|
||||
if ($this->options['persistent']) {
|
||||
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
|
||||
} else {
|
||||
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
|
||||
}
|
||||
|
||||
if ('' != $this->options['password']) {
|
||||
$this->handler->auth($this->options['password']);
|
||||
}
|
||||
|
||||
if (0 != $this->options['select']) {
|
||||
$this->handler->select($this->options['select']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密后的Token
|
||||
* @param string $token Token标识
|
||||
* @return string
|
||||
*/
|
||||
protected function getEncryptedToken($token)
|
||||
{
|
||||
$config = Config::get('token');
|
||||
return $this->options['tokenprefix'] . hash_hmac($config['hashalgo'], $token, $config['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员的key
|
||||
* @param $user_id
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserKey($user_id)
|
||||
{
|
||||
return $this->options['userprefix'] . $user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储Token
|
||||
* @param string $token Token
|
||||
* @param int $user_id 会员ID
|
||||
* @param int $expire 过期时长,0表示无限,单位秒
|
||||
* @return bool
|
||||
*/
|
||||
public function set($token, $user_id, $expire = 0)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
$key = $this->getEncryptedToken($token);
|
||||
if ($expire) {
|
||||
$result = $this->handler->setex($key, $expire, $user_id);
|
||||
} else {
|
||||
$result = $this->handler->set($key, $user_id);
|
||||
}
|
||||
//写入会员关联的token
|
||||
$this->handler->sAdd($this->getUserKey($user_id), $key);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token内的信息
|
||||
* @param string $token
|
||||
* @return array
|
||||
*/
|
||||
public function get($token)
|
||||
{
|
||||
$key = $this->getEncryptedToken($token);
|
||||
$value = $this->handler->get($key);
|
||||
if (is_null($value) || false === $value) {
|
||||
return [];
|
||||
}
|
||||
//获取有效期
|
||||
$expire = $this->handler->ttl($key);
|
||||
$expire = $expire < 0 ? 365 * 86400 : $expire;
|
||||
$expire_time = time() + $expire;
|
||||
//解决使用redis方式储存token时api接口Token刷新与检测因expires_in拼写错误报错的BUG
|
||||
$result = ['token' => $token, 'user_id' => $value, 'expire_time' => $expire_time, 'expires_in' => $expire];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Token是否可用
|
||||
* @param string $token Token
|
||||
* @param int $user_id 会员ID
|
||||
* @return boolean
|
||||
*/
|
||||
public function check($token, $user_id)
|
||||
{
|
||||
$data = self::get($token);
|
||||
return $data && $data['user_id'] == $user_id ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Token
|
||||
* @param string $token
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete($token)
|
||||
{
|
||||
$data = $this->get($token);
|
||||
if ($data) {
|
||||
$key = $this->getEncryptedToken($token);
|
||||
$user_id = $data['user_id'];
|
||||
$this->handler->del($key);
|
||||
$this->handler->sRem($this->getUserKey($user_id), $key);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定用户的所有Token
|
||||
* @param int $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($user_id)
|
||||
{
|
||||
$keys = $this->handler->sMembers($this->getUserKey($user_id));
|
||||
$this->handler->del($this->getUserKey($user_id));
|
||||
$this->handler->del($keys);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user