first commit

This commit is contained in:
Mr.Qin
2022-08-19 19:48:37 +08:00
commit afdd648b65
3275 changed files with 631084 additions and 0 deletions

151
extend/system/File.php Normal file
View 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
View 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
View 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
View 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);
}
}
}

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

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

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

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

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

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

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

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

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

View 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
View 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'] : '';
}
}

View 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 : [];
}
}

View 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 : [];
}
}