first commit
This commit is contained in:
179
extend/SensitiveHelper/HashMap.php
Normal file
179
extend/SensitiveHelper/HashMap.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
/**
|
||||
* php构建哈希表类.
|
||||
* User: Lustre
|
||||
* Date: 17/3/9
|
||||
* Time: 上午9:10
|
||||
**/
|
||||
|
||||
namespace SensitiveHelper;
|
||||
|
||||
class HashMap
|
||||
{
|
||||
/**
|
||||
* 哈希表变量
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $hashTable = array();
|
||||
|
||||
public function __construct()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 向HashMap中添加一个键值对
|
||||
*
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function put($key, $value)
|
||||
{
|
||||
if (!array_key_exists($key, $this->hashTable)) {
|
||||
$this->hashTable[$key] = $value;
|
||||
return null;
|
||||
}
|
||||
$_temp = $this->hashTable[$key];
|
||||
$this->hashTable[$key] = $value;
|
||||
return $_temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取对应的value
|
||||
*
|
||||
* @param $key
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->hashTable)) {
|
||||
return $this->hashTable[$key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定key的键值对
|
||||
*
|
||||
* @param $key
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$temp_table = array();
|
||||
if (array_key_exists($key, $this->hashTable)) {
|
||||
$tempValue = $this->hashTable[$key];
|
||||
while ($curValue = current($this->hashTable)) {
|
||||
if (!(key($this->hashTable) == $key)) {
|
||||
$temp_table[key($this->hashTable)] = $curValue;
|
||||
}
|
||||
next($this->hashTable);
|
||||
}
|
||||
$this->hashTable = null;
|
||||
$this->hashTable = $temp_table;
|
||||
return $tempValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HashMap的所有键值
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keys()
|
||||
{
|
||||
return array_keys($this->hashTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HashMap的所有value值
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function values()
|
||||
{
|
||||
return array_values($this->hashTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个HashMap的值全部put到当前HashMap中
|
||||
*
|
||||
* @param \DfaFilter\HashMap $map
|
||||
*/
|
||||
public function putAll($map)
|
||||
{
|
||||
if (!$map->isEmpty() && $map->size() > 0) {
|
||||
$keys = $map->keys();
|
||||
foreach ($keys as $key) {
|
||||
$this->put($key, $map->get($key));
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除HashMap中所有元素
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeAll()
|
||||
{
|
||||
$this->hashTable = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断HashMap中是否包含指定的值
|
||||
*
|
||||
* @param $value
|
||||
* @return bool
|
||||
*/
|
||||
public function containsValue($value)
|
||||
{
|
||||
while ($curValue = current($this->hashTable)) {
|
||||
if ($curValue == $value) {
|
||||
return true;
|
||||
}
|
||||
next($this->hashTable);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断HashMap中是否包含指定的键key
|
||||
*
|
||||
* @param $key
|
||||
* @return bool
|
||||
*/
|
||||
public function containsKey($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->hashTable)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HashMap中元素个数
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function size()
|
||||
{
|
||||
return count($this->hashTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断HashMap是否为空
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return (count($this->hashTable) == 0);
|
||||
}
|
||||
}
|
||||
336
extend/SensitiveHelper/SensitiveHelper.php
Normal file
336
extend/SensitiveHelper/SensitiveHelper.php
Normal file
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
/**
|
||||
* 敏感词类库.
|
||||
* User: Lustre
|
||||
* Date: 17/3/9
|
||||
* Time: 上午9:11
|
||||
*/
|
||||
namespace SensitiveHelper;
|
||||
use app\common\controller\common\model\system\Tags;
|
||||
|
||||
class SensitiveHelper
|
||||
{
|
||||
/**
|
||||
* 待检测语句长度
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $contentLength = 0;
|
||||
|
||||
/**
|
||||
* 敏感词单例
|
||||
*
|
||||
* @var object|null
|
||||
*/
|
||||
private static $_instance = null;
|
||||
|
||||
/**
|
||||
* 敏感词库树
|
||||
*
|
||||
* @var HashMap|null
|
||||
*/
|
||||
protected $wordTree = null;
|
||||
|
||||
/**
|
||||
* 存放待检测语句敏感词
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected static $badWordList = null;
|
||||
|
||||
/**
|
||||
* 获取单例
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
if (!self::$_instance instanceof self) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建敏感词树【文件模式】
|
||||
* @param string $filepath
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setTreeByFile($filepath = null)
|
||||
{
|
||||
if (!file_exists($filepath)) {
|
||||
throw new \Exception('没有词库');
|
||||
}
|
||||
|
||||
// 词库树初始化
|
||||
$this->wordTree = $this->wordTree ?: new HashMap();
|
||||
foreach ($this->yieldToReadFile($filepath) as $word) {
|
||||
$this->buildWordToTree(trim($word));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建敏感词树【数组模式】
|
||||
* @param null $sensitiveWords
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setTree($sensitiveWords = null, bool $type = true)
|
||||
{
|
||||
// 默认从数据库读取
|
||||
if (empty($sensitiveWords)) {
|
||||
$sensitiveWords = Tags::where([
|
||||
'type'=> $type,
|
||||
'status'=> true,
|
||||
])->column('name');
|
||||
}
|
||||
|
||||
$this->wordTree = new HashMap();
|
||||
foreach ($sensitiveWords as $word) {
|
||||
$this->buildWordToTree($word);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测文字中的敏感词
|
||||
*
|
||||
* @param string $content 待检测内容
|
||||
* @param int $matchType 匹配类型 [默认为最小匹配规则]
|
||||
* @param int $wordNum 需要获取的敏感词数量 [默认获取全部]
|
||||
* @return array
|
||||
*/
|
||||
public function getBadWord($content, $matchType = 1, $wordNum = 0)
|
||||
{
|
||||
$this->contentLength = mb_strlen($content, 'utf-8');
|
||||
$badWordList = array();
|
||||
for ($length = 0; $length < $this->contentLength; $length++) {
|
||||
$matchFlag = 0;
|
||||
$flag = false;
|
||||
$tempMap = $this->wordTree;
|
||||
for ($i = $length; $i < $this->contentLength; $i++) {
|
||||
|
||||
$keyChar = mb_substr($content, $i, 1, 'utf-8');
|
||||
|
||||
// 获取指定节点树
|
||||
$nowMap = $tempMap->get($keyChar);
|
||||
|
||||
// 不存在节点树,直接返回
|
||||
if (empty($nowMap)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 存在,则判断是否为最后一个
|
||||
$tempMap = $nowMap;
|
||||
|
||||
// 找到相应key,偏移量+1
|
||||
$matchFlag++;
|
||||
|
||||
// 如果为最后一个匹配规则,结束循环,返回匹配标识数
|
||||
if (false === $nowMap->get('ending')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$flag = true;
|
||||
|
||||
// 最小规则,直接退出
|
||||
if (1 === $matchType) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$flag) {
|
||||
$matchFlag = 0;
|
||||
}
|
||||
|
||||
// 找到相应key
|
||||
if ($matchFlag <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$badWordList[] = mb_substr($content, $length, $matchFlag, 'utf-8');
|
||||
|
||||
// 有返回数量限制
|
||||
if ($wordNum > 0 && count($badWordList) == $wordNum) {
|
||||
return $badWordList;
|
||||
}
|
||||
|
||||
// 需匹配内容标志位往后移
|
||||
$length = $length + $matchFlag - 1;
|
||||
}
|
||||
|
||||
return $badWordList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换敏感字字符
|
||||
*
|
||||
* @param $content 文本内容
|
||||
* @param string $replaceChar 替换字符
|
||||
* @param bool $repeat true=>重复替换为敏感词相同长度的字符
|
||||
* @param int $matchType
|
||||
* @return mixed
|
||||
*/
|
||||
public function replace($content, $replaceChar = '', $repeat = false, $matchType = 1)
|
||||
{
|
||||
if (empty($content)) {
|
||||
throw new \Exception('请填写检测的内容');
|
||||
}
|
||||
|
||||
$badWordList = self::$badWordList ? self::$badWordList : $this->getBadWord($content, $matchType);
|
||||
|
||||
// 未检测到敏感词,直接返回
|
||||
if (empty($badWordList)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ($badWordList as $badWord) {
|
||||
$hasReplacedChar = $replaceChar;
|
||||
if ($repeat) {
|
||||
$hasReplacedChar = $this->dfaBadWordConversChars($badWord, $replaceChar);
|
||||
}
|
||||
$content = str_replace($badWord, $hasReplacedChar, $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记敏感词
|
||||
* @param $content 文本内容
|
||||
* @param string $sTag 标签开头,如<mark>
|
||||
* @param string $eTag 标签结束,如</mark>
|
||||
* @param int $matchType
|
||||
* @return mixed
|
||||
*/
|
||||
public function mark($content, $sTag, $eTag, $matchType = 1)
|
||||
{
|
||||
if (empty($content)) {
|
||||
throw new \Exception('请填写检测的内容');
|
||||
}
|
||||
|
||||
$badWordList = self::$badWordList ? self::$badWordList : $this->getBadWord($content, $matchType);
|
||||
|
||||
// 未检测到敏感词,直接返回
|
||||
if (empty($badWordList)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ($badWordList as $badWord) {
|
||||
$replaceChar = $sTag . $badWord . $eTag;
|
||||
$content = str_replace($badWord, $replaceChar, $content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 被检测内容是否合法
|
||||
* @param $content
|
||||
* @return bool
|
||||
*/
|
||||
public function islegal($content)
|
||||
{
|
||||
$this->contentLength = mb_strlen($content, 'utf-8');
|
||||
for ($length = 0; $length < $this->contentLength; $length++) {
|
||||
|
||||
$matchFlag = 0;
|
||||
$tempMap = $this->wordTree;
|
||||
for ($i = $length; $i < $this->contentLength; $i++) {
|
||||
|
||||
$keyChar = mb_substr($content, $i, 1, 'utf-8');
|
||||
|
||||
// 获取指定节点树
|
||||
$nowMap = $tempMap->get($keyChar);
|
||||
|
||||
// 不存在节点树,直接返回
|
||||
if (empty($nowMap)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 找到相应key,偏移量+1
|
||||
$tempMap = $nowMap;
|
||||
$matchFlag++;
|
||||
|
||||
// 如果为最后一个匹配规则,结束循环,返回匹配标识数
|
||||
if (false === $nowMap->get('ending')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 找到相应key
|
||||
if ($matchFlag <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 需匹配内容标志位往后移
|
||||
$length = $length + $matchFlag - 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function yieldToReadFile($filepath)
|
||||
{
|
||||
$fp = fopen($filepath, 'r');
|
||||
while (!feof($fp)) {
|
||||
yield fgets($fp);
|
||||
}
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
// 将单个敏感词构建成树结构
|
||||
protected function buildWordToTree($word = '')
|
||||
{
|
||||
if ('' === $word) {
|
||||
return;
|
||||
}
|
||||
$tree = $this->wordTree;
|
||||
$wordLength = mb_strlen($word, 'utf-8');
|
||||
for ($i = 0; $i < $wordLength; $i++) {
|
||||
$keyChar = mb_substr($word, $i, 1, 'utf-8');
|
||||
|
||||
// 获取子节点树结构
|
||||
$tempTree = $tree->get($keyChar);
|
||||
|
||||
if ($tempTree) {
|
||||
$tree = $tempTree;
|
||||
} else {
|
||||
// 设置标志位
|
||||
$newTree = new HashMap();
|
||||
$newTree->put('ending', false);
|
||||
|
||||
// 添加到集合
|
||||
$tree->put($keyChar, $newTree);
|
||||
$tree = $newTree;
|
||||
}
|
||||
// 到达最后一个节点
|
||||
if ($i == $wordLength - 1) {
|
||||
$tree->put('ending', true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 敏感词替换为对应长度的字符
|
||||
* @param $word
|
||||
* @param $char
|
||||
* @return string
|
||||
*/
|
||||
protected function dfaBadWordConversChars($word, $char)
|
||||
{
|
||||
$str = '';
|
||||
$length = mb_strlen($word, 'utf-8');
|
||||
for ($counter = 0; $counter < $length; ++$counter) {
|
||||
$str .= $char;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
32
extend/WordAnalysis/Analysis.php
Normal file
32
extend/WordAnalysis/Analysis.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace WordAnalysis;
|
||||
|
||||
class Analysis
|
||||
{
|
||||
/**
|
||||
* Notes:关键字提取
|
||||
* @auther: xxf
|
||||
* Date: 2019/8/19
|
||||
* Time: 11:09
|
||||
* @param string $content
|
||||
* @param int $num 获取数量
|
||||
* @return string
|
||||
*/
|
||||
public static function getKeywords(string $content = null,int $num = 2) {
|
||||
|
||||
if (empty ($content )) {
|
||||
return '';
|
||||
}
|
||||
|
||||
require_once 'phpanalysis.class.php';
|
||||
\PhpAnalysis::$loadInit = false;
|
||||
$pa = new \PhpAnalysis ( 'utf-8', 'utf-8', false );
|
||||
$pa->LoadDict();
|
||||
$pa->SetSource($content);
|
||||
$pa->StartAnalysis(true);
|
||||
return $pa->GetFinallyKeywords($num);
|
||||
}
|
||||
|
||||
}
|
||||
BIN
extend/WordAnalysis/dict/base_dic_full.dic
Normal file
BIN
extend/WordAnalysis/dict/base_dic_full.dic
Normal file
Binary file not shown.
25
extend/WordAnalysis/dict/readme.txt
Normal file
25
extend/WordAnalysis/dict/readme.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
文件说明:
|
||||
|
||||
1、base_dic_full.dic
|
||||
hash索引 -- 字典带有词频和词性标志。
|
||||
|
||||
2、words_addons.dic
|
||||
s 开头的表示停止词 u 后缀词(地名后缀、数学单位等) n 前导词(姓、汉字数词等) a 后导词(地区,部门等)
|
||||
|
||||
3、 not-build/base_dic_full.txt
|
||||
没编译过的词典源码
|
||||
|
||||
4、重新编译词典的方法:
|
||||
|
||||
<?php
|
||||
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
|
||||
require_once('phpanalysis.class.php');
|
||||
|
||||
$pa = new PhpAnalysis('utf-8', 'utf-8', false);
|
||||
$pa->MakeDict( sourcefile, 16 , 'dict/base_dic_full.dic');
|
||||
|
||||
echo "OK";
|
||||
|
||||
?>
|
||||
17
extend/WordAnalysis/dict/words_addons.dic
Normal file
17
extend/WordAnalysis/dict/words_addons.dic
Normal file
@@ -0,0 +1,17 @@
|
||||
s:停止词
|
||||
并,让,才,上,被,把,近,而,是,为,由,等,合,子,除,均,很,也,称,还,分,据,后,向,经,对,但,只,则,设,靠,至,到,将,及,与,或,来,了,从,说,就,的,和,在,方,以,已,有,都,给,要
|
||||
|
||||
n:姓或其它专用前缀词
|
||||
新,肖,胡,罗,程,施,满,石,秦,苏,范,包,袁,许,舒,薛,蒋,董,白,田,季,丁,汪,段,梁,林,杜,杨,毛,江,熊,王,潘,沈,汤,谢,谭,韩,顾,雷,陈,阎,陆,马,高,龙,龚,黎,黄,魏,钱,钟,赵,邓,赖,贾,贺,邱,邵,郭,金,郝,郑,邹,李,武,余,夏,唐,朱,何,姚,孟,孙,孔,姜,周,吴,卢,单,刘,冯,史,叶,吕,候,傅,宋,任,文,戴,徐,张,万,方,曾,曹,易,廖,彭,常,尹,乔,于,康,崔,布,钟离,令狐,公冶,公孙,闻人,鲜于,上官,仲孙,万俟,东方,闾丘,长孙,诸葛,申屠,皇甫,尉迟,濮阳,澹台,欧阳,慕容,淳于,宗政,宇文,司徒,轩辕,单于,赫连,司空,太叔,夏侯,司马,公羊,勿,成吉,埃,哈
|
||||
|
||||
u:单位或专用后缀词
|
||||
u‰,℃,℉,毛,段,步,毫,池,滴,派,洲,款,次,桩,档,桌,桶,梯,楼,棵,炮,点,盏,盆,界,盒,盘,眼,画,男,环,版,片,班,瓣,生,瓶,案,格,族,方,斤,日,时,期,月,曲,斗,文,指,拳,拨,掌,排,丈,撮,本,朵,栋,柜,柄,栏,株,根,样,架,枪,条,束,村,杯,枝,枚,石,码,辈,辆,轮,连,通,里,部,遍,转,车,言,角,袋,课,起,路,趟,重,针,项,顷,顶,顿,颗,首,餐,页,集,锅,钱,钟,门,间,隅,队,行,节,筐,笔,筒,箱,篮,篓,篇,章,站,磅,碟,碗,种,科,窝,秒,簇,米,脚,股,群,船,艇,色,艘,罐,级,粒,类,组,维,缸,缕,招,支,发,双,厘,口,句,台,只,厅,卷,包,勺,匙,匹,升,区,叶,号,地,圈,圆,场,块,堆,坪,团,回,吨,名,拍,员,周,副,剑,代,付,件,伏,份,人,亩,世,下,两,个,串,伙,位,划,分,列,则,剂,刻,刀,出,倍,例,元,克,册,具,声,听,幅,帧,房,批,师,岁,尾,尺,局,层,届,手,壶,成,张,截,户,扇,年,度,座,尊,幢,室,寸,头,宗,字,孔,所,女,套,拉,家,处,折,天,把,夜,担,號,个月,公斤,公分,公克,公担,公亩,公升,公尺,像素,月份,盎司,位数,公里,年级,点钟,克拉,英亩,平方,加仑,公顷,秒钟,千克,世纪,千米,分钟,海里,英寸,英尺,英里,年代,周年,小时,阶段,平米,立方米,立方码,平方米,平方码,平方厘米,立方英寸,立方厘米,立方分米,立方公尺,立方英尺,平方公尺,平方英尺,平方英寸,平方分米,平方公里,平方英里,百位,十位,百次,千次,千名,千亩,千里,千人,千台,千位,万次,万元,万里,万位,万件,万单,万个,万台,万名,万人,亿元,亿,万,千,萬
|
||||
|
||||
a:地名等后置词
|
||||
语,署,苑,街,省,湖,乡,海,观,路,娃,山,阁,部,镇,江,河,厅,郡,厂,楼,园,区,党,井,亭,塔,县,家,市,弄,巷,寺,局,中路,村委,诺夫,斯基,维奇,村委会,机,型,率
|
||||
|
||||
c:数量前缀词
|
||||
零,一,二,三,四,五,六,七,八,九,十,百,千,万,亿,第,半,几,俩,卅,两,壹,贰,叁,肆,伍,陆,柒,捌,玖,拾,伯,仟
|
||||
|
||||
t:省会等专用词
|
||||
京,津,沪,渝,冀,豫,云,辽,黑,湘,皖,鲁,新,苏,浙,赣,鄂,桂,甘,晋,蒙,陕,吉,闽,贵,粤,青,藏,川,宁,琼
|
||||
1125
extend/WordAnalysis/phpanalysis.class.php
Normal file
1125
extend/WordAnalysis/phpanalysis.class.php
Normal file
File diff suppressed because it is too large
Load Diff
4
extend/conf/.htaccess
Normal file
4
extend/conf/.htaccess
Normal file
@@ -0,0 +1,4 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteCond % !^$
|
||||
RewriteRule ^.*\.(php|php3|php4|php5|php7|pht|phtml|asp|aspx|jsp|exe) - [F]
|
||||
</IfModule>
|
||||
BIN
extend/conf/ip/ip2region.db
Normal file
BIN
extend/conf/ip/ip2region.db
Normal file
Binary file not shown.
9
extend/conf/maps/360.html
Normal file
9
extend/conf/maps/360.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex>
|
||||
<volist name="list_map" id="vo">
|
||||
<sitemap>
|
||||
<loc>{$vo.url}</loc>
|
||||
<lastmod>{$vo.createtime}</lastmod>
|
||||
</sitemap>
|
||||
</volist>
|
||||
</sitemapindex>
|
||||
11
extend/conf/maps/baidu.html
Normal file
11
extend/conf/maps/baidu.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset>
|
||||
<volist name="listmap" id="vo">
|
||||
<php>echo '<'.'url'.'>';</php>
|
||||
<loc>{$vo.url}</loc>
|
||||
<lastmod>{$vo.createtime}</lastmod>
|
||||
<changefreq>always</changefreq>
|
||||
<priority>1.0</priority>
|
||||
<php>echo '</'.'url'.'>';</php>
|
||||
</volist>
|
||||
</urlset>
|
||||
17
extend/conf/maps/google.html
Normal file
17
extend/conf/maps/google.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>{$site_http}</loc>
|
||||
<lastmod>{:date('Y-m-d',time())}</lastmod>
|
||||
<changefreq>hourly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<volist name="list_map" id="vo">
|
||||
<url>
|
||||
<loc>{$vo.url}</loc>
|
||||
<lastmod>{$vo.createtime}</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
</volist>
|
||||
</urlset>
|
||||
52
extend/conf/sms/sms.php
Normal file
52
extend/conf/sms/sms.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
return array(
|
||||
/*
|
||||
* 短信模板配置
|
||||
*/
|
||||
'alisms' => array(
|
||||
'register' => array(
|
||||
'name' => '用户注册',
|
||||
'auto' => true,
|
||||
'template' => 'SMS_206595526'
|
||||
),
|
||||
'forgot' => array(
|
||||
'name' => '找回密码',
|
||||
'auto' => true,
|
||||
'template' => 'SMS_206854581'
|
||||
),
|
||||
'change' => array(
|
||||
'name' => '修改信息',
|
||||
'auto' => true,
|
||||
'template' => 'SMS_206595526'
|
||||
),
|
||||
'notice' => array(
|
||||
'name' => '消息通知',
|
||||
'auto' => false,
|
||||
'template' => 'SMS_206854581'
|
||||
)
|
||||
),
|
||||
'tensms' => array(
|
||||
'register' => array(
|
||||
'name' => '用户注册',
|
||||
'auto' => true,
|
||||
'template' => '1361749'
|
||||
),
|
||||
'forgot' => array(
|
||||
'name' => '找回密码',
|
||||
'auto' => true,
|
||||
'template' => '1361742'
|
||||
),
|
||||
'change' => array(
|
||||
'name' => '修改信息',
|
||||
'auto' => true,
|
||||
'template' => '1401023'
|
||||
),
|
||||
'notice' => array(
|
||||
'name' => '消息通知',
|
||||
'auto' => false,
|
||||
'template' => '901315'
|
||||
)
|
||||
),
|
||||
|
||||
// TODO...
|
||||
);
|
||||
25
extend/conf/tpl/captcha.tpl
Normal file
25
extend/conf/tpl/captcha.tpl
Normal file
@@ -0,0 +1,25 @@
|
||||
<div style="background-color:#ECECEC; padding: 35px;">
|
||||
<table cellpadding="0" align="center" style="width: 80%; margin: 0px auto; text-align: left; position: relative; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: rgb(153, 153, 153) 0px 0px 5px; border-collapse: collapse; background-position: initial initial; background-repeat: initial initial;background:#fff;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th valign="middle" style="height: 25px; line-height: 25px; padding: 15px 35px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #C46200; background-color: #e01b3c; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px;">
|
||||
<font face="微软雅黑" size="5" style="color: rgb(255, 255, 255); ">{site_name}</font></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="padding:25px 35px 40px; background-color:#fff;">
|
||||
<h2 style="margin: 5px 0px; ">
|
||||
<font color="#333333" style="line-height: 20px; ">
|
||||
<font style="line-height: 22px; " size="4">亲爱的用户:</font></font>
|
||||
</h2>
|
||||
<p style="font-size:14px;">
|
||||
验证码为 {code}, 10分钟有效<br/>
|
||||
如果不是您自己的操作,请忽略!
|
||||
</p><br/>
|
||||
<p align="right" style="font-size:14px;">{site_name} 官方团队</p>
|
||||
<p align="right" style="font-size:14px;">{time}</p></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
41
extend/conf/tpl/close.tpl
Normal file
41
extend/conf/tpl/close.tpl
Normal file
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>您访问的站点已被关闭</title>
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no" />
|
||||
<style>
|
||||
*{ padding:0; margin:0; box-sizing: border-box;font-family: "微软雅黑"; }
|
||||
body,html{ width:100%; height:100%; }
|
||||
body {background: #f2f2f2;font-family: -apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;}
|
||||
img{ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
|
||||
.container{ max-width:90%; margin:10% auto; padding:80px 0px; }
|
||||
.bg{ display:block; max-width:100%; margin:0px auto; text-align: center;}
|
||||
.btn{ width:400px; margin:0 auto; max-width:100%; margin-top:40px; }
|
||||
.btn a{ float:left; text-decoration: none; width:46.5%; border:1px solid #5298ff; background:#5298ff; color:#FFF; display:block; height:46px; line-height:44px; text-align:center; font-size:16px; border-radius:3px; overflow: hidden; }
|
||||
.btn .goindex{ margin-right:7%; }
|
||||
.btn .lx{ border: 1px solid #d8d8d8; background: #ffffff; color: #8c8c8c; }
|
||||
.btn .lx:hover{ border:1px solid #5298ff; background:#5298ff; color:#FFF; }
|
||||
.font {font-size: 50px;font-weight: 600;margin-bottom: 80px;}
|
||||
@media screen and (max-width: 500px) {
|
||||
.btn{ width:85%; }
|
||||
.btn a{ width:100%; font-size:15px; height:42px; line-height:42px; }
|
||||
.btn .goindex{ margin-right:0; margin-bottom:20px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="bg">
|
||||
<div class="font">网站维护中</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
<div class="btn">
|
||||
<a href="//www.swiftadmin.net" class="goindex" target="_blank">开发者</a>
|
||||
<a href="mailto:#email" title="发送邮件" class="lx">联系站长</a>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
26
extend/conf/tpl/forgot.tpl
Normal file
26
extend/conf/tpl/forgot.tpl
Normal file
@@ -0,0 +1,26 @@
|
||||
<div style="background-color:#ECECEC; padding: 35px;">
|
||||
<table cellpadding="0" align="center" style="width: 80%; margin: 0px auto; text-align: left; position: relative; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: rgb(153, 153, 153) 0px 0px 5px; border-collapse: collapse; background-position: initial initial; background-repeat: initial initial;background:#fff;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th valign="middle" style="height: 25px; line-height: 25px; padding: 15px 35px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #C46200; background-color: #e01b3c; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px;">
|
||||
<font face="微软雅黑" size="5" style="color: rgb(255, 255, 255); ">{site_name}</font></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="padding:25px 35px 40px; background-color:#fff;">
|
||||
<h2 style="margin: 5px 0px; ">
|
||||
<font color="#333333" style="line-height: 20px; ">
|
||||
<font style="line-height: 22px; " size="4">亲爱的用户:</font></font>
|
||||
</h2>
|
||||
<p style="font-size:14px;">
|
||||
|
||||
找回密码链接请点击:{url} 10分钟有效<br/>
|
||||
如果您有什么疑问可以联系管理员,Email: {email}。
|
||||
</p><br/>
|
||||
<p align="right" style="font-size:14px;">{site_name} 官方团队</p>
|
||||
<p align="right" style="font-size:14px;">{time}</p></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
26
extend/conf/tpl/register.tpl
Normal file
26
extend/conf/tpl/register.tpl
Normal file
@@ -0,0 +1,26 @@
|
||||
<div style="background-color:#ECECEC; padding: 35px;">
|
||||
<table cellpadding="0" align="center" style="width: 80%; margin: 0px auto; text-align: left; position: relative; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: rgb(153, 153, 153) 0px 0px 5px; border-collapse: collapse; background-position: initial initial; background-repeat: initial initial;background:#fff;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th valign="middle" style="height: 25px; line-height: 25px; padding: 15px 35px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #C46200; background-color: #e01b3c; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px;">
|
||||
<font face="微软雅黑" size="5" style="color: rgb(255, 255, 255); ">{site_name}</font></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="padding:25px 35px 40px; background-color:#fff;">
|
||||
<h2 style="margin: 5px 0px; ">
|
||||
<font color="#333333" style="line-height: 20px; ">
|
||||
<font style="line-height: 22px; " size="5">亲爱的 {username}:</font></font>
|
||||
</h2>
|
||||
<p style="font-size:14px;">
|
||||
感谢您加入{site_name}! 请您在发表言论时,遵守当地法律法规。<br/>
|
||||
请点击链接激活:{url} 10分钟有效<br/>
|
||||
如果您有什么疑问可以联系管理员,Email: {email}。
|
||||
</p><br/>
|
||||
<p align="right" style="font-size:14px;">{site_name} 官方团队</p>
|
||||
<p align="right" style="font-size:14px;">{time}</p></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
12
extend/conf/tpl/tpl.php
Normal file
12
extend/conf/tpl/tpl.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
return array (
|
||||
'1'=>array(
|
||||
'name'=>'用户注册',
|
||||
'path'=>'extend/conf/tpl/register.tpl'
|
||||
),
|
||||
'2'=>array(
|
||||
'name'=>'找回密码',
|
||||
'path'=>'extend/conf/tpl/forgot.tpl'
|
||||
),
|
||||
// TODO...
|
||||
);
|
||||
1
extend/conf/version.txt
Normal file
1
extend/conf/version.txt
Normal file
@@ -0,0 +1 @@
|
||||
swiftadmin dev
|
||||
151
extend/system/File.php
Normal file
151
extend/system/File.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace system;
|
||||
|
||||
/**
|
||||
* 文件操作类
|
||||
* @author meystack
|
||||
*/
|
||||
class File
|
||||
{
|
||||
|
||||
/**
|
||||
* 递归创建文件夹
|
||||
* @param string $dirs
|
||||
*/
|
||||
public static function mkDirs(string $dirs)
|
||||
{
|
||||
if (!is_dir($dirs)) {
|
||||
self::mkDirs(dirname($dirs));
|
||||
mkdir($dirs, 0755);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除文件夹
|
||||
* @param string $dirs
|
||||
* @return mixed
|
||||
*/
|
||||
public static function rmDirs(string $dirs)
|
||||
{
|
||||
if (!is_dir($dirs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$files = scandir($dirs);
|
||||
foreach ($files as $file) {
|
||||
if ($file == '.' || $file == '..') {
|
||||
continue;
|
||||
}
|
||||
if (is_dir($dirs . '/' . $file)) {
|
||||
self::rmDirs($dirs . '/' . $file);
|
||||
} else {
|
||||
unlink($dirs . '/' . $file);
|
||||
}
|
||||
}
|
||||
rmdir($dirs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前文件夹大小
|
||||
* @param string $dirs
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getDirSize(string $dirs)
|
||||
{
|
||||
$handle = opendir($dirs);
|
||||
$size = 0;
|
||||
while (false !== ($FolderOrFile = readdir($handle))) {
|
||||
if ($FolderOrFile != "." && $FolderOrFile != "..") {
|
||||
if (is_dir("$dirs/$FolderOrFile")) {
|
||||
$size += self::getDirSize("$dirs/$FolderOrFile");
|
||||
} else {
|
||||
$size += filesize("$dirs/$FolderOrFile");
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
return $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件夹文件列表
|
||||
* @param string $dirs
|
||||
* @return array
|
||||
*/
|
||||
public static function getDirFile(string $dirs): array
|
||||
{
|
||||
$handle = opendir($dirs);
|
||||
$file = [];
|
||||
while (false !== ($FolderOrFile = readdir($handle))) {
|
||||
if ($FolderOrFile != "." && $FolderOrFile != "..") {
|
||||
if (is_dir("$dirs/$FolderOrFile")) {
|
||||
$file[] = self::getDirFile("$dirs/$FolderOrFile");
|
||||
} else {
|
||||
$file[] = "$dirs/$FolderOrFile";
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 [app, public] 的路径
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public static function getCopyDirs(string $name): array
|
||||
{
|
||||
return [
|
||||
plugin_path($name) . 'app',
|
||||
plugin_path($name) . 'public'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件比较
|
||||
* @param $source
|
||||
* @param $destFileOrPath
|
||||
* @param string $prefix
|
||||
* @param bool $onlyFiles
|
||||
* @return mixed
|
||||
*/
|
||||
public static function mutexCompare($source, $destFileOrPath, string $prefix = '', bool $onlyFiles = false): array
|
||||
{
|
||||
$list = [];
|
||||
$destFileOrPath = $destFileOrPath ?: root_path();
|
||||
if (!is_array($source) && is_file($source) && is_file($destFileOrPath)) {
|
||||
return md5_file($source) !== md5_file($destFileOrPath);
|
||||
}
|
||||
|
||||
foreach ($source as $filesPath) {
|
||||
if (is_dir($filesPath)) {
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($filesPath, \FilesystemIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file->isFile()) {
|
||||
$filePath = $file->getPathname();
|
||||
$appPath = str_replace($prefix, '', $filePath);
|
||||
$destPath = $destFileOrPath . $appPath;
|
||||
if ($onlyFiles) {
|
||||
if (is_file($destPath)) {
|
||||
if (md5_file($filePath) != md5_file($destPath)) {
|
||||
$list[] = $appPath;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$list[] = $appPath;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
659
extend/system/Form.php
Normal file
659
extend/system/Form.php
Normal file
@@ -0,0 +1,659 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// +----------------------------------------------------------------------
|
||||
// | swiftAdmin 极速开发框架 [基于WebMan开发]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2020-2030 http://www.swiftadmin.net
|
||||
// +----------------------------------------------------------------------
|
||||
// | swiftAdmin.net High Speed Development Framework
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: meystack <coolsec@foxmail.com> Apache2 License
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace system;
|
||||
use think\Facade;
|
||||
|
||||
/**
|
||||
* 表单生成器
|
||||
* SAPHP框架专用
|
||||
*/
|
||||
|
||||
class Form extends Facade
|
||||
{
|
||||
protected static function getFacadeClass()
|
||||
{
|
||||
return 'system\FormBuilder';
|
||||
}
|
||||
}
|
||||
|
||||
class FormBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* Item宽度
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $width = 100;
|
||||
|
||||
/**
|
||||
* 标签宽度
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $labelwidth = 110;
|
||||
|
||||
/**
|
||||
* 公用属性
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $attrs = [
|
||||
'type',
|
||||
'name',
|
||||
'min',
|
||||
'max',
|
||||
'maxlength',
|
||||
'required',
|
||||
'readonly',
|
||||
'disabled',
|
||||
'placeholder',
|
||||
];
|
||||
|
||||
public $replace = [];
|
||||
|
||||
/**
|
||||
* @var object 对象实例
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $formtype = true;
|
||||
|
||||
/**
|
||||
* 类构造函数
|
||||
* class constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param array $options 参数
|
||||
* @return object|FormBuilder|null
|
||||
*/
|
||||
public static function instance(array $options = [])
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new static($options);
|
||||
}
|
||||
|
||||
// 返回实例
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始生成元素
|
||||
*
|
||||
* @param array $data
|
||||
* @param bool $formType
|
||||
* @return string
|
||||
*/
|
||||
public function itemElem(array $data = [], bool $formType = true): string
|
||||
{
|
||||
|
||||
$this->formtype = $formType;
|
||||
|
||||
if ($data['tag'] == 'tab') {
|
||||
return $this->tab($data);
|
||||
}
|
||||
|
||||
if ($data['tag'] == 'grid') {
|
||||
return $this->grid($data);
|
||||
}
|
||||
|
||||
$itemHtml = '<div class="layui-form-item" ';
|
||||
|
||||
if (isset($data['width']) && $data['width']) {
|
||||
if ($data['width'] != $this->width) {
|
||||
$itemHtml .= 'style="width:' . $data['width'] . '%;"';
|
||||
}
|
||||
}
|
||||
|
||||
$itemHtml .= '>' . PHP_EOL;
|
||||
if (isset($data['label'])) {
|
||||
$itemHtml .= $this->label($data['label'], $data) . PHP_EOL;
|
||||
}
|
||||
|
||||
$itemHtml .= $this->block($data) . PHP_EOL;
|
||||
$itemHtml .= '</div>' . PHP_EOL;
|
||||
|
||||
return $itemHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成Label标签
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function label(string $text, array $data = []): string
|
||||
{
|
||||
$label = '<label class="layui-form-label';
|
||||
if ($data['labelhide']) {
|
||||
$label .= ' layui-hide';
|
||||
}
|
||||
|
||||
$label .= '"';
|
||||
if ($data['labelwidth'] && $data['labelwidth'] != $this->labelwidth) {
|
||||
$label .= ' style="width:' . $data['labelwidth'] . 'px;"';
|
||||
}
|
||||
$label .= '>';
|
||||
if (isset($data['required']) && $data['required']) {
|
||||
$label .= '<font color="red">* </font>';
|
||||
}
|
||||
|
||||
return $label .= $text . '</label>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成BLOCK区块
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function block(array $data = []): string
|
||||
{
|
||||
$block = '<div class="layui-input-block"';
|
||||
|
||||
if (isset($data['labelhide'])) {
|
||||
if ($data['labelhide']) {
|
||||
$style = 'margin-left:0';
|
||||
} else {
|
||||
if ($data['labelwidth'] && $data['labelwidth'] != $this->labelwidth) {
|
||||
$style = 'margin-left:' . ($data['labelwidth'] + 30) . 'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($style)) {
|
||||
$block .= ' style="' . $style . '"';
|
||||
}
|
||||
|
||||
$block .= '>';
|
||||
$block .= call_user_func([Form::instance(), $data['tag']], $data);
|
||||
$block .= '</div>';
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取input
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function input(array $data = []): string
|
||||
{
|
||||
$value = $this->formtype ? 'value="{$data.' . $data['name'] . '}"' : '';
|
||||
return '<input class="layui-input" ' . $this->attributes($data) . $value . ' >';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多行编辑
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function textarea(array $data = []): string
|
||||
{
|
||||
$value = $this->formtype ? '{$data.' . $data['name'] . '}' : '';
|
||||
return '<textarea class="layui-textarea"' . $this->attributes($data) . ' >' . $value . '</textarea>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单选框
|
||||
*
|
||||
* @param array $data
|
||||
* @return string|string[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function radio(array $data = [])
|
||||
{
|
||||
return $this->radioCheckSelect($data,'radio');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多选框
|
||||
*
|
||||
* @param array $data
|
||||
* @return string|string[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function checkbox(array $data = [])
|
||||
{
|
||||
if (!$this->formtype) {
|
||||
throw new \Exception('多选框不支持生成内置表单');
|
||||
}
|
||||
return $this->radioCheckSelect($data,'checkbox','[]');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下拉框
|
||||
*
|
||||
* @param array $data
|
||||
* @return string|string[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function select(array $data = [])
|
||||
{
|
||||
return $this->radioCheckSelect($data,'select');
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证选项
|
||||
*
|
||||
* @param array $options
|
||||
* @return string|string[]|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function validOptions(array $options = [])
|
||||
{
|
||||
if (!is_array($options) || !$options) {
|
||||
throw new \Exception("Options is Empty", 1);
|
||||
}
|
||||
|
||||
$export = var_exports($options, true);
|
||||
return preg_replace('/\s+/', '', $export);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取PHP代码
|
||||
*
|
||||
* @param [type] $argc
|
||||
* @param [type] $options
|
||||
* @return string
|
||||
*/
|
||||
public function getVarPHPList($argc = null, $options = null): string
|
||||
{
|
||||
return PHP_EOL . "<php>$$argc = $options;</php>";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $type
|
||||
* @param string $attr
|
||||
* @return string|string[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function radioCheckSelect(array $data = [], string $type = '', string $attr = '' )
|
||||
{
|
||||
$options = $this->validOptions($data['options']);
|
||||
$varName = ucfirst($data['name']).'_LIST';
|
||||
$getAttr = $this->attributes($data,$attr);
|
||||
$varHtml = $this->getVarPHPList($varName, $options);
|
||||
$varHtml .= read_file($this->getHtmlTpl($type));
|
||||
|
||||
$this->replace = [
|
||||
'varlist' => $varName,
|
||||
'field' => $data['name'],
|
||||
'attributes' => $getAttr,
|
||||
];
|
||||
|
||||
foreach ($this->replace as $key => $value) {
|
||||
$varHtml = str_replace("{%$key%}", $value, $varHtml);
|
||||
}
|
||||
|
||||
return $varHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日期
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function date(array $data = []): string
|
||||
{
|
||||
$value = $this->formtype ? 'value="{$data.' . $data['name'] . '}"' : '';
|
||||
return '<input class="layui-input" lay-datetime="" ' . $this->attributes($data) . $value . ' >';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取颜色选择器
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function colorpicker(array $data = []): string
|
||||
{
|
||||
$value = $this->formtype ? 'value="{$data.' . $data['name'] . '}"' : '';
|
||||
|
||||
if (!$this->formtype) {
|
||||
throw new \Exception('颜色选择器不支持生成内置表单');
|
||||
}
|
||||
|
||||
return <<<Eof
|
||||
<input class="layui-input layui-hide" {$this->attributes($data)} {$value} >
|
||||
<div lay-colorpicker="{$data['name']}"></div>
|
||||
Eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取滑块
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function slider(array $data = []): string
|
||||
{
|
||||
$value = $this->formtype ? 'value="{$data.' . $data['name'] . '}"' : '';
|
||||
return <<<Eof
|
||||
<input class="layui-input layui-hide" name="{$data['name']}" {$value} >
|
||||
<div class="lay-slider" lay-slider="{$data['name']}" {$this->attributes($data)} ></div>
|
||||
Eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评分
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function rate(array $data = []): string
|
||||
{
|
||||
|
||||
$value = $this->formtype ? 'value="{$data.' . $data['name'] . '}"' : '';
|
||||
|
||||
if (!$this->formtype) {
|
||||
throw new \Exception("评分组件不支持生成内置表单");
|
||||
}
|
||||
|
||||
return <<<Eof
|
||||
<input class="layui-input layui-hide" name="{$data['name']}" {$value} >
|
||||
<div lay-rate="{$data['name']}" {$this->attributes($data)} ></div>
|
||||
Eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取开关
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function switch(array $data = []): string
|
||||
{
|
||||
$value = $this->formtype ? 'value="{$data.' . $data['name'] . '}"' : '';
|
||||
$param = '$data.' . $data['name'];
|
||||
return <<<Eof
|
||||
<input type="hidden" type="checkbox" name="{$data['name']}" value="0" />
|
||||
<input type="checkbox" name="{$data['name']}" value="1" <eq name="{$param}" value="1" > checked </eq> lay-skin="switch" />
|
||||
Eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取级联选择器
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function cascader(array $data = [])
|
||||
{
|
||||
if (!$this->formtype) {
|
||||
throw new \Exception("级联选择器不支持生成内置表单");
|
||||
}
|
||||
$value = 'value="{$data.' . $data['name'] . '}"';
|
||||
return <<<Eof
|
||||
<input type="text" id="{$data['name']}" class="layui-hide" lay-cascader="" {$this->attributes($data)} {$value} />
|
||||
Eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取富文本
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function editor(array $data = []): string
|
||||
{
|
||||
if (!$this->formtype) {
|
||||
throw new \Exception("富文本不支持生成内置表单");
|
||||
}
|
||||
// 非INPUT表单 值
|
||||
$value = '{$data.' . $data['name'] . '}';
|
||||
return <<<Eof
|
||||
<textarea id="{$data['name']}" {$data['editorType']} class="layui-hide" {$this->attributes($data)} type="layui-textarea" >{$value}</textarea>
|
||||
Eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传模板
|
||||
*
|
||||
* @param array $data
|
||||
* @return false|string|string[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function upload(array $data = [])
|
||||
{
|
||||
|
||||
if (!$this->formtype && ($data['uploadtype'] == 'multiple' || $data['uploadtype'] == 'images')) {
|
||||
throw new \Exception("上传组件仅支持 File类型 生成内置表单");
|
||||
}
|
||||
|
||||
$value = $this->formtype ? '{$data.' . $data['name'] . '}' : '';
|
||||
$varHtml = read_file($this->getHtmlTpl($data['uploadtype']));
|
||||
$this->replace = [
|
||||
'value' => $value,
|
||||
'field' => $data['name'],
|
||||
'accept' => $data['data_accept'],
|
||||
'size' => (string)$data['data_size'],
|
||||
];
|
||||
|
||||
foreach ($this->replace as $key => $value) {
|
||||
$varHtml = str_replace("{%$key%}", $value, $varHtml);
|
||||
}
|
||||
|
||||
return $varHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取TAGS模板
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function tags(array $data = []): string
|
||||
{
|
||||
$value = 'value="{$data.' . $data['name'] . '}"';
|
||||
return '<input type="text" lay-tags="" id="' . $data['name'] . '" name="' . $data['name'] .'" '. $value .' class="layui-input" >';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JSON模板
|
||||
*
|
||||
* @param array $data
|
||||
* @return false|string|string[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function json(array $data = [])
|
||||
{
|
||||
if (!$this->formtype) {
|
||||
throw new \Exception("JSON组件不支持生成内置表单");
|
||||
}
|
||||
|
||||
$value = $this->formtype ? 'value="{$data.' . $data['name'] . '}"' : '';
|
||||
$jsonHtml = read_file($this->getHtmlTpl($data['tag']));
|
||||
|
||||
$this->replace = [
|
||||
'value' => $value,
|
||||
'field' => $data['name'],
|
||||
];
|
||||
|
||||
foreach ($this->replace as $key => $value) {
|
||||
$jsonHtml = str_replace("{%$key%}", $value, $jsonHtml);
|
||||
}
|
||||
|
||||
return $jsonHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提示器
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function tips(array $data = []): string
|
||||
{
|
||||
return '<div class="layui-input-inline"><i class="layui-icon layui-icon-about" lay-tips="' . $data['msg'] . '" data-offset="' . $data['offset'] . '"></i></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取便签
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function note(array $data = []): string
|
||||
{
|
||||
return '<blockquote class="layui-elem-quote">' . $data['textarea'] . '</blockquote>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取横线
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function subtraction(array $data = []): string
|
||||
{
|
||||
return '<hr class="' . $data['border'] . '">';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取行高
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function space(array $data = []): string
|
||||
{
|
||||
return '<div style="height:' . $data['height'] . 'px;"></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选项卡
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function tab(array $data = []): string
|
||||
{
|
||||
$tabHtml = '<div id="layui-tab" id="' . $data['name'] . '" class="layui-tab layui-tab-brief">';
|
||||
$tabHtml .= '<ul class="layui-tab-title">';
|
||||
$tabContent = '';
|
||||
foreach ($data['options'] as $key => $option) {
|
||||
$tabHtml .= '<li class="' . ($option['checked'] ? 'layui-this' : '') . '">' . $option['title'] . '</li>';
|
||||
$tabContent .= '<div class="layui-tab-item ' . ($option['checked'] ? 'layui-show ' : '') . '" data-index="' . $key . '">';
|
||||
|
||||
foreach ($data['children'][$key] as $children) {
|
||||
foreach ($children as $elem) {
|
||||
$tabContent .= $this->itemElem($elem);
|
||||
}
|
||||
}
|
||||
|
||||
$tabContent .= '</div>';
|
||||
}
|
||||
|
||||
$tabHtml .= '</ul>';
|
||||
$tabHtml .= '<div class="layui-tab-content">' . $tabContent . '</div>';
|
||||
$tabHtml .= '</div>';
|
||||
return $tabHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取布局组件
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function grid(array $data = []): string
|
||||
{
|
||||
$gridHtml = '<div class="layui-form-item layui-row" >';
|
||||
$col = 12 / $data['column'];
|
||||
for ($key=0; $key < $data['column']; $key++) {
|
||||
|
||||
$gridHtml .= '<div class="layui-col-md' .$col. ' layui-grid-' .$key. '" data-index="' .$key. '">';
|
||||
|
||||
foreach ($data['children'][$key] as $children) {
|
||||
foreach ($children as $elem) {
|
||||
$gridHtml .= $this->itemElem($elem);
|
||||
}
|
||||
}
|
||||
|
||||
$gridHtml .= '</div>';
|
||||
}
|
||||
|
||||
$gridHtml .= '</div>';
|
||||
|
||||
return $gridHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表单属性
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $suffix
|
||||
* @return string
|
||||
*/
|
||||
public function attributes(array $data = [], string $suffix = ''): string
|
||||
{
|
||||
$vars = [];
|
||||
foreach ($data as $key => $elem) {
|
||||
if (array_search($key, $this->attrs)) {
|
||||
|
||||
if (!$elem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 单独处理NAME值
|
||||
if ($key == 'name') {
|
||||
$elem .= $suffix;
|
||||
}
|
||||
|
||||
$vars[] = $key . '="' . $elem . '"';
|
||||
} else {
|
||||
if (strstr($key, 'lay_') || strstr($key, 'data_')) {
|
||||
$_key = str_replace('_', '-', $key);
|
||||
$vars[] = $_key . '="' . $elem . '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count($vars) > 0 ? ' ' . implode(' ', $vars) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板文件
|
||||
*
|
||||
* @param [type] $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getHtmlTpl($name): string
|
||||
{
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'form' . DIRECTORY_SEPARATOR . $name . '.html';
|
||||
}
|
||||
}
|
||||
105
extend/system/Http.php
Normal file
105
extend/system/Http.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace system;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/**
|
||||
* Http 请求类
|
||||
*/
|
||||
class Http
|
||||
{
|
||||
/**
|
||||
* PC/Mobile 标识
|
||||
* @var object 对象实例
|
||||
*/
|
||||
protected static $agent = [
|
||||
'Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10',
|
||||
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
|
||||
];
|
||||
|
||||
/**
|
||||
* 发送一个POST请求
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @param bool $agent
|
||||
* @param array $options
|
||||
* @param $header
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function post(string $url, array $params = [], bool $agent = true, array $options = [], $header = '')
|
||||
{
|
||||
$req = self::request($url, $params, $agent, 'POST', $options, $header);
|
||||
return $req['ret'] ? $req['msg'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送一个GET请求
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @param bool $agent
|
||||
* @param array $options
|
||||
* @param $header
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function get(string $url, array $params = [], bool $agent = true, array $options = [], $header = [])
|
||||
{
|
||||
$req = self::request($url, $params, $agent, 'GET', $options, $header);
|
||||
return $req['ret'] ? $req['msg'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @param bool $agent
|
||||
* @param string $method
|
||||
* @param array $options
|
||||
* @param array $header
|
||||
* @return array
|
||||
*/
|
||||
public static function request(string $url, array $params, bool $agent, string $method = 'GET', array $options = [], array $header = []): array
|
||||
{
|
||||
try {
|
||||
$client = self::getClient($agent, $options, $header);
|
||||
$response = $client->request($method, $url, $params ? ['query' => $params] : [])->getBody()->getContents();
|
||||
if (!empty($response)) {
|
||||
return ['ret' => true, 'msg' => $response];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return ['ret' => false, 'msg' => $e->getMessage()];
|
||||
}
|
||||
|
||||
return ['ret' => false, 'msg' => $response];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问客户端
|
||||
* @param bool $agent
|
||||
* @param array $options
|
||||
* @param array $header
|
||||
* @return mixed
|
||||
*/
|
||||
private static function getClient(bool $agent, array $options = [], array $header = [])
|
||||
{
|
||||
if (empty($options)) {
|
||||
$options = [
|
||||
'timeout' => 60,
|
||||
'connect_timeout' => 60,
|
||||
'verify' => false,
|
||||
'http_errors' => false,
|
||||
'headers' => [
|
||||
'X-REQUESTED-WITH' => 'XMLHttpRequest',
|
||||
'Referer' => dirname(request()->url()),
|
||||
'User-Agent' => self::$agent[$agent]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($header)) {
|
||||
$options['headers'] = array_merge($options['headers'], $header);
|
||||
}
|
||||
|
||||
return new Client($options);
|
||||
}
|
||||
|
||||
}
|
||||
146
extend/system/Random.php
Normal file
146
extend/system/Random.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// +----------------------------------------------------------------------
|
||||
// | swiftAdmin 极速开发框架 [基于WebMan开发]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2020-2030 http://www.swiftadmin.net
|
||||
// +----------------------------------------------------------------------
|
||||
// | swiftAdmin.net High Speed Development Framework
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: meystack <coolsec@foxmail.com> Apache2 License
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace system;
|
||||
|
||||
class Random
|
||||
{
|
||||
|
||||
/**
|
||||
* @var object 对象实例
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* 类构造函数
|
||||
* class constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param array $options 参数
|
||||
* @return self
|
||||
*/
|
||||
|
||||
public static function instance(array $options = [])
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new static($options);
|
||||
}
|
||||
|
||||
// 返回实例
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成大小写字母
|
||||
*
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
public static function alpha(int $length = 6): string
|
||||
{
|
||||
return self::Generate('alpha', $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成纯数字
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
public static function number(int $length = 6): string
|
||||
{
|
||||
return self::Generate('number', $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成小写字母
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
public static function lower(int $length = 6): string
|
||||
{
|
||||
return self::Generate('lower', $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成大写字母
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
public static function upper(int $length = 6): string
|
||||
{
|
||||
return self::Generate('upper', $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下划线随机
|
||||
*
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
public static function alphaDash(int $length = 6): string
|
||||
{
|
||||
return self::Generate('alphaDash', $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成数字+字母
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
public static function alphaNum(int $length = 6): string
|
||||
{
|
||||
return self::Generate('alphaNum', $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符
|
||||
* @param string $type
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
public static function Generate(string $type = 'alpha', int $length = 6): string
|
||||
{
|
||||
$config = [
|
||||
'number' => '1234567890',
|
||||
'lower' => 'abcdefghijklmnopqrstuvwxyz',
|
||||
'upper' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'alpha' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'alphaDash' => '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'alphaNum' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
];
|
||||
|
||||
$letter = str_shuffle($config[$type]);
|
||||
return substr($letter, 0, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单ID
|
||||
*
|
||||
* @param boolean $other
|
||||
* @return string
|
||||
*/
|
||||
public static function orderId(bool $other = false): string
|
||||
{
|
||||
if (!$other) {
|
||||
return date('Ymd') . str_pad((string)mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
|
||||
} else {
|
||||
return date('Ymd') . substr(implode('', array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
121
extend/system/ZipArchives.php
Normal file
121
extend/system/ZipArchives.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// +----------------------------------------------------------------------
|
||||
// | swiftAdmin 极速开发框架 [基于WebMan开发]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2020-2030 http://www.swiftadmin.net
|
||||
// +----------------------------------------------------------------------
|
||||
// | swiftAdmin.net High Speed Development Framework
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: meystack <coolsec@foxmail.com> Apache2 License
|
||||
// +----------------------------------------------------------------------
|
||||
namespace system;
|
||||
|
||||
use FilesystemIterator;
|
||||
|
||||
/**
|
||||
* 文件压缩类
|
||||
* @author meystack
|
||||
* @version 1.0
|
||||
*/
|
||||
class ZipArchives
|
||||
{
|
||||
/**
|
||||
* 解压文件
|
||||
* @param string $fileName
|
||||
* @param string $filePath
|
||||
* @param string $search
|
||||
* @param bool $delete
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function unzip(string $fileName, string $filePath = '', string $search = '', bool $delete = false)
|
||||
{
|
||||
if (!is_file($fileName) && preg_match('/^[a-z]{3,32}/', $fileName)) {
|
||||
$fileName = plugin_path() . $fileName . '.zip';
|
||||
}
|
||||
|
||||
if (!is_file($fileName)) {
|
||||
throw new \Exception(__('解压文件不存在'), -113);
|
||||
}
|
||||
|
||||
$fileStream = '';
|
||||
$filePath = $filePath ?: plugin_path();
|
||||
$zip = new \ZipArchive();
|
||||
if ($zip->open($fileName) !== TRUE) {
|
||||
throw new \Exception(__("访问解压文件失败"), -114);
|
||||
}
|
||||
try {
|
||||
if (!empty($search)) {
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$filePath = str_replace('\\','/',$zip->getNameIndex($i));
|
||||
$fileName = explode('/', $filePath);
|
||||
if (end($fileName) == $search) {
|
||||
$fileStream = $zip->getFromIndex($i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!is_dir($filePath)) {
|
||||
@mkdir($filePath, 0755, true);
|
||||
}
|
||||
$zip->extractTo($filePath);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
throw new \Exception("解压 " . $fileName . " 包失败", -115);
|
||||
} finally {
|
||||
$zip->close();
|
||||
if ($delete && !$search) {
|
||||
unlink($fileName);
|
||||
}
|
||||
}
|
||||
|
||||
return $search ? $fileStream : $filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩文件夹
|
||||
* @param string $fileName
|
||||
* @param string $filePath
|
||||
* @param string $rootPath
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function compression(string $fileName, string $filePath, string $rootPath = ''): bool
|
||||
{
|
||||
$zip = new \ZipArchive();
|
||||
try {
|
||||
|
||||
@unlink($fileName);
|
||||
$zip->open($fileName, \ZipArchive::CREATE);
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($filePath, FilesystemIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
// 默认为插件目录
|
||||
$rootPath = $rootPath ?: plugin_path();
|
||||
foreach ($files as $fileinfo) {
|
||||
if ($fileinfo->isFile()) {
|
||||
// 过滤冗余文件
|
||||
$filePath = str_replace('\\','/',$fileinfo->getRealPath());
|
||||
if (!in_array($fileinfo->getFilename(), ['.git', '.vscode', 'Thumbs.db'])) {
|
||||
$zip->addFile($filePath, str_replace($rootPath, '', $filePath));
|
||||
}
|
||||
} else {
|
||||
$localDir = str_replace('\\','/',$fileinfo->getPathName());
|
||||
$localDir = str_replace($rootPath, '', $localDir);
|
||||
$zip->addEmptyDir($localDir);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
throw new \Exception("压缩 " . $fileName . " 包失败", -115);
|
||||
} finally {
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
92
extend/system/exception.tpl
Normal file
92
extend/system/exception.tpl
Normal file
@@ -0,0 +1,92 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>系统发生错误</title>
|
||||
<meta name="robots" content="noindex,nofollow" />
|
||||
<style>
|
||||
body{color:#333;margin:0;padding:0 20px 20px;min-height:100%;background:#edf1f4;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",微软雅黑,Arial,sans-serif}
|
||||
h1{margin:10px 0 0;font-size:28px;font-weight:500;line-height:32px}
|
||||
h2{color:#4288ce;font-weight:400;padding:6px 0;margin:6px 0 0;font-size:18px;border-bottom:1px solid #eee}
|
||||
h3{margin:12px;font-size:16px;font-weight:bold}
|
||||
abbr{cursor:help;text-decoration:underline;text-decoration-style:dotted}
|
||||
a{color:#868686;cursor:pointer}
|
||||
a:hover{text-decoration:underline}
|
||||
.line-error{background:#f8cbcb}
|
||||
.echo table{width:100%}
|
||||
.echo pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f7f7f7;border:0;border-radius:3px;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace}
|
||||
.echo pre > pre{padding:0;margin:0}
|
||||
.exception{margin-top:20px}
|
||||
.exception .message{padding:12px;border:1px solid #ddd;border-bottom:0 none;line-height:18px;font-size:16px;border-top-left-radius:4px;border-top-right-radius:4px;font-family:Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑",serif}
|
||||
.exception .code{float:left;text-align:center;color:#fff;margin-right:12px;padding:16px;border-radius:4px;background:#999}
|
||||
.exception .source-code{padding:6px;border:1px solid #ddd;background:#f9f9f9;overflow-x:auto}
|
||||
.exception .source-code pre{margin:0}
|
||||
.exception .source-code pre ol{margin:0;color:#4288ce;display:inline-block;min-width:100%;box-sizing:border-box;font-size:14px;font-family:"Century Gothic",Consolas,"Liberation Mono",Courier,Verdana,serif;padding-left:<?php echo (isset($source) && !empty($source)) ? parse_padding($source):40;?>px}
|
||||
.exception .source-code pre li{border-left:1px solid #ddd;height:18px;line-height:18px}
|
||||
.exception .source-code pre code{color:#333;height:100%;display:inline-block;border-left:1px solid #fff;font-size:14px;font-family:Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑",serif}
|
||||
.exception .trace{padding:6px;border:1px solid #ddd;border-top:0 none;line-height:16px;font-size:14px;font-family:Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑",serif}
|
||||
.exception .trace h2:hover{text-decoration:underline;cursor:pointer}
|
||||
.exception .trace ol{margin:12px}
|
||||
.exception .trace ol li{padding:2px 4px}
|
||||
.exception div:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px}
|
||||
.exception-var table{width:100%;margin:12px 0;box-sizing:border-box;table-layout:fixed;word-wrap:break-word}
|
||||
.exception-var table caption{text-align:left;font-size:16px;font-weight:bold;padding:6px 0}
|
||||
.exception-var table caption small{font-weight:300;display:inline-block;margin-left:10px;color:#ccc}
|
||||
.exception-var table tbody{font-size:13px;font-family:Consolas,"Liberation Mono",Courier,"微软雅黑",serif}
|
||||
.exception-var table td{padding:0 6px;vertical-align:top;word-break:break-all}
|
||||
.exception-var table td:first-child{width:28%;font-weight:bold;white-space:nowrap}
|
||||
.exception-var table td pre{margin:0}
|
||||
.copyright{margin-top:24px;padding:12px 0;border-top:1px solid #eee}
|
||||
pre.prettyprint .pln{color:#000}
|
||||
pre.prettyprint .str{color:#080}
|
||||
pre.prettyprint .kwd{color:#008}
|
||||
pre.prettyprint .com{color:#800}
|
||||
pre.prettyprint .typ{color:#606}
|
||||
pre.prettyprint .lit{color:#066}
|
||||
pre.prettyprint .pun,pre.prettyprint .opn,pre.prettyprint .clo{color:#660}
|
||||
pre.prettyprint .tag{color:#008}
|
||||
pre.prettyprint .atn{color:#606}
|
||||
pre.prettyprint .atv{color:#080}
|
||||
pre.prettyprint .dec,pre.prettyprint .var{color:#606}
|
||||
pre.prettyprint .fun{color:red}
|
||||
.exception-container{border-radius:5px;text-align:center;box-shadow:0 0 30px rgba(99,99,99,0.06);padding:50px;background-color:#fff;width:100%;left:50%;top:50%;max-width:456px;position:absolute;margin-top:-280px;margin-left:-280px}
|
||||
.exception-container .head-line{transition:color .2s linear;font-size:40px;line-height:60px;letter-spacing:-1px;color:#777}
|
||||
.exception-container .subheader{transition:color .2s linear;font-size:26px;line-height:46px;color:#494949}
|
||||
.exception-container .hr{height:1px;background-color:#eee;width:80%;max-width:350px;margin:23px auto}
|
||||
.exception-container .context{transition:color .2s linear;font-size:16px;line-height:27px;color:#aaa}
|
||||
.exception-container .buttons-container{margin-top:35px;overflow:hidden}
|
||||
.exception-container .buttons-container a{transition:text-indent .2s ease-out,color .2s linear,background-color .2s linear;text-indent:0px;font-size:14px;text-transform:uppercase;text-decoration:none;color:#fff;background-color:#1890ff;border-radius:10px;padding:10px 10px;text-align:center;display:inline-block;overflow:hidden;position:relative;width:40%;margin:0px 8px 0px 8px}
|
||||
.status-ico{width:72px;height:72px;line-height:72px;font-size:42px;color:#fff;text-align:center;border-radius:50%;display:inline-block;margin-bottom:24px;background-color:#52c41a!important}
|
||||
.status-error{background-color:#ff4d4f!important}
|
||||
@media screen and (max-width:580px){padding:30px 5%}
|
||||
.head-line{font-size:36px}
|
||||
.subheader{font-size:27px;line-height:37px}
|
||||
.hr{margin:30px auto;width:215px}
|
||||
}@media screen and (max-width:450px){padding:30px}
|
||||
.head-line{font-size:32px}
|
||||
.hr{margin:25px auto;width:180px}
|
||||
.context{font-size:15px;line-height:22px}
|
||||
.context p:nth-child(n+2){margin-top:10px}
|
||||
.buttons-container{margin-top:29px}
|
||||
.buttons-container a{float:none !important;width:65%;margin:0 auto;font-size:13px;padding:9px 0}
|
||||
.buttons-container a:nth-child(2){margin-top:12px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="exception-container">
|
||||
<div class="head-line"><span class="status-ico status-error">X</span></div>
|
||||
<div class="subheader">服务器异常</div>
|
||||
<div class="hr"></div>
|
||||
<div class="context">
|
||||
<p>你可以返回上一页重试,或直接向我们反馈错误报告</p>
|
||||
</div>
|
||||
<div class="buttons-container">
|
||||
<a href="/">返回主页</a>
|
||||
<a href="/">反馈错误</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
10
extend/system/form/checkbox.html
Normal file
10
extend/system/form/checkbox.html
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<volist name="{%varlist%}" id="vo">
|
||||
<input type="checkbox" {%attributes%} value="{$vo.value}" title="{$vo.title}"
|
||||
<if (isset($data['id']) && $data['id']) >
|
||||
<in name="$vo.value" value="$data.{%field%}">checked</in>
|
||||
<else/>
|
||||
<eq name="$vo.checked" value="true">checked</eq>
|
||||
</if>
|
||||
>
|
||||
</volist>
|
||||
8
extend/system/form/file.html
Normal file
8
extend/system/form/file.html
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
<input type="text" name="{%field%}" value="{%value%}" class="layui-input layui-input-upload {%field%}" >
|
||||
<button type="button" class="layui-btn" lay-upload="{%field%}" data-type="normal" data-accept="{%accept%}" data-size="{%size%}">
|
||||
<i class="layui-icon layui-icon-upload"></i> 上传
|
||||
</button>
|
||||
<button type="button" class="layui-btn ml10" lay-choose="{%field%}" data-type="file" >
|
||||
<i class="layui-icon layui-icon-windows"></i> 选择
|
||||
</button>
|
||||
24
extend/system/form/images.html
Normal file
24
extend/system/form/images.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<input class="layui-input layui-input-upload {%field%}" name="{%field%}" value="{%value%}">
|
||||
<button type="button" class="layui-btn" lay-choose="{%field%}" data-type="images" >
|
||||
<i class="layui-icon layui-icon-windows"></i> 选择
|
||||
</button>
|
||||
<div class="clear"></div>
|
||||
<notempty name="$data['{%field%}']" >
|
||||
<div class="layui-upload-drag layui-uplpad-image mt10" lay-upload="{%field%}" data-type="images" data-accept="{%accept%}" data-size="{%size%}">
|
||||
<i class="layui-icon layui-icon-upload layui-hide"></i>
|
||||
<p class="layui-hide">点击上传,或将文件拖拽到此处</p>
|
||||
<div >
|
||||
<hr><img src="{%value%}" class="layui-upload-dragimg {%field%}" alt="上传成功后渲染" >
|
||||
<span class="layui-badge layui-upload-clear">删除</span>
|
||||
</div>
|
||||
</div>
|
||||
<else/>
|
||||
<div class="layui-upload-drag layui-uplpad-image mt10" lay-upload="{%field%}" data-type="images" data-accept="{%accept%}" data-size="{%size%}">
|
||||
<i class="layui-icon layui-icon-upload"></i>
|
||||
<p>点击上传,或将文件拖拽到此处</p>
|
||||
<div class="layui-hide">
|
||||
<hr><img src="{%value%}" class="layui-upload-dragimg {%field%}" alt="上传成功后渲染" >
|
||||
<span class="layui-badge layui-upload-clear">删除</span>
|
||||
</div>
|
||||
</div>
|
||||
</notempty>
|
||||
21
extend/system/form/json.html
Normal file
21
extend/system/form/json.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<table class="layui-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>变量值</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<notempty name="data.{%field%}">
|
||||
<volist name="data.{%field%}" id="vo" key="i">
|
||||
<tr>
|
||||
<td><input type="text" class="layui-input" name="{%field%}[key][]" value="{$key}" ></td>
|
||||
<td><input type="text" class="layui-input" name="{%field%}[value][]" value="{$vo}"></td>
|
||||
<td><i class="layui-icon fa-times" data-name="{%field%}" onclick="layui.admin.resetInput(this);"></i></td>
|
||||
</tr>
|
||||
</volist>
|
||||
</notempty>
|
||||
</tbody>
|
||||
</table>
|
||||
<button type="button" class="layui-btn layui-btn-normal layui-jsonvar-add" data-name="{%field%}">追加</button>
|
||||
25
extend/system/form/multiple.html
Normal file
25
extend/system/form/multiple.html
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
<div class="layui-imagesbox">
|
||||
|
||||
<!-- // 循环输出代码 -->
|
||||
<notempty name="$data['{%field%}']" >
|
||||
<volist name="$data['{%field%}']" id="vo">
|
||||
<div class="layui-input-inline layui-uplpad-image">
|
||||
<img src="{$vo.src}" lay-image-hover >
|
||||
<input type="text" name="{%field%}[{$key}][src]" class="layui-hide" value="{$vo.src}" >
|
||||
<input type="text" name="{%field%}[{$key}][title]" class="layui-input" value="{$vo.title}" placeholder="图片简介">
|
||||
<span class="layui-badge layui-badge-red" data-name="{%field%}" onclick="layui.admin.resetInput(this,'images');">删除</span>
|
||||
</div>
|
||||
</volist>
|
||||
</notempty>
|
||||
<div class="layui-input-inline layui-uplpad-image">
|
||||
<div class="layui-upload-drag" lay-upload="{%field%}" data-type="multiple" data-accept="{%accept%}" data-size="{%size%}">
|
||||
<i class="layui-icon layui-icon-upload"></i>
|
||||
<p>点击上传,或将文件拖拽到此处</p>
|
||||
<div class="layui-hide"></div>
|
||||
</div>
|
||||
<button type="button" class="layui-btn layui-btn-xs layui-btn-fluid" lay-choose="{%field%}" data-name="{%field%}" data-type="multiple">
|
||||
<i class="layui-icon layui-icon-windows"></i> 选择
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
10
extend/system/form/radio.html
Normal file
10
extend/system/form/radio.html
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<volist name="{%varlist%}" id="vo">
|
||||
<input type="radio" {%attributes%} value="{$vo.value}" title="{$vo.title}"
|
||||
<if (isset($data['id']) && $data['id']) >
|
||||
<eq name="$vo.value" value="$data.{%field%}">checked</eq>
|
||||
<else/>
|
||||
<eq name="$vo.checked" value="true">checked</eq>
|
||||
</if>
|
||||
>
|
||||
</volist>
|
||||
13
extend/system/form/select.html
Normal file
13
extend/system/form/select.html
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
<select {%attributes%} >
|
||||
<volist name="{%varlist%}" id="vo">
|
||||
<option value="{$vo.value}"
|
||||
<if (isset($data['id']) && $data['id']) >
|
||||
<in name="$vo.value" value="$data.{%field%}">selected</in>
|
||||
<else/>
|
||||
<eq name="$vo.checked" value="true">selected</eq>
|
||||
</if>
|
||||
>{$vo.title}</option>
|
||||
</volist>
|
||||
</select>
|
||||
146
extend/system/third/gitee.php
Normal file
146
extend/system/third/gitee.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace system\third;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
|
||||
/**
|
||||
* Gitee登录类
|
||||
*/
|
||||
|
||||
class gitee
|
||||
{
|
||||
|
||||
const GET_AUTH_CODE_URL = "https://gitee.com/oauth/authorize";
|
||||
const GET_ACCESS_TOKEN_URL = "https://gitee.com/oauth/token";
|
||||
const GET_USERINFO_URL = "https://gitee.com/api/v5/user";
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
* @var array
|
||||
*/
|
||||
private $config = [];
|
||||
|
||||
/**
|
||||
* Http实例
|
||||
* @var Object
|
||||
*/
|
||||
protected $http = null;
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if ($config = saenv('gitee')) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
$this->config = array_merge($this->config, is_array($options) ? $options : []);
|
||||
$this->http = new Client();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
public function login() {
|
||||
return redirect($this->getAuthorizeUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录地址
|
||||
*/
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
$state = hash('sha256',uniqid((string)mt_rand()));
|
||||
session('state', $state);
|
||||
$queryarr = array(
|
||||
"response_type" => "code",
|
||||
"client_id" => $this->config['app_id'],
|
||||
"redirect_uri" => $this->config['callback'],
|
||||
// "scope" => 'user_info',
|
||||
"state" => $state,
|
||||
);
|
||||
|
||||
request()->isMobile() && $queryarr['display'] = 'mobile';
|
||||
$url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function getUserInfo(array $params = []): array
|
||||
{
|
||||
$params = $params ? $params : input();
|
||||
if ((isset($params['state']) && $params['state'] == session('state') && isset($params['code']))) {
|
||||
|
||||
// 获取access_token
|
||||
$data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
|
||||
|
||||
$access_token = isset($data['access_token']) ? $data['access_token'] : '';
|
||||
$refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
|
||||
$expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
|
||||
if ($access_token) {
|
||||
|
||||
// 获取用户信息
|
||||
$queryarr = [
|
||||
"access_token" => $access_token,
|
||||
];
|
||||
|
||||
$ret = $this->http->get(self::GET_USERINFO_URL.'?'.http_build_query($queryarr))->getBody()->getContents();
|
||||
$userinfo = json_decode($ret, true);
|
||||
|
||||
if (!$userinfo || !is_array($userinfo)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$userinfo['avatar'] = isset($userinfo['avatar_url']) ? $userinfo['avatar_url'] : '';
|
||||
$userinfo['avatar'] = str_replace('http://','https://',$userinfo['avatar']);
|
||||
$data = [
|
||||
'access_token' => $access_token,
|
||||
'refresh_token' => $refresh_token,
|
||||
'expires_in' => $expires_in,
|
||||
'userinfo' => $userinfo,
|
||||
'id' => $userinfo['id'],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access_token
|
||||
* @param string $code
|
||||
* @return array
|
||||
*/
|
||||
public function getAccessToken($code = '')
|
||||
{
|
||||
if (!$code) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$queryarr = array(
|
||||
"grant_type" => "authorization_code",
|
||||
"client_id" => $this->config['app_id'],
|
||||
"client_secret" => $this->config['app_key'],
|
||||
"redirect_uri" => $this->config['callback'],
|
||||
"code" => $code,
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
$params = $this->http->post(self::GET_ACCESS_TOKEN_URL,['query'=>$queryarr])->getBody()->getContents();
|
||||
} catch (\Throwable $th) {
|
||||
if (strstr($th->getMessage(),'error_description')) {
|
||||
throw new \Exception('登录已过期,请重新登录');
|
||||
}
|
||||
}
|
||||
|
||||
return $params ? json_decode($params,true): [];
|
||||
}
|
||||
}
|
||||
154
extend/system/third/qq.php
Normal file
154
extend/system/third/qq.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace system\third;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/**
|
||||
* QQ登录类
|
||||
*/
|
||||
|
||||
class qq
|
||||
{
|
||||
|
||||
const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
|
||||
const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
|
||||
const GET_USERINFO_URL = "https://graph.qq.com/user/get_user_info";
|
||||
const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
* @var array
|
||||
*/
|
||||
private $config = [];
|
||||
|
||||
/**
|
||||
* Http实例
|
||||
* @var Object
|
||||
*/
|
||||
protected $http = null;
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if ($config = saenv('qq')) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
$this->config = array_merge($this->config, is_array($options) ? $options : []);
|
||||
$this->http = new Client();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
public function login() {
|
||||
return redirect($this->getAuthorizeUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录地址
|
||||
*/
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
$state = hash('sha256',uniqid((string)mt_rand()));
|
||||
session('state', $state);
|
||||
$queryarr = array(
|
||||
"response_type" => "code",
|
||||
"client_id" => $this->config['app_id'],
|
||||
"redirect_uri" => $this->config['callback'],
|
||||
"scope" => 'get_user_info',
|
||||
"state" => $state,
|
||||
);
|
||||
|
||||
request()->isMobile() && $queryarr['display'] = 'mobile';
|
||||
$url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public function getUserInfo($params = [])
|
||||
{
|
||||
$params = $params ? $params : input();
|
||||
if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == session('state') && isset($params['code']))) {
|
||||
|
||||
//获取access_token
|
||||
$data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
|
||||
$access_token = isset($data['access_token']) ? $data['access_token'] : '';
|
||||
$refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
|
||||
$expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
|
||||
if ($access_token) {
|
||||
$openid = $this->getOpenId($access_token);
|
||||
//获取用户信息
|
||||
$queryarr = [
|
||||
"access_token" => $access_token,
|
||||
"oauth_consumer_key" => $this->config['app_id'],
|
||||
"openid" => $openid,
|
||||
];
|
||||
|
||||
$ret = $this->http->get(self::GET_USERINFO_URL.'?'.http_build_query($queryarr))->getBody()->getContents();
|
||||
$userinfo = (array)json_decode($ret, true);
|
||||
if (!$userinfo || !isset($userinfo['ret']) || $userinfo['ret'] !== 0) {
|
||||
return [];
|
||||
}
|
||||
$userinfo = $userinfo ? $userinfo : [];
|
||||
$userinfo['avatar'] = isset($userinfo['figureurl_qq_2']) ? $userinfo['figureurl_qq_2'] : '';
|
||||
$userinfo['avatar'] = str_replace('http://','https://',$userinfo['avatar']);
|
||||
$data = [
|
||||
'access_token' => $access_token,
|
||||
'refresh_token' => $refresh_token,
|
||||
'expires_in' => $expires_in,
|
||||
'openid' => $openid,
|
||||
'userinfo' => $userinfo
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access_token
|
||||
* @param string $code
|
||||
* @return array
|
||||
*/
|
||||
public function getAccessToken($code = '')
|
||||
{
|
||||
if (!$code) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$queryarr = array(
|
||||
"grant_type" => "authorization_code",
|
||||
"client_id" => $this->config['app_id'],
|
||||
"client_secret" => $this->config['app_key'],
|
||||
"redirect_uri" => $this->config['callback'],
|
||||
"code" => $code,
|
||||
);
|
||||
|
||||
$ret = $this->http->get(self::GET_ACCESS_TOKEN_URL.'?'.http_build_query($queryarr))->getBody()->getContents();
|
||||
$params = [];
|
||||
parse_str($ret, $params);
|
||||
return $params ? $params : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取open_id
|
||||
* @param string $access_token
|
||||
* @return string
|
||||
*/
|
||||
private function getOpenId($access_token = '')
|
||||
{
|
||||
$response = $this->http->get(self::GET_OPENID_URL.'?access_token='.$access_token)->getBody()->getContents();
|
||||
if (strpos($response, "callback") !== false) {
|
||||
$lpos = strpos($response, "(");
|
||||
$rpos = strrpos($response, ")");
|
||||
$response = substr($response, $lpos + 1, $rpos - $lpos - 1);
|
||||
}
|
||||
$user = (array)json_decode($response, true);
|
||||
return isset($user['openid']) ? $user['openid'] : '';
|
||||
}
|
||||
}
|
||||
133
extend/system/third/weibo.php
Normal file
133
extend/system/third/weibo.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace system\third;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/**
|
||||
* 微博登录类
|
||||
*/
|
||||
|
||||
class weibo
|
||||
{
|
||||
|
||||
const GET_AUTH_CODE_URL = "https://api.weibo.com/oauth2/authorize";
|
||||
const GET_ACCESS_TOKEN_URL = "https://api.weibo.com/oauth2/access_token";
|
||||
const GET_USERINFO_URL = "https://api.weibo.com/2/users/show.json";
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
* @var array
|
||||
*/
|
||||
private $config = [];
|
||||
|
||||
/**
|
||||
* Http实例
|
||||
* @var Object
|
||||
*/
|
||||
protected $http = null;
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if ($config = saenv('weibo')) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
$this->config = array_merge($this->config, is_array($options) ? $options : []);
|
||||
|
||||
$this->http = new Client();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
public function login() {
|
||||
return redirect($this->getAuthorizeUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录地址
|
||||
*/
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
$state = hash('sha256',uniqid((string)mt_rand()));
|
||||
session('state', $state);
|
||||
$queryarr = array(
|
||||
"response_type" => "code",
|
||||
"client_id" => $this->config['app_id'],
|
||||
"redirect_uri" => $this->config['callback'],
|
||||
"state" => $state,
|
||||
);
|
||||
|
||||
request()->isMobile() && $queryarr['display'] = 'mobile';
|
||||
$url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public function getUserInfo($params = [])
|
||||
{
|
||||
$params = $params ? $params : input();
|
||||
if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == session('state') && isset($params['code']))) {
|
||||
//获取access_token
|
||||
$data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
|
||||
$access_token = isset($data['access_token']) ? $data['access_token'] : '';
|
||||
$refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
|
||||
$expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
|
||||
if ($access_token) {
|
||||
$uid = isset($data['uid']) ? $data['uid'] : '';
|
||||
//获取用户信息
|
||||
$queryarr = [
|
||||
"access_token" => $access_token,
|
||||
"uid" => $uid,
|
||||
];
|
||||
$ret = $this->http->get(self::GET_USERINFO_URL.'?'.http_build_query($queryarr))->getBody()->getContents();
|
||||
$userinfo = (array)json_decode($ret, true);
|
||||
if (!$userinfo || isset($userinfo['error_code'])) {
|
||||
return [];
|
||||
}
|
||||
$userinfo = $userinfo ? $userinfo : [];
|
||||
$userinfo['nickname'] = isset($userinfo['screen_name']) ? $userinfo['screen_name'] : '';
|
||||
$userinfo['avatar'] = isset($userinfo['profile_image_url']) ? $userinfo['profile_image_url'] : '';
|
||||
$userinfo['avatar'] = str_replace('http://','https://',$userinfo['avatar']);
|
||||
$data = [
|
||||
'access_token' => $access_token,
|
||||
'refresh_token' => $refresh_token,
|
||||
'expires_in' => $expires_in,
|
||||
'openid' => $uid,
|
||||
'userinfo' => $userinfo
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access_token
|
||||
* @param string code
|
||||
* @return array
|
||||
*/
|
||||
public function getAccessToken($code = '')
|
||||
{
|
||||
if (!$code) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$queryarr = array(
|
||||
"grant_type" => "authorization_code",
|
||||
"client_id" => $this->config['app_id'],
|
||||
"client_secret" => $this->config['app_key'],
|
||||
"redirect_uri" => $this->config['callback'],
|
||||
"code" => $code,
|
||||
);
|
||||
|
||||
$response = $this->http->post(self::GET_ACCESS_TOKEN_URL,['query'=>$queryarr])->getBody()->getContents();
|
||||
$ret = (array)json_decode($response, true);
|
||||
return $ret ? $ret : [];
|
||||
}
|
||||
}
|
||||
143
extend/system/third/weixin.php
Normal file
143
extend/system/third/weixin.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace system\third;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/**
|
||||
* 微信登录类
|
||||
*/
|
||||
|
||||
class weixin
|
||||
{
|
||||
const GET_AUTH_CODE_URL = "https://open.weixin.qq.com/connect/qrconnect";
|
||||
const GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
|
||||
const GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
* @var array
|
||||
*/
|
||||
private $config = [];
|
||||
|
||||
/**
|
||||
* Http实例
|
||||
* @var Object
|
||||
*/
|
||||
protected $http = null;
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if ($config = saenv('weixin')) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
$this->config = array_merge($this->config, is_array($options) ? $options : []);
|
||||
|
||||
$this->http = new Client();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
public function login() {
|
||||
return redirect($this->getAuthorizeUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录地址
|
||||
*/
|
||||
public function getAuthorizeUrl()
|
||||
{
|
||||
$state = hash('sha256',uniqid((string)mt_rand()));
|
||||
session('state', $state);
|
||||
$queryarr = array(
|
||||
"response_type" => "code",
|
||||
"appid" => $this->config['app_id'],
|
||||
"redirect_uri" => $this->config['callback'],
|
||||
"scope" => 'snsapi_login,',
|
||||
"state" => $state,
|
||||
);
|
||||
|
||||
request()->isMobile() && $queryarr['display'] = 'mobile';
|
||||
$url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public function getUserInfo($params = [])
|
||||
{
|
||||
$params = $params ? $params : input();
|
||||
if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == session('state') && isset($params['code']))) {
|
||||
|
||||
//获取access_token
|
||||
$data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
|
||||
|
||||
$access_token = isset($data['access_token']) ? $data['access_token'] : '';
|
||||
$refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
|
||||
$expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
|
||||
if ($access_token) {
|
||||
$openid = isset($data['openid']) ? $data['openid'] : '';
|
||||
$unionid = isset($data['unionid']) ? $data['unionid'] : '';
|
||||
if (stripos($data['scope'], 'snsapi_login') !== false) {
|
||||
//获取用户信息
|
||||
$queryarr = [
|
||||
"access_token" => $access_token,
|
||||
"openid" => $openid,
|
||||
"lang" => 'zh_CN'
|
||||
];
|
||||
|
||||
$ret = $this->http->get(self::GET_USERINFO_URL.'?'.http_build_query($queryarr))->getBody()->getContents();
|
||||
$userinfo = (array)json_decode($ret, true);
|
||||
if (!$userinfo || isset($userinfo['errcode'])) {
|
||||
return [];
|
||||
}
|
||||
$userinfo = $userinfo ? $userinfo : [];
|
||||
$userinfo['avatar'] = isset($userinfo['headimgurl']) ? $userinfo['headimgurl'] : '';
|
||||
$userinfo['avatar'] = str_replace('http://','https://',$userinfo['avatar']);
|
||||
} else {
|
||||
$userinfo = [];
|
||||
}
|
||||
|
||||
$data = [
|
||||
'access_token' => $access_token,
|
||||
'refresh_token' => $refresh_token,
|
||||
'expires_in' => $expires_in,
|
||||
'openid' => $openid,
|
||||
'unionid' => $unionid,
|
||||
'userinfo' => $userinfo
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access_token
|
||||
* @param string code
|
||||
* @return array
|
||||
*/
|
||||
public function getAccessToken($code = '')
|
||||
{
|
||||
if (!$code) {
|
||||
return [];
|
||||
}
|
||||
$queryarr = array(
|
||||
"grant_type" => "authorization_code",
|
||||
"appid" => $this->config['app_id'],
|
||||
"secret" => $this->config['app_key'],
|
||||
"code" => $code,
|
||||
);
|
||||
|
||||
$response = $this->http->get(self::GET_ACCESS_TOKEN_URL.'?'.http_build_query($queryarr))->getBody()->getContents();
|
||||
$ret = (array)json_decode($response, true);
|
||||
return $ret ? $ret : [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user