This commit is contained in:
2024-07-10 23:52:13 +08:00
parent 9cc4d11f67
commit 10eb7d07df
258 changed files with 54986 additions and 2 deletions

Submodule digital_doctor/vendor/wanghua/general-utility-tools-php deleted from f31ce1f391

View File

@@ -0,0 +1,6 @@
.idea
.git
/.git
/.idea
/composer.lock

View File

@@ -0,0 +1,37 @@
# general_utility_tools_php
#### Description
php常用工具箱
general utility tool for php
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

View File

@@ -0,0 +1,142 @@
# general_utility_tools_php
#### 介绍
[PHP常用通用工具库]
一、常用验证类库
1. 验证手机号是否正确
2. 验证邮箱
3. 验证身份证号码
4. 是否字母或数字
5. 是否是小数格式
6. 更多功能请参考代码
二、PHP日期处理工具
1. 日期加上N年、月、日、时、分、秒得到计算后的时间
2. 两个日期相减得到天、时、分、秒(没有两个日期相加一说)
3. 更多更完善功能敬请期待......
三、更多类库参考本类库目录
1. 每个目录都有使用说明.
#### 软件架构
每一种工具独立为一个功能类库。
#### 软件要求
thinkphp5.0+PHP7.1+
#### 安装教程
composer require wanghua/general-utility-tools-php dev-master
###### 注意如果总是安装失败可切换国内或者国外源如果是找不到版本则加上dev-master尝试
###### 注意如果总是安装失败可卸载此包重新安装卸载指令composer remove wanghua/general-utility-tools-php
###### 注意:如果总是不能提交 vendor下面的一个文件夹(或文件)请参考这篇文章解决https://blog.csdn.net/qq_15941409/article/details/113184021
### 一、常用验证类库使用说明
##### 初始化
//静态方法无需实例化直接调用
##### 验证参数是否是小数格式
$str = '1.223';
var_dump(Validate::is_float_number($str));
##### 验证参数是否字母或数字
$str = 'qwer#199';
var_dump(Validate::is_letter_or_number($str));
###### 注: 更多功能请参考源码
### 二、PHP日期处理工具使用说明
##### 初始化
$date = (new Date());
$date->date_format = 'Y-m-d H:i:s';//设置格式默认Y-m-d H:i:s
##### 日期类型参数(第2个参数)可选项:
//分、时、天、周、月、年
protected $data_type = [
'm' =>'minute',//分钟
'minute' =>'minute',//分钟
'h' =>'hour',//小时
'hour' =>'hour',//小时
'd' =>'day',//天
'day' =>'day',//天
'w' =>'week',//周
'week' =>'week',//周
'M' =>'month',//月
'month' =>'month',//月
'y' =>'year',//年
'year' =>'year',//年
];
##### 在当前时间基础上加3天第三个参数不传默认使用当前时间
$res = $date->addTime(3, 'd');//支持单词和字母例如d表示天day也表示天
dump($res);
##### 在当前时间基础上加1小时第三个参数不传默认使用当前时间
$res = $date->addTime(1, 'h');//支持单词和字母例如h表示小时hour也表示小时
dump($res);
##### 在指定时间基础上加1小时
$res = $date->addTime(1, 'h', strtotime('2010-10-01 12:00:10'));
dump($res);
##### 在指定时间基础上减1个月 注意月是大写M字母分钟是小写m字母
$res = $date->reduceTime(1, 'M', strtotime('2010-10-01 12:00:10'));
dump($res);
##### 在指定时间基础上减20分钟 注意分钟是小写m字母
$res = $date->reduceTime(20, 'm', strtotime('2010-10-01 12:00:10'));
dump($res);
##### 在指定时间基础上减1年
$res = $date->reduceTime(1, 'y', strtotime('2010-10-01 12:00:10'));
dump($res);
### 日期时间相减
//时间相减返回的时间类型 秒、分、时、天 默认返回秒
protected $time_type = [
's' =>1,//秒
'second' =>1,//秒
'm' =>60,//分钟
'minute' =>60,//分钟
'h' =>3600,//小时
'hour' =>3600,//小时
'd' =>86400,//天
'day' =>86400,//天
];
$start_time = '2010-05-01 12:30:00';
$end_time = '2010-10-28 12:30:00';
//结束时间减去开始时间得到秒数 (返回类型参考上方配置)
$res = $date->timeReduceTime($end_time, $start_time);
dump($res);
//结束时间减去开始时间得到天数 (返回类型参考上方配置)
$res = $date->timeReduceTime($end_time, $start_time, 'day');
dump($res);
###### 注: 更多功能请参考源码
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

View File

@@ -0,0 +1,25 @@
{
"type": "library",
"name": "wanghua/general-utility-tools-php",
"homepage": "https://gitee.com/drop_drop/general_utility_tools_php.git",
"description": "general utility tools for thinkPHP",
"license": "Apache-2.0",
"version":"1.0.1",
"keywords": ["general-utility-tools","php tool","common tool"],
"authors": [
{
"name": "wanghua",
"email": "wanghua@qq.com",
"homepage": "https://blog.csdn.net/qq_15941409"
}
],
"minimum-stability": "stable",
"require": {
"ext-json": "*"
},
"autoload": {
"psr-4": {
"wanghua\\general_utility_tools_php\\": "src/"
}
}
}

View File

@@ -0,0 +1,396 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/10/27} {11:43}
*/
namespace wanghua\general_utility_tools_php;
/**
* 日期时间处理类
* Class Date
* @package libraries
*/
class Date
{
//时间默认返回格式
public $date_format = 'Y-m-d H:i:s';
//分、时、天、周、月、年
protected $data_type = [
'm' =>'minute',//分钟
'minute' =>'minute',//分钟
'h' =>'hour',//小时
'hour' =>'hour',//小时
'd' =>'day',//天
'day' =>'day',//天
'w' =>'week',//周
'week' =>'week',//周
'M' =>'month',//月
'month' =>'month',//月
'y' =>'year',//年
'year' =>'year',//年
];
//秒、分、时、天
protected $time_type = [
's' =>1,//秒
'second' =>1,//秒
'm' =>60,//分钟
'minute' =>60,//分钟
'h' =>3600,//小时
'hour' =>3600,//小时
'd' =>86400,//天
'day' =>86400,//天
];
/**
* desc日期时间加N
*
* 【注意】
* 请在计算月时使用$date->date_format = 'Y-m'防止跳月份
* 例如2020-03-31月基础上加一个月在默认$date->date_format='Y-m-d H:i:s'时会跳到2020-05-01
*
* authorwh
* @param int $times 相加的时间数量 整型
* @param string $date_type 要相加的时间类型 可选值m 分钟h 小时d 天w 周M 月y 年
* @param int $default_time 时间戳,默认当前时间
* @return false|string 返回$this->date_format格式可根据需要设定格式
*/
function addTime(int $times, string $date_type, int $default_time=0){
return date($this->date_format, strtotime("+{$times} {$this->data_type[$date_type]}", $default_time?$default_time:time()));
}
/**
* desc日期时间减N
*
* 【注意】
* 请在计算月时使用$date->date_format = 'Y-m'防止跳月份
* 例如2020-03-31月基础上加一个月在默认$date->date_format='Y-m-d H:i:s'时会跳到2020-05-01
*
* authorwh
* @param int $times 减去的时间数量 整型
* @param string $date_type 要相减的时间类型 可选值m 分钟h 小时d 天w 周M 月y 年
* @param int $default_time 时间戳,默认当前时间
* @return false|string 返回$this->date_format格式可根据需要设定格式
*/
function reduceTime(int $times, string $date_type, int $default_time=0){
return date($this->date_format, strtotime("-{$times} {$this->data_type[$date_type]}", $default_time?$default_time:time()));
}
/**
* desc日期时间相减通常结束时间大于开始时间
* authorwh
* @param string $end_time 结束时间
* @param string $start_time 开始时间
* @param string $return_type 日期时间相减后得到的时间类型可能是小数。可选值s 秒m 分钟h 小时d
* @return float|int 返回计算后的天、时、分、秒数
*/
function timeReduceTime(string $end_time, string $start_time, string $return_type='s'){
return (strtotime($end_time) - strtotime($start_time)) / $this->time_type[$return_type];
}
/**
* desc日期减日期, 返回月数或年数
*
* authorwh
* @param string $start_time 开始时间
* @param string $end_time 结束时间
* @param string $return_type M 返回一共有多少个月y 返回有多少个年
* @return float|int
*/
function dateCutDate(string $start_time, string $end_time, string $return_type='M'){
$e = date_create($end_time);
$s = date_create($start_time);
$diff = date_diff($e, $s);
//计算月份
if ($diff->y > 0) {
$m = $diff->y * 12 + $diff->m;
}else{
$m = $diff->m;
}
return $return_type=='M'?$m:$diff->y;
}
/**
* desc日期相减得到月数
* 注意:不是计算的时间戳,而是计算的月份差值
* authorwh
* @param string $start_time
* @param string $end_time
* @return false|float|int|string
*/
function dateCutMonth(string $start_time, string $end_time){
$start_y = date('Y', strtotime($start_time));
$end_y = date('Y', strtotime($end_time));
$start_m = date('m', strtotime($start_time));
$end_m = date('m', strtotime($end_time));
$ym = ($end_y-$start_y) * 12;
return $ym - $start_m + $end_m;
}
/**
* desc日期相减得到年数
* 注意:不是计算的时间戳,而是计算的年份差值
* authorwh
* @param string $start_time
* @param string $end_time
* @return false|float|int|string
*/
function dateCutYear(string $start_time, string $end_time){
$start_y = date('Y', strtotime($start_time));
$end_y = date('Y', strtotime($end_time));
return ($end_y-$start_y) * 12;
}
/**
* desc返回年中第几天
* authorwh
* @param string $month 指定月份
* @param string $day 指定日
* @param string $year 指定年份
* @return false|string
*/
function get_day_Year(string $month, string $day, string $year){
return date('z',mktime(0,0,0,$month,$day,$year));
}
/**
* desc今天的开始时间
* authorwh
* @return false|string
*/
function beginToday(){
return date('Y-m-d').' 00:00:00';
}
/**
* desc今天的结束时间
* authorwh
* @return false|string
*/
function endToday(){
return date('Y-m-d').' 23:59:59';
}
/**
* desc昨天的开始时间
* authorwh
* @return string
*/
function beginYesterday(){
return date('Y-m-d', strtotime('-1 day')).' 00:00:00';
}
/**
* desc昨天的结束时间
* authorwh
* @return string
*/
function endYesterday(){
return date('Y-m-d', strtotime('-1 day')).' 23:59:59';
}
/**
* desc前天的开始时间
* authorwh
* @return string
*/
function beginBeforeYesterday(){
return date('Y-m-d', strtotime('-2 day')).' 00:00:00';
}
/**
* desc前天的结束时间
* authorwh
* @return string
*/
function endBeforeYesterday(){
return date('Y-m-d', strtotime('-2 day')).' 23:59:59';
}
/**
* 本周的开始日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function beginWeek($His = true)
{
//此代码会跳周、月、年
//$timestamp = mktime(0, 0, 0, date('m'), date('d') - date('w') + 1, date('Y'));
//return $His ? date('Y-m-d H:i:s', $timestamp) : date('Y-m-d', $timestamp);
//改进
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
$first=1;
$sdefaultDate = date("Y-m-d");
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
$w=date('w',strtotime($sdefaultDate));
//是否展示时分秒
$week_start = date('Y-m-d',strtotime("$sdefaultDate -".($w ? $w - $first : 6).' days'));
//是否展示时分秒
if($His){
//$week_start
return $week_start.' 00:00:00';
}
//$week_start
return $week_start;
}
/**
* 本周的结束日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function endWeek($His = true)
{
//此代码会跳周、月、年
//$timestamp = mktime(23, 59, 59, date('m'), date('d') - date('w') + 7, date('Y'));
//return $His ? date('Y-m-d H:i:s', $timestamp) : date('Y-m-d', $timestamp);
//改进
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
$first=1;
$sdefaultDate = date("Y-m-d");
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
$w=date('w',strtotime($sdefaultDate));
//是否展示时分秒
$week_start = date('Y-m-d',strtotime("$sdefaultDate -".($w ? $w - $first : 6).' days'));
$week_end=date('Y-m-d',strtotime("$week_start +6 days"));
//是否展示时分秒
return $His?$week_end.' 23:59:59':$week_end;
}
/**
* desc上周开始
* authorwh
* @return false|string
*/
function beginBeforeWeek(){
$week = $this->beginWeek();
$this->date_format = 'Y-m-d';
return $this->reduceTime(7,'d',strtotime($week)) . ' 00:00:00';
//此代码会跳周、月、年
//return date('Y-m-d', strtotime('-1 monday', time())).' 00:00:00';//上周一,无论今天几号,-1 monday为上一个有效周未
}
/**
* desc上周结束
* authorwh
* @return false|string
*/
function endBeforeWeek(){
$week = $this->beginWeek();
$this->date_format = 'Y-m-d';
return $this->reduceTime(1,'d',strtotime($week)) . ' 23:59:59';
//此代码会跳周、月、年
//return date('Y-m-d', strtotime('-1 sunday', time())).' 23:59:59'; //上一个有效周日,同样适用于其它星期
}
/**
* 本月的开始日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function beginMonth($His = true)
{
return $His ? date('Y-m-').'01 00:00:00' : date('Y-m-').'01';
}
/**
* 本月的结束日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function endMonth($His = true)
{
$timestamp = mktime(23, 59, 59, date('m'), date('t'), date('Y'));
return $His ? date('Y-m-d H:i:s', $timestamp) : date('Y-m-d', $timestamp);
}
/**
* desc上月开始时间上月一日
* authorwh
* @return false|string
*/
function beginBeforeMonth(){
return date('Y-m-d', strtotime('-1 month', strtotime(date('Y-m', time()) . '-01 00:00:00'))).' 00:00:00'; //本月一日直接strtotime上减一个月
}
/**
* desc上月结束时间上月最后一日
* authorwh
* @return false|string
*/
function endBeforeMonth(){
return date('Y-m-d', strtotime(date('Y-m', time()) . '-01 00:00:00') - 86400).' 23:59:59'; //本月一日减一天即是上月最后一日
}
/**
* desc七天(一周)以内
* authorwh
* @return false|string
*/
function innerWeekDay(){
$time = time();
//当前时间减去30天
$second = $time - 7 * 86400;
return date('Y-m-d', $second);
}
/**
* desc一月以内
* authorwh
* @return false|string
*/
function innerMonth(){
$time = time();
//当前时间减去30天
$second = $time - 30 * 86400;
return date('Y-m-d', $second);
}
/**
* desc一年以内
* authorwh
* @return false|string
*/
function innerYear(){
$time = time();
//当前时间减去30天
$second = $time - 365 * 86400;
return date('Y-m-d', $second);
}
/**
* 几年的开始日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function beginYear($His = true)
{
$timestamp = date('Y');
return $His ? $timestamp.'-01-01 00:00:00' : $timestamp.'-01-01';
}
/**
* 几年的结束日期
*
* @param bool $His 是否展示时分秒 默认true
*
* @return false|string
*/
function endYear($His = true)
{
$timestamp = date('Y');
return $His ? $timestamp.'-12-31 23:59:59' : $timestamp.'-12-31';
}
}

View File

@@ -0,0 +1,125 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/5/1} {21:28}
*/
namespace wanghua\general_utility_tools_php;
use think\Db;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* thinkphp5模型快捷方法库
* Class Mmodel
* @package wanghua\general_utility_tools_php
*/
class Mmodel
{
/**
* desc存在则更新否则插入
* authorwh
* @param string $table 表名
* @param array $where 更新条件(只在更新时有效)
* @param array $data 更新或写入数据
* @return int 数据主键id
*/
static function existsUpdateInsert($table,$where,$data){
if(empty($where)){
return Db::table($table)->insertGetId($data);
}
$res = Db::table($table)
->where($where)
->find();
if($res){
Db::table($table)
->where($where)
->data($data)
->update();
return $res['id'];
}
return Db::table($table)->insertGetId($data);
}
/**
* desc捕获式函数块
*
* 无事务处理
*
* authorwh
* @param $fn
*/
static function catch($fn){
try{
Tools::log_to_write_txt(['input'=>input()]);
return $fn();
}catch (\Exception $e){
Tools::error_txt_log($e);
return Tools::set_fail('操作失败.',$e->getMessage());
}
}
/**
* desc捕获式函数块
*
* 无事务处理
*
* authorwh
* @param $fn
*/
static function catchJson($fn){
try{
Tools::log_to_write_txt(['input'=>input()]);
$res = $fn();
return json($res);
}catch (\Exception $e){
Tools::error_txt_log($e);
return json(Tools::set_fail('操作失败.',$e->getMessage()));
}
}
/**
* desc捕获式函数块
*
* 自动事务处理
*
* authorwh
* @param $fn
*/
static function catchTrans($fn){
Db::startTrans();
try{
Tools::log_to_write_txt(['input'=>input()]);
$res = $fn();
Db::commit();
return $res;
}catch (\Exception $e){
Db::rollback();
Tools::error_txt_log($e);
return Tools::set_fail('操作失败.',$e->getMessage());
}
}
/**
* desc捕获式函数块
*
* 自动事务处理
*
* authorwh
* @param $fn
*/
static function catchTransJson($fn){
Db::startTrans();
try{
Tools::log_to_write_txt(['input'=>input()]);
$res = $fn();
Db::commit();
return json($res);
}catch (\Exception $e){
Db::rollback();
Tools::error_txt_log($e);
return json(Tools::set_fail('操作失败.',$e->getMessage()));
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,50 @@
<?php
/*
* description
* authorwh
* email
* createTime{2023/12/31} {21:56}
*/
namespace wanghua\general_utility_tools_php;
use app\common\model\TabConf;
use think\Db;
/**
* 系统杂项配置
*
* 读取fa_sundry_config表中的配置
*
* Class SundryConfig
* @package wanghua\general_utility_tools_php
*/
class SundryConfig
{
private static $tablename = 'fa_zc_sundry_config';
/**
* desc获取配置的值设置配置的值
*
* 默认缓存值
*
* authorwh
* @param string $key
* @param string $val
* @return float|mixed|string
*/
static function val(string $key,string $val=''){
$tabname = self::$tablename;
if($val){
$obj = Db::table($tabname);
$obj->where('key',$key);
$obj->data(['val'=>$val]);
return $obj->update();
}
$obj = Db::table($tabname);
return $obj->where(['key'=>$key])
->cache($tabname.'_'.$key,0,$tabname)
->value('val');
}
}

View File

@@ -0,0 +1,360 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/11/1} {15:09}
*/
namespace wanghua\general_utility_tools_php;
class Validate
{
/**
* desc验证手机号是否正确
* authorwh
* @param $m
* @return bool
*/
static function is_mobile($m) {
return preg_match('/^1[3,4,5,6,7,8,9]{1}\d{9}$/', $m) ? true : false;
}
/**
* 验证邮箱
* authorwh
* @param $email
* @return bool
*/
static function is_email($email){
return preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/",$email)?true:false;
}
/**
* [is_IDCard 验证身份证号码]
* @Author
* @Date 2019-11-26
* @param [string] $string [身份证号]
* @return boolean [description]
*/
static function is_IDCard($string){
return preg_match("/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/", $string)?true:false;
}
/**
* [is_money_times 验证金额是否是100的整数倍]
* @Author
* @Date 2019-12-12
* @param [type] $money [金额]
* @return boolean [description]
*/
static function is_money_times($money){
if(!is_numeric($money) || $money <= 0){
return false;
}
return $money%100===0;
}
/**
* desc是否是金额
* authorwh
* @param $money
* @return bool
*/
static function is_money($money){
return (preg_match('/^[0-9]+.[0-9]+$/', $money) || preg_match('/^\d+$/', $money))?true:false;
}
/**
* desc是否是金额并且最多只有两位小数
*
* 严格金额格式校验
*
* authorwh
* @param $money
* @return bool
*/
static function is_money_double($money){
$r = (preg_match('/^[0-9]+.[0-9]+$/', $money) || preg_match('/^\d+$/', $money))?true:false;
$exp = explode('.',$money);
if(strpos($money,'.') && preg_match('/^[0-9]+.[0-9]+$/', $money) && strlen($exp[1])>2){
//返回false表示非严格的2位数金额
return false;
}
return $r;
}
/**
* desc是否是小数格式
* authorwh
* @param string $num
* @return bool
*/
static function is_float_number(string $num){
return preg_match('/^[0-9]+.[0-9]+$/', $num)?true:false;
}
/**
* desc是否是数字
* authorwh
* @param string $num
* @return bool
*/
static function is_number(string $num){
return preg_match('/^\d+$/', $num)?true:false;
}
/**
* desc是否是字母
* author wh
* @param string $str
* @return bool
*/
static function is_letter(string $str){
return preg_match('/^[a-zA-Z]+$/', $str) ? true : false;
}
/**
* desc是否字母或数字
* authorwh
* @param string $str
* @return bool
*/
static function is_letter_or_number(string $str){
return preg_match('/^[a-zA-Z|0-9]+$/', $str) ? true : false;
}
/**
* desc验证数组是否存在空值
* 仅针对基本数据类型
* 仅针对一维数组
* authorwh
* @param $array
* @return bool
*/
static function check_array_val_empty($array){
$is_empty = false;
foreach ($array as $value){
if(($value!==0 && $value !=='0') && empty($value)){
$is_empty = true;
break;
}
if(is_int($value) && empty(1*$value)){
$is_empty = true;
break;
}
}
return $is_empty;
}
/**
* desc是否是url
* authorwh
* @param $v
* @return bool
*/
static function is_url($v){
$pattern="#(http|https)://(.*\.)?.*\..*#i";
return preg_match($pattern,$v)?true:false;
}
/**
* [是否全部大写]
*
* @param $str
* @return bool
* @example
* @see
* @link
*/
static function is_upper($str){
return preg_match('/^[A-Z]+$/', $str)?true:false;
}
/**
* [是否全部小写]
*
* @param $str
* @return bool
* @example
* @see
* @link
*/
static function is_lower($str){
return preg_match('/^[a-z]+$/', $str)?true:false;
}
/**
* desc验证是微信内还是微信外
*
* 是否在微信内,是否在微信中
*
* authorwh
* @return bool
*/
static function is_weixin(){
return strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false;
}
/**
* desc是否包含特殊字符
* authorwh
* @param string $str
* @return bool
*/
static function is_special_character(string $str){
$res = preg_match ( '/[\Q~!@#$%^&*()+-_=.:?<>\E]/', $str );
return $res ? true : false;
}
/**
* desc验证字符串是否全部是中文
*
* 返回 true表示全部是中文false表示部分是中文或没有中文
*
* authorwh
* @param string $str
* @return bool
*/
static function is_all_chinese(string $str){
return preg_match("/^[\x{4e00}-\x{9fa5}]+$/u",$str)?true:false;
}
/**
* desc验证两个浮点数值是否相等
*
* 例如:
* $num1 = 0.1;
$num2 = 0.7;
$res = $num1 + $num2;
var_dump($res);
var_dump($res == 0.8);//false 不相等
var_dump(intval(strval($res)) == intval(strval(0.8)));//true 相等
*
* authorwh
* @param $float_num1
* @param $float_num2
* @return bool
*/
static function is_equal_num_val($float_num1, $float_num2){
return intval(strval($float_num1)) == intval(strval($float_num2));
}
/**
* desc是否包含给定的主域名
* authorwh
* @param $domain
* @param $main_domain
* @return bool
*/
static function is_main_domain($domain,$main_domain){
$exp_arr = explode('.',$domain);
$str = $exp_arr[count($exp_arr)-2] .'.'. $exp_arr[count($exp_arr)-1];
return $str==$main_domain;
}
/**
* desc国内国外IP校验校验IP来源
*
* authorwh
* @param $ip
* @return bool
*/
static function validate_ip($ip) {
$chinaStart = ip2long('1.0.1.0'); // 中国大陆起始IP
$chinaEnd = ip2long('254.254.254.254'); // 中国大陆结束IP
$hongKongStart = ip2long('8.36.0.0'); // 香港起始IP
$hongKongEnd = ip2long('8.37.255.255'); // 香港结束IP
$taiwanStart = ip2long('192.168.0.0'); // 台湾起始IP
$taiwanEnd = ip2long('192.168.255.255'); // 台湾结束IP
$ipLong = ip2long($ip);
if ($ipLong >= $chinaStart && $ipLong <= $chinaEnd ||
$ipLong >= $hongKongStart && $ipLong <= $hongKongEnd ||
$ipLong >= $taiwanStart && $ipLong <= $taiwanEnd) {
return true; // IP属于国内
} else {
return false; // IP不属于国内
}
}
/**
* 是否移动端访问访问
*
* @return bool
*/
static function is_mobile_client()
{
// 如果有HTTP_X_WAP_PROFILE则一定是移动设备
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
return true;
}
//如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
if (isset($_SERVER['HTTP_VIA'])) {
//找不到为flase,否则为true
return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
}
//判断手机发送的客户端标志
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$clientkeywords = [
'nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp',
'sie-', 'philips', 'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu',
'android', 'netfront', 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi',
'openwave', 'nexusone', 'cldc', 'midp', 'wap', 'mobile','alipay'
];
// 从HTTP_USER_AGENT中查找手机浏览器的关键字
if (preg_match("/(".implode('|', $clientkeywords).")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
return true;
}
}
//协议法,因为有可能不准确,放到最后判断
if (isset($_SERVER['HTTP_ACCEPT'])) {
// 如果只支持wml并且不支持html那一定是移动设备
// 如果支持wml和html但是wml在html之前则是移动设备
if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
return true;
}
}
return false;
}
/**
* 判断是否微信内置浏览器访问
* @return bool
*/
static function is_wx_client()
{
return strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false;
}
/**
* 判断是否支付宝内置浏览器访问
* @return bool
*/
static function is_ali_client()
{
return strpos($_SERVER['HTTP_USER_AGENT'], 'Alipay') !== false;
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/24} {10:20}
*/
namespace wanghua\general_utility_tools_php\alipay;
use app\index\model\AppOrderPayReqRecordModel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 调起支付宝支付
*
* Class AlipayLogic
*
* 依赖:
* composer require wanghua/general-utility-tools-php dev-master
* @deprecated
*/
class AlipayLogic
{
/**
* desc调起支付
* authorwh
* @return false|mixed|\SimpleXMLElement|string|\提交表单HTML文本|null
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
* @throws \think\exception\PDOException
*/
//function pay(string $notify_url,string $return_url=''){
///* *
// * 功能:支付宝手机网站支付接口(alipay.trade.wap.pay)接口调试入口页面
// * 版本2.0
// * 修改日期2016-11-01
// * 说明:
// * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
// 请确保项目文件有可写权限,不然打印不了日志。
// */
//
//header("Content-type: text/html; charset=utf-8");
//
//
//require_once Tools::get_root_path().'library/AlipayTradeWapPay/wappay/service/AlipayTradeService.php';
//require_once Tools::get_root_path().'library/AlipayTradeWapPay/wappay/buildermodel/AlipayTradeWapPayContentBuilder.php';
////require dirname ( __FILE__ ).DIRECTORY_SEPARATOR.'./../config.php';
//$config = config('pay_config.alipay');
//if(empty($_POST['WIDout_trade_no']) || trim($_POST['WIDout_trade_no'])=="") return null;
////商户订单号,商户网站订单系统中唯一订单号,必填
//$out_trade_no = input('WIDout_trade_no');
//if(empty($out_trade_no)){
// return '参数错误';
//}
//$order = AppOrderPayReqRecordModel::getOrder($out_trade_no);
//if(empty($order)){
// return '订单错误';
//}
////超时时间
//$timeout_express="1m";
//
//$payRequestBuilder = new \AlipayTradeWapPayContentBuilder();
//$payRequestBuilder->setBody($order['goods_name']);
//$payRequestBuilder->setSubject($order['goods_name']);
//$payRequestBuilder->setOutTradeNo($out_trade_no);
//$payRequestBuilder->setTotalAmount($order['goods_price']);
//$payRequestBuilder->setTimeExpress($timeout_express);
//
//$payResponse = new \AlipayTradeService($config);
//$result = $payResponse->wapPay($payRequestBuilder,$return_url,$notify_url);
//return $result;
//}
}

View File

@@ -0,0 +1,195 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/10} {23:15}
*/
namespace wanghua\general_utility_tools_php\alipay\pay;
use Alipay\EasySDK\Kernel\Config;
use Alipay\EasySDK\Kernel\Factory;
use app\common\model\TabConf;
use app\common\tools\EmailTool;
use app\index\logic\AlipayBarcodePayLogic;
use app\index\logic\CheckstandLogic;
use app\index\model\OrderModel;
use app\index\model\PartnerAccountModel;
use think\Db;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 支付宝面对面扫码支付(用户出示付款码)
*
* 【扫码支付专用】
*
* 依赖:"alipaysdk/easysdk": "^2.2" || composer require alipaysdk/easysdk
*
* Class AlipayScanPay
* @package wanghua\general_utility_tools_php\alipay\transfer
*/
class AlipayScanPay
{
/**
* 支付参数配置
* @var array
*/
private array $config = [];
function __construct($config)
{
/** 设置支付参数 */
$this->config = $config;
}
/**
* desc面对面扫码支付-(扫用户的付款码)
*
* 注意:
* 免密支付是同步返回支付结果,输入密码支付是异步通知支付结果,
* 所以需要处理异步支付结果通知。
* @param array $order 订单信息
* @param string $auth_code 扫码枪扫描后的授权码
* @return array 返回支付结果(一维数组)
*/
function scanPay($order,$auth_code){
if(empty($auth_code)){
return Tools::set_res(500,'授权码错误');
}
try {
//1. 设置参数(全局只需设置一次)
Factory::setOptions($this->getOptions());
//2. 发起API调用以支付能力下的统一收单交易创建接口为例
$result = Factory::payment()->faceToFace()
->optional('scene','bar_code')
->pay($order['title'], $order['orderid'], $order['real_amount'], $auth_code);
if($result->code == 10003){
return Tools::set_res(230,'交易进行中(提示用户输入密码)'.$result->msg);
}
//必须解析json
$resp = json_decode($result->httpBody, true);
//支付宝面对面扫码支付后续流程
return Tools::set_ok('ok',$resp['alipay_trade_pay_response']);//返回支付结果(一维数组)
} catch (\Exception $e) {
$err_data = [
'error'=>'支付宝付款码支付错误'.$e->getMessage(),
'error_info'=>$e->getTraceAsString()
];
Tools::log_to_write_txt($err_data);
return Tools::set_fail('错误.'.$e->getMessage());
}
}
/**
* desc获取支付配置
* authorwh
* @return Config
* @throws Exception
*/
private function getOptions()
{
if(empty($this->config)){
throw new Exception('支付宝配置错误');
}
if(empty($this->config['notifyUrl'])){
throw new Exception('支付宝配置错误notifyUrl通知地址字段必须配置免密支付是同步返回支付结果输入密码支付是异步通知支付结果');
}
$config = $this->config;
$options = new Config();
$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipay.com';
$options->signType = $config['sign_type'];
$options->appId = $config['app_id'];//'<-- 请填写您的AppId例如2019022663440152 -->';
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
$options->merchantPrivateKey = $config['alipay_app_private_key'];//'<-- 请填写您的应用私钥例如MIIEvQIBADANB ... ... -->';
$options->alipayCertPath = Tools::get_root_path().$config['cert_path_alipayCertPublicKey_RSA2'];//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$options->alipayRootCertPath = Tools::get_root_path().$config['cert_path_alipayRootCert'];//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$options->merchantCertPath = Tools::get_root_path().$config['appCertPublicKey'];//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->';
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
// $options->alipayPublicKey = '<-- 请填写您的支付宝公钥例如MIIBIjANBg... -->';
//可设置异步通知接收服务地址(可选)
$options->notifyUrl = $this->config['notifyUrl'];//request()->domain().'/index/alipay/notify';//"<-- 请填写您的支付类接口异步通知接收服务地址例如https://www.test.com/callback -->";
return $options;
}
/**
* desc支付宝扫码异步通知
*
* 【通知结果是一维数组】
*
* {"gmt_create":"2024-06-10 11:53:37","charset":"UTF-8","seller_email":"15023017325",
* "subject":"购物消费",
* "sign":"oCR3X0ViRrydigLMeOCTrMBNx7O8bSZnEflT0qZYo0gXk2jTI2OVB9mG2UPCkGuSND3wq/pMRFN
* ljuz+2r3KMha9a6ArD/ey8w1am3yCKI+FMqUMQSv9TJqfpWdAsc8B45egtr+txBBOA1NMfN41hPnTxM3QzQ82cm
* 7VYw6pnYpxJVj/3AsqYxY+wcyMq5+v25eC4TgBo+YJ9pfb3rqaAV91cJBoHRq1Cwh0XEnOzm9zpv7b5cu3r+V0oeW
* wTCCi8S1TXW2dfE4H8o63xgASYfJO2fgGIu19YICQdUONkDhu9Tfbf3fFf9SHw3RIw8zrQRqkwAXC32NwfUv4Ru5NqQ==",
* "buyer_id":"2088612757425274","invoice_amount":"3258.60","notify_id":"2024061001222115350025271439748729",
* "fund_bill_list":"[{\"amount\":\"3258.60\",\"fundChannel\":\"ALIPAYACCOUNT\"}]",
* "notify_type":"trade_status_sync","trade_status":"TRADE_SUCCESS","receipt_amount":"3258.60",
* "app_id":"2021004136614333","buyer_pay_amount":"3258.60","sign_type":"RSA2",
* "seller_id":"2088642975857443","gmt_payment":"2024-06-10 11:53:50",
* "notify_time":"2024-06-10 12:07:40","version":"1.0","out_trade_no":"cs25obpv1717991615660",
* "total_amount":"3258.60","trade_no":"2024061022001425271426453509","auth_app_id":"2021004136614333",
* "buyer_logon_id":"146***@qq.com","point_amount":"0.00"}
*
* authorwh
* param function $fn 传入订单处理逻辑函数 要求返回success、fail
* @return string
*/
function scanPayNotify($fn){
Tools::log_to_write_txt([
'支付宝异步通知',
'input'=>input()
]);
$out_trade_no = input('out_trade_no');
if(empty($out_trade_no)){
Tools::log_to_write_txt(['error'=>'out_trade_no为空,文件:'.__FILE__.',line:'.__LINE__]);
return '';
}
//$order = Db::table(TabConf::$fa_order)
// ->where('orderid',$out_trade_no)
// ->find();
//if(empty($order)){
// Tools::log_to_write_txt(['error'=>'订单不存在,文件:'.__FILE__.',line:'.__LINE__]);
// return '';
//}
//该字段用于判断成功、失败
$trade_status = input('trade_status');
if(empty($trade_status)){
Tools::log_to_write_txt(['error'=>'trade_status为空,文件:'.__FILE__.',line:'.__LINE__]);
return '';
}
if(empty($this->config['seller_id'])){
Tools::log_to_write_txt(['error'=>'卖家seller_id为空,文件:'.__FILE__.',line:'.__LINE__]);
return '';
}
//判断来源
$seller_id = input('seller_id');
if($seller_id != $this->config['seller_id']){
Tools::log_to_write_txt(['error'=>'支付通知来源错误','seller_id'=>$seller_id,'config'=>$this->config['seller_id']]);
return 'fail';
}
//支付宝面对面扫码支付后续流程
$result = input();
//异步通知
//$res = CheckstandLogic::alipayScanSuccessFlow($result,$order);
//if($res['code'] == 200 || $res['code'] == 230){//成功或进行中
// return 'success';
//}
return $fn($result);//要求返回success、fail
}
}

View File

@@ -0,0 +1,143 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/09/24} {19:43}
*/
namespace wanghua\general_utility_tools_php\alipay\transfer;
use Alipay\EasySDK\Kernel\Config;
use Alipay\EasySDK\Kernel\Factory;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 支付宝转账(独立且区别于支付,连参数都不能用支付参数)
*
* 注:换句话说,转账是转账的参数,支付是支付的参数,不能混为一谈(以此备注长记性)。
*
* 环境要求PHP7.0+ThinkPHP5.0+
*
* 依赖:
* composer require alipaysdk/easysdk:^2.0
*
* composer require wanghua/general-utility-tools-php dev-master
*
* 重要说明:
*
* 支付宝开放平台可能有多个应用,可能存在一个默认应用(应用名称:应用2.0签约2020102692466480),转账配置就用此应用的参数,而不是其它应用的配置!
*
*/
class AlipayTransfer
{
/**
* desc转账到支付宝证书模式
* authorwh
* @param array $alipayConfig
* @param $transferConfig TransferConfig.php
* @return mixed
*
* 使用案例:
*
function transfer(array $trans_data){
$alipayConfig = \config('pay_config.alipay_transfer');//转账配置
$trans_config = new TransferConfig();
$trans_config->cert_path_alipayCertPublicKey_RSA2 = Tools::get_root_path().$alipayConfig['cert_path_alipayCertPublicKey_RSA2'];//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$trans_config->cert_path_alipayRootCert = Tools::get_root_path().$alipayConfig['cert_path_alipayRootCert'];//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$trans_config->appCertPublicKey = Tools::get_root_path().$alipayConfig['appCertPublicKey'];//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->'
$order_prefix = 'trs';
$order_id = Tools::to_create_order_no($order_prefix);
$trans_config->order_id = $order_id;//订单号 必须
$trans_config->timestamp = Tools::get_now_date();//请求时间 eg2020-01-08 10:12:50
$trans_config->trans_amount = $trans_data['trans_amount'];//转账金额 必须 且为字符串 最低0.1元 取值范围[0.1,100000000]
$trans_config->order_title = $trans_data['order_title'];//订单标题 可选
$trans_config->phone = $trans_data['phone'];//支付宝登录手机号 必须
$trans_config->name = $trans_data['name'];//支付宝真实姓名 必须
$trans_config->remark = $trans_data['remark'];//支付备注 可选
$trans = new AlipayTransfer();
Tools::log_to_write_txt(['转账到支付宝,入参:'=>input(), 'trans_config'=>$trans_config]);
$pay_res = $trans->alitransfer($alipayConfig, $trans_config);
Tools::log_to_write_txt(['转账到支付宝,出参:'=>$pay_res]);
//直接返回转账结果自主验证成功失败的status。code=10000表示操作成功status=SUCCESS表示转账成功
//由于是同步响应,所以不需要对结果验签
return $pay_res;
}
*
*/
function alitransfer(array $alipayConfig, $transferConfig){
//$transferConfig = new TransferConfig();
Factory::setOptions($this->getAlipayOptions($alipayConfig, $transferConfig));
$method = 'alipay.fund.trans.uni.transfer';
//公共参数
$textParams = [
'app_id'=>$alipayConfig['app_id'],
'charset'=>'utf-8',
'sign_type'=>'RSA2',
'timestamp'=>$transferConfig->timestamp,
'version'=>'1.0',
];
//请求参数
//订单号前缀
$biz_content = [
'out_biz_no'=>$transferConfig->order_id,//商家支付订单号
'trans_amount'=>$transferConfig->trans_amount,
'product_code'=>'TRANS_ACCOUNT_NO_PWD',
'biz_scene'=>'DIRECT_TRANSFER',
'order_title'=>$transferConfig->order_title,
'payee_info'=>[
'identity'=>$transferConfig->phone,//手机号
'identity_type'=>'ALIPAY_LOGON_ID',
'name'=>$transferConfig->name,//参与方真实姓名如果非空将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时本字段必填。
],
'remark'=>$transferConfig->remark,
];
$pay_res = Factory::util()->generic()->execute($method,$textParams,$biz_content);
return $pay_res;
}
/**
* desc证书模式参数非证书模式参数需单独获取
* authorwh
* @param array $alipayConfig 支付宝转账配置(非支付配置)
* @return Config
*/
protected function getAlipayOptions(array $alipayConfig, $transferConfig)
{
$options = new Config();
$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipay.com';
$options->signType = 'RSA2';
//'<-- 请填写您的AppId例如2019022663440152 -->'
$options->appId = $alipayConfig['app_id'];//'2021002103639985';
// 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
$options->merchantPrivateKey = $alipayConfig['alipay_app_private_key'];//'<-- 请填写您的应用私钥例如MIIEvQIBADANB ... ... -->';
$options->alipayCertPath = $transferConfig->cert_path_alipayCertPublicKey_RSA2;//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$options->alipayRootCertPath = $transferConfig->cert_path_alipayRootCert;//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$options->merchantCertPath = $transferConfig->appCertPublicKey;//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->';
//注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
// $options->alipayPublicKey = $alipayConfig['alipay_public_key'];//'<-- 请填写您的支付宝公钥例如MIIBIjANBg... -->';
//可设置异步通知接收服务地址(可选)
$options->notifyUrl = $transferConfig->notifyUrl;//"<-- 请填写您的支付类接口异步通知接收服务地址例如https://www.test.com/callback -->";
//可设置AES密钥调用AES加解密相关接口时需要可选
//$options->encryptKey = "<-- 请填写您的AES密钥例如aa4BtZ4tspm2wnXLb1ThQA== -->";
return $options;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/09/24} {20:07}
*/
namespace wanghua\general_utility_tools_php\alipay\transfer;
/**
* 调用转账之前请配置此参数
*
* 配置示例:
*
$alipayConfig = \config('pay_config.alipay_transfer');//转账配置
$trans_config = new TransferConfig();
$trans_config->cert_path_alipayCertPublicKey_RSA2 = Tools::get_root_path().$alipayConfig['cert_path_alipayCertPublicKey_RSA2'];//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
$trans_config->cert_path_alipayRootCert = Tools::get_root_path().$alipayConfig['cert_path_alipayRootCert'];//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
$trans_config->appCertPublicKey = Tools::get_root_path().$alipayConfig['appCertPublicKey'];//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->'
$order_prefix = 'trs';
$order_id = Tools::to_create_order_no($order_prefix);
$trans_config->order_id = $order_id;//订单号 必须
$trans_config->timestamp = Tools::get_now_date();//请求时间 eg2020-01-08 10:12:50
$trans_config->trans_amount = '0.21';//转账金额 必须 且为字符串 最低0.1元 取值范围[0.1,100000000]
$trans_config->order_title = '代理提现';//订单标题 可选
$trans_config->phone = '18290416033';//支付宝登录手机号 必须
$trans_config->name = '王华';//支付宝真实姓名 必须
$trans_config->remark = '代理提现到支付宝';//支付备注 可选
$trans = new AlipayTransfer();
$pay_res = $trans->alitransfer($alipayConfig, $trans_config);
dump($pay_res);
*
*/
class TransferConfig
{
//'<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->';
public $cert_path_alipayCertPublicKey_RSA2 = '';//必须
//'<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt" -->';
public $cert_path_alipayRootCert = '';//必须
//'<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->'
public $appCertPublicKey = '';//必须
public $notifyUrl = '';//接收异步通知(可选)
//业务参数
public $order_id = '';//订单号 必须
public $timestamp = '';//请求时间 必须 eg2020-01-08 10:12:50
public $trans_amount = '';//转账金额 必须 且为字符串 最低0.1元 取值范围[0.1,100000000]
public $order_title = '';//订单标题 可选
public $phone = '';//支付宝登录手机号 必须
public $name = '';//支付宝真实姓名 必须
public $remark = '';//支付备注 可选
}

View File

@@ -0,0 +1,425 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {11:21}
*/
namespace wanghua\general_utility_tools_php\api;
use wanghua\general_utility_tools_php\mysql\lib\Table;
use wanghua\general_utility_tools_php\tool\Tools;
class Api extends BaseLibApi
{
private $apidir = '';
private $namespace = 'app\api\controller';
private $table_obj = null;
//一维数组
private $functions_arr = [];//要创建的初始方法
public function __construct()
{
//默认api目录
$this->apidir = Tools::get_root_path().'application/api/controller/';
if(!$this->table_obj)
$this->table_obj = new Table();
}
/**
* descapi目录
* authorwh
* @param string $dir api创建目录格式必须按照例子填写 eg: application/api/controller/
*/
function setapidir(string $dir){
$this->apidir = $dir;
$nsp = str_replace('application','app',substr($dir,0,strlen($dir)-2));
$this->namespace = str_replace('/','\\',$nsp);
}
/**
* desc替换表前缀
* authorwh
* @param $tablename
* @return false|string
*/
function replaceTablePrefix($tablename){
$prefix = config('database.prefix');
if($prefix){
return substr($tablename,strlen($prefix));
}
$tmp_prefix = 't_';
if(false !== strpos($tablename,$tmp_prefix)){
return substr($tablename,strlen($tmp_prefix));
}
return $tablename;
}
/**
* desc设置方法名称
*
* authorwh
* @param array $functions_arr
*/
function setApiFunctionsName(array $functions_arr){
$this->functions_arr = array_merge($functions_arr,$this->functions_arr);
}
/**
* desc动态创建方法
*
* authorwh
*/
private function syncCreateFunction(array $func_name_arr){
$code = '';
foreach ($func_name_arr as $func_name=>$func_remark){ //接口方法公共代码模块
$commonFunctionCodeModule = $this->commonFunctionCodeModule($func_remark);
$code .= <<<EOF
/**
* desc{$func_remark}
*
* author
*/
function {$func_name}(){
{$commonFunctionCodeModule}
}
EOF;
}
return $code;
}
/**
* desc创建查询接口的代码体
*
* 本接口只针对简单查询逻辑
*
* and
* like%%like%,
* >, <, >=, <=, !=
* between
* in
*
* authorwh
* @param array $select_in_params eg:
* [
* 'gameid'=>['and'],
* 'nickname'=>['like%'],
* 'reg_time'=>['between'],//时间区间查询
* 'audit_time'=>['2015-01-3','eq'],//时间区间查询
* ]
*
*/
function setQueryCodeBody(array $select_in_params){
$code = '';
return $code;
}
/**
* desc写入查询接口的代码体
*
* authorwh
* @param array $insert_params
* @return string
*/
function setInsertCodeBody(array $insert_params){
$code = '';
return $code;
}
/**
* desc快速构建api代码
*
* 本方法不验证接口名称是否重复
*
* authorwh
* @param string $tablename 数据表名称
* @param array $func_name_arr 方法名称数组 eg:['getData'=>'获取数据','function_aaa'=>'function_注释aaa']
*/
function buildApi(string $tablename, array $func_name_arr){
//deal table name
$tablename = $this->replaceTablePrefix($tablename);
//基础 start
//create base controller
$this->buildBaseApiPublicController();//开放控制器
$this->buildBaseApiAuthController();//权限控制器
$this->errController();//错误控制器
//基础 end
//接口类名
$classname = ucfirst(Tools::convertUnderLine($tablename));
//接口注释
$comment = $this->table_obj->getTableComment($tablename);
//批量构建方法代码
$build_functions_code = $this->syncCreateFunction($func_name_arr);
//PHP文件名不含物理路径
$file_name = $classname.$this->ext();
//PHP文件路径和文件名
$php_file = $this->apidir.$file_name;
if(file_exists($php_file)){
//追加代码
//读取并删除最后的“}”
$php_code_str = file_get_contents($php_file);
$sub_php_code_str = mb_substr($php_code_str,0,mb_strlen($php_code_str)-2);
//追加新的代码
$sub_php_code_str .= $build_functions_code ."\n }";
//清空代码
file_put_contents($php_file,'');
//追加
$this->createFileExists($php_file,$sub_php_code_str);
}else{
//创建代码文件
$use_tpl = $this->useTpl();
$php = <<<EOF
<?php
namespace {$this->namespace};
{$use_tpl}
/**
* {$comment}
* Class {$classname}
* @package {$this->namespace}
*/
class {$classname} extends BaseApiPublicController
{
{$build_functions_code}
}
EOF;
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
}
/**
* desc公共方法模块
*
* authorwh
* @param $comment
* @return string
*/
private function commonFunctionCodeModule($comment){
$php = <<<EOF
try {
Tools::log_to_write_txt(['title'=>'{$comment},__IN','INPUT'=>input()]);
//region 校验区 start
//endregion 校验区 end
//region 业务逻辑区 start
//endregion 业务逻辑区 end
//响应结果处理
Tools::log_to_write_txt(['title'=>'{$comment},__OUT','OUTPUT'=>[]]);
return json(Tools::set_res(200,'ok'));
}catch (\Exception \$e){
Tools::log_to_write_txt([
'error'=>'{$comment},API异常。'.\$e->getMessage(),
'入参'=>input(),
'error_info'=>\$e->getTraceAsString()
]);
return json(Tools::set_res(500,'系统繁忙'));
}
EOF;
return $php;
}
/**
* descapi基础开放控制器
*
* 如果存在则不创建
*
* authorwh
*/
private function buildBaseApiPublicController(){
$file_name = 'BaseApiPublicController';
$php = <<<EOF
<?php
namespace {$this->namespace};
use think\Controller;
/**
* descapi基础开放架构
*
* 所有开放接口继承此父类
*
* author
*/
class {$file_name} extends Controller
{
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
/**
* descapi基础权限控制器
*
* 如果存在则不创建
*
* authorwh
*/
private function buildBaseApiAuthController(){
$file_name = 'BaseApiAuthController';
$php = <<<EOF
<?php
namespace {$this->namespace};
use think\Controller;
/**
* descapi基础权限架构
*
* 所有权限接口继承此父类
*
* author
*/
class {$file_name} extends Controller
{
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
/**
* descapi底层基础控制器架构
*
* [按需创建][可单独调用]
*
* 如果存在则不创建
*
* authorwh
*/
function buildBaseApiController(){
$file_name = 'BaseApiController';
$php = <<<EOF
<?php
namespace {$this->namespace};
use think\Controller;
/**
* descapi底层基础控制器架构
*
* author
*/
class {$file_name} extends Controller
{
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
/**
* descuse模块
*
* authorwh
* @return string
*/
private function useTpl(){
$php = <<<EOF
use wanghua\general_utility_tools_php\\tool\Tools;
EOF;
return $php;
}
/**
* desc错误控制器
*
* authorwh
*/
function errController(){
$file_name = 'Err';
$php = <<<EOF
<?php
namespace {$this->namespace};
use wanghua\general_utility_tools_php\\framework\\base\PublicController;
use wanghua\general_utility_tools_php\\tool\Tools;
/**
*
* 错误(异常)中转架构
*
* Class Err
* @package app\index\controller
*/
class {$file_name} extends BaseApiPublicController
{
/**
* desc校验失败中转
*
* 场景:内网端口验证未通过时跳转;
*
* authorwh
* @return \\think\\response\\Json
*/
function checkfailed(){
return json(Tools::set_res(500,'Permission denied',input()));
}
}
EOF;
$php_file = $this->apidir.$file_name.$this->ext();
$this->createDir($this->apidir);
$this->createFile($php_file,$php);
}
}

View File

@@ -0,0 +1,264 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/5/15} {21:40}
*/
namespace wanghua\general_utility_tools_php\api;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* desc接口文档构建器
*
* 使用步骤:
* (new ApiDocument())->buildDoc();
* Class ApiDocument
*/
class ApiDocument
{
private $api_cache_arr = [];//缓存所有接口
public $api_domain = 'http://127.0.0.1:8080/';//接口域名/ip
/**
* @var string 接口控制器命名空间
*/
public $namespace = 'app\\api\\controller';
/**
* @var string 接口控制器基类精确到类名app\\common\\controller\\Api
* 注意:如果同级目录存在多个基类,则设置直接基类,如果同级目录没有基类,则设置底层基类
*/
public $extends_base_class = '';
//控制器目录物理路径
public $controllerDirectory = '';
/**
* @var string 接口文档保存路径
*/
public $api_docs_save_dir = 'public/api_docs/';
//设置过滤类
private $filterClassArr = [];
//设置过滤方法
private $filterFunctionArr = [];
public function __construct($api_domain='',$controllerDirectory='')
{
//默认如果是tp6那application就要改为app了自行传参吧
$this->controllerDirectory = Tools::get_root_path().'application/api/controller';
if($api_domain){
$this->api_domain = $api_domain;
}
if($controllerDirectory){
$this->controllerDirectory = $controllerDirectory;
}
}
/**
* desc设置过滤类类名
* 过滤场景:
* 1、基类
* 2、测试类
* 3、定时任务类
* 4、其它非必要类
* authorwh
* @param array $filterClassArr
*/
function setFilterClass(array $filterClassArr=[]){
$this->filterClassArr = $filterClassArr;
}
function setFilterFunction(array $filterArr=[]){
$this->filterFunctionArr = $filterArr;
}
/**
* desc构建接口文档支持同步到在线文档
* authorwh
*/
function buildDoc(){
if(empty($this->extends_base_class)){
throw new Exception('请设置接口控制器基类精确到类名app\\common\\controller\\Api');
}
$out_path = Tools::get_root_path().$this->api_docs_save_dir;
if(!file_exists($out_path)){
mkdir($out_path,0777,true);
}
$outputFile = $out_path.'api_list.md';
$controllerClasses = [];
// 搜索控制器目录下的所有PHP文件
foreach (glob($this->controllerDirectory . '/*.php') as $filename) {
// 获取文件中的类名
$class = basename($filename, '.php');
if(in_array($class,$this->filterClassArr)){
continue;
}
// 构建完整的命名空间类名
$fullClassName = $this->namespace . '\\' . $class;
foreach (explode(',',$this->extends_base_class) as $base_class){
// 检查类是否有效并且是think\Controller的子类
if (class_exists($fullClassName) && is_subclass_of($fullClassName, $base_class)) {
$controllerClasses[] = $fullClassName;
}
}
}
// 创建Markdown文件
$file = fopen($outputFile, 'w') or die('无法创建文件');
$head_text = <<<EOF
# API 文档
## 接口列表
###### ctrl+f 搜索)(如果更改了路由,请根据路由规则定位)
##### 请求域名:{$this->api_domain}
##### 请求方式POST默认
EOF;
// 写入Markdown文件头部
fwrite($file, $head_text);
foreach ($controllerClasses as $controllerClass) {
$reflector = new \ReflectionClass($controllerClass);
// 遍历控制器中的公共方法
$methods = $reflector->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
//过滤方法
if(in_array($method->name, $this->filterFunctionArr)){
continue;
}
$exp_class = explode('\\',$controllerClass);
//过滤类
if(in_array($exp_class[count($exp_class)-1],$this->filterClassArr)){
continue;
}
$comments = $method->getDocComment();
if ($comments) {
$this->processMethodComment($comments, $controllerClass, $method->name, $outputFile);
}
}
}
fclose($file);
//缓存所有接口
cache('api_doc_cache_arr',$this->api_cache_arr);
}
/**
* desc解析方法注释并写入Markdown文件
* authorwh
* @param $comments
* @param $className
* @param $methodName
* @param $savepath
*/
private function processMethodComment($comments, $className, $methodName, $savepath) {
if($methodName == '__construct'){
return '';
}
$api_url = "/api/{$className}/{$methodName}";
$js_api_func_name = "api_{$className}_{$methodName}";
$str = <<<EOF
***
```
EOF;
$comments_str = mb_substr($comments,0,-2);
$class_arr = explode('\\',$className);
$className = $class_arr[count($class_arr)-1];
$className = $this->camelCaseToUnderscore($className);
$api_name = "api/{$className}/{$methodName}";
$doc_txt = <<<EOF
* $api_name
*/
```
EOF;
$doc_txt= $str.$comments_str.$doc_txt." \r\n";
$this->api_cache_arr[] = ['api_name'=>$api_name,'doc_txt'=>$doc_txt];
file_put_contents($savepath,$doc_txt, FILE_APPEND);
}
/**
* desc驼峰转下划线
* authorwh
* @param $string
* @return string
*/
private function camelCaseToUnderscore($string) {
$str = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $string));
return strpos($str,'_')===0?substr($str,1):$str;
}
function buildApiDocHtml(){
$api_doc_cache_arr = cache('api_doc_cache_arr');
$htm_str = "";
$script_str = "";
foreach ($api_doc_cache_arr as $item){
$api_name = $item['api_name'];
$doc_txt = $item['doc_txt'];
$function_name = str_replace('/','_',$api_name);
$htm_str .= <<<EOF
<div id="{$function_name}">
<div class="markdown_content">{$doc_txt}</div>
<div>
按需填写其它接口参数:
<textarea name="" id="{$function_name}_textarea" cols="100" rows="3">/{$api_name}</textarea>
<a href='JavaScript:;' onclick="DocObject.{$function_name}()">测试</a>
</div>
<div class="{$function_name}_response_result"></div>
</div>
EOF;
$script_str.=<<<EOF
{$function_name}(){
let url = $('#{$function_name}_textarea').val();
$.post(url,{},function(res) {
$('.{$function_name}_response_result').html(JSON.stringify(res, null, "\\t"));
$('.{$function_name}_response_result').attr('style','color:green');
},'json');
},
EOF;
}
$html = <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>接口文档</title>
</head>
<body>
<div>
{$htm_str}
</div>
</body>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="/static/common/js/marked.min.js"></script>
<script>
$(function() {
//加载markdown
DocObject.markdown_content();
});
let DocObject = {
markdown_content(){
$('.markdown_content').each(function(k,ele) {
$(ele).html(marked.parse($(ele).html()));
});
},
$script_str
}
</script>
</html>
EOF;
return $html;
}
}

View File

@@ -0,0 +1,230 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/11} {16:14}
*/
namespace wanghua\general_utility_tools_php\api;
use wanghua\general_utility_tools_php\exception\api\ApiException;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 废弃
* descapi交互
*
* authorwh
* Class BaseApi
* @package app\apidata
*/
class BaseApi
{
protected $post_url = '';
private $prefix = 'api_';
protected $log_file_name = 'log';
protected $__sit__ = '';
function __construct(string $url)
{
$this->post_url = $url;
if(empty($this->post_url)) {
Tools::log_to_write_txt(['error'=>'API OPERATE: '.ApiException::EMPTY_URL_ERROR], $this->getLogFileName());
throw new \Exception(ApiException::EMPTY_URL_ERROR);
}
}
/**
* desc设置api请求日志文件名称
*
* 日志保存在runtime中
*
* authorwh
* @param string $log_file_name
*/
function setLogFileName(string $log_file_name=''){
if($log_file_name)$this->log_file_name = $log_file_name;
}
/**
* desc 获取记录日志的文件名
* authorwh
* @return string
*/
function getLogFileName(){
return $this->prefix.$this->log_file_name;
}
/**
* desc设置调用位置
* authorwh
* @param $__file__
* @param $__line__
*/
function setFileDir($__file__, $__line__){
$this->__sit__ = $__file__.' > '.$__line__;
}
/**
* desc错误位置
* authorwh
* @return string
*/
function getErrorSit(){
return $this->__sit__;
}
/**
* desc
*
* authorwh
* @param array $params
*/
private function mergeAuthParams(array &$params){
$params['nonce'] = Tools::rand_str(10);
$params['timestamp'] = time();
$token = config('service_framework_config.sign_token');
$params['sign'] = Tools::signature($params, $token);
}
/**
* desc执行post请求, 带权限参数
*
* 同框架、内部系统推荐
*
* 返回结果由code 错误码和msg 错误信息组成
*
* authorwh
* @param $param 以表单格式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function apiPost($param){
$this->mergeAuthParams($param);
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, $param);
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(empty($data['code'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR);
}
return $data;
}
/**
*
* desc执行post请求
*
* 返回结果由code 错误码和msg 错误信息组成
*
* authorwh
* @param $param 以json格式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function do($param){
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, json_encode($param));
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(empty($data['code'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR.$this->post_url);
}
return $data;
}
/**
*
* desc执行请求
*
* do 方法的改版返回结果由state 错误码和err 错误信息组成
*
* authorwh
* @param $param 以json格式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function apiDo($param){
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, json_encode($param));
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(is_null($data['state'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR);
}
$data['code'] = isset($data['state'])&&$data['state']==0?200:$data['state'];
$data['msg'] = isset($data['err'])?$data['err']:'api接口错误';
return $data;
}
/**
*
* desc执行请求
*
* 同框架、内部系统推荐
*
* authorwh
* @param $param 以表单方式提交数据
* @return array|bool|int|mixed|string
* @throws ApiException
* @throws \think\Exception
*/
function doPost($param){
Tools::log_to_write_txt(['API OPERATE, IN',$this->post_url,$param], $this->getLogFileName());
$res = Tools::curl_post($this->post_url, $param);
Tools::log_to_write_txt(['API OPERATE, OUT',$this->post_url, $res], $this->getLogFileName());
if(empty($res['data'])) return $res;
$data = json_decode($res['data'], true);
if(empty($data['code'])) {
Tools::log_to_write_txt([
'error: api操作,错误',
'post_url'=>$this->post_url,
'error_info'=>ApiException::API_RESPONSE_FORMAT_ERROR,
'error_sit'=>$this->getErrorSit(),
'result'=>$res,
], $this->getLogFileName());
throw new \Exception(ApiException::API_RESPONSE_FORMAT_ERROR);
}
return $data;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {15:01}
*/
namespace wanghua\general_utility_tools_php\api;
class BaseLibApi
{
/**
* desc创建目录
*
* authorwh
* @param $apidir
*/
function createDir($apidir){
if(!is_dir($apidir)){
mkdir($apidir,0777,true);
}
}
/**
* desc文件存在不写入
*
* authorwh
* @param $php_file
* @param $php
*/
function createFile($php_file,$php){
if(!file_exists($php_file)){
file_put_contents($php_file,$php);
}
}
/**
* desc追加文本
*
* authorwh
* @param $php_file
* @param $php
*/
function createFileExists($php_file,$php){
file_put_contents($php_file,$php,FILE_APPEND);
}
function ext(){
return '.php';
}
}

View File

@@ -0,0 +1,193 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/2/26} {10:54}
*/
namespace wanghua\general_utility_tools_php\api;
use app\common\model\TabConf;
use think\Db;
use wanghua\general_utility_tools_php\tool\Tools;
class BaseUserLogic
{
public $user_table = 'fa_users';
/**
* desc【通用】用户登录逻辑
*
* 请求端加密,服务端校验
*
* 签名规则
* 请求参数:
username //用户名
password //密码 md5(md5('a123456')) d477887b0636e5d87f79cc25c99d7dc9
timestamp //请求时间戳 2020-01-01 12:34:07
noncestr //随机字符串8位
sign //签名串,参考全站签名规则
other_params //其它接口的其它参数
* 全站sign规则
1. 对关联数组按照键升序排序
2. 拼接成字符串 keyvalkey2val2key3val3
3. 再2次md5生成32位小写串
4. 与请求参数一起发送到服务器
*
* authorwh
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
function tologin(){
if(!request()->isPost()){
return Tools::set_fail('错误');
}
Tools::log_to_write_txt(['登录入参:'=>input()]);
$input_data = input();
if(empty($input_data)){
return Tools::set_fail('请求参数错误');
}
if(empty($input_data['username'])){
return Tools::set_fail('用户名或密码错误');
}
if(empty($input_data['password'])){
return Tools::set_fail('用户名或密码错误.');
}
// start 这段代码可放在请求合法性里面校验 Tools::signature
if(empty($input_data['timestamp'])){
return Tools::set_fail('请求错误..');
}
if(empty($input_data['noncestr'])){
return Tools::set_fail('请求错误...');
}
if(empty($input_data['sign'])){
return Tools::set_fail('请求错误。');
}
$sign = $input_data['sign'];//md5后的字符串
unset($input_data['sign']);
//验签
$signstr = Tools::signature($input_data);
//Tools::log_to_write_txt([
// '签名日志:',
// $signstr,
// $sign,
// 'input'=>input(),
//]);
if($signstr != $sign){
return Tools::set_fail('非法请求');
}
//请求时间超过有效期 N分钟内有效
if(time()-5*60 > strtotime($input_data['timestamp'])){
return Tools::set_fail('请求失效');
}
// end 这段代码可放在请求合法性里面校验 Tools::signature
$username = $input_data['username'];
$user = Db::table($this->user_table)->where('username',$username)->find();
if(empty($user)){
return Tools::set_fail('账号密码错误!');
}
//校验密码
if($user['password'] != $input_data['password']){
return Tools::set_fail('账号密码错误!!');
}
$expires = time()+12*60*60;
//返回票据
$ticketstr = md5($user['username'].$user['expires']);
//保存ticket
$user[$ticketstr] = $expires;//N秒后过期
//修改有效期
Db::table($this->user_table)
->data([
'ticket'=>$ticketstr,
'expires'=>7*86400+time(),//7天
])
->where('username',$username)
->update();
session('api_user',$user);//备用(跨环境情况下session不生效)
return Tools::set_ok('登录成功',['ticket'=>$ticketstr]);
}
/**
* desc验证登录根据提交的ticket来校验是否登录适用于跨环境登录
*
* authorwh
* @return bool
*/
function isLogin(){
$ticket = input('ticket');
if(empty($ticket)){
Tools::log_to_write_txt(['title'=>'业务ticket字段不存在','input'=>input()]);
return false;
}
$user = Db::table(TabConf::$fa_users)
->where('ticket',$ticket)
->find();
if(empty($user)){
Tools::log_to_write_txt(['title'=>'用户未登录']);
return false;
}
//无效票据
//if(empty($user[$ticket])){
// Tools::log_to_write_txt(['title'=>'未获取到用户ticket',$ticket=>$user]);
//
// return false;
//}
//已过期
if(time() > $user['expires']){
Tools::log_to_write_txt(['title'=>'ticket已过期',$ticket=>$user]);
return false;
}
return true;
}
/**
* desc用户注册
*
* 此模块通用性不高,按需使用
*
* authorwh
*/
function reg($input_data){
try {
if(empty($input_data['username'])){
return Tools::set_fail('用户名错误');
}
if(empty($input_data['password'])){
return Tools::set_fail('密码错误.');
}
if(empty($input_data['password2'])){
return Tools::set_fail('密码错误!');
}
if($input_data['password'] != $input_data['password2']){
return Tools::set_fail('密码不一致!');
}
$insert_data = [
'username'=>$input_data['username'],
'password'=>$input_data['password'],
];
if(!empty($input_data['phone'])){
$insert_data['phone'] = $input_data['phone'];
}
Db::table($this->user_table)
->data($insert_data)
->insert();
return Tools::set_ok();
}catch (\Exception $e){
Tools::error_txt_log($e);
return Tools::set_fail();
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* description apk应用安装包信息读取
* authorwh
* email
* createTime{2020/7/1} {17:16}
*/
namespace wanghua\general_utility_tools_php\apk;
class Apk
{
/**
* 前提安装PHP ZIP扩展
* 扩展下载https://windows.php.net/downloads/pecl/releases/zip/
* desc读取APK
* authorwh
* @param string $path 文件地址
* @param bool $isRemote 是否远程文件
* @param string $proxy 代理
* @return array
*/
function read(string $path, $filename, bool $isRemote=false, $proxy=''){
/*解析安卓apk包中的压缩XML文件还原和读取XML内容
依赖功能需要PHP的ZIP包函数支持。*/
$appObj = new ApkParser();
$targetFile = $path;//a.apk; apk所在的路径地址
// 如果是远程文件,先下载到本地
if ($isRemote) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $path);
if ($proxy != '') {
curl_setopt($ch, CURLOPT_PROXY, $proxy);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 50);
$fileContent = curl_exec($ch);
curl_close($ch);
// 写入临时文件
$sys_tmp_path = tempnam(sys_get_temp_dir(), 'DL');
$fp = @fopen($sys_tmp_path, 'w+');
fwrite($fp, $fileContent);
$tmp_path = config('app.ROOT_PATH_PRO').'runtime/temp_file';
if(!file_exists($tmp_path)){
mkdir($tmp_path, 0777, true);
}
$targetFile = $tmp_path. '/' .time().$filename;
$res = move_uploaded_file($sys_tmp_path, $targetFile);
dump($res);die;
}
$result = [
'app_name'=>'',// 应用名称
'package'=>'',// 应用包名
'version_name'=>'',// 版本名称
'version_code'=>'',// 版本代码
];
dump($targetFile);die;
if(!is_file($targetFile)){
return $result;
}
$appObj->open($targetFile);//$res
$result = [
'app_name'=>$appObj->getAppName(),// 应用名称
'package'=>$appObj->getPackage(),// 应用包名
'version_name'=>$appObj->getVersionName(),// 版本名称
'version_code'=>$appObj->getVersionCode(),// 版本代码
];
return $result;
}
}

View File

@@ -0,0 +1,421 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {17:12}
*/
namespace wanghua\general_utility_tools_php\apk;
use Exception;
class ApkParser{
//----------------------
// 公共函数,供外部调用
//----------------------
public function open($apk_file, $xml_file='AndroidManifest.xml'){
$zip = new \ZipArchive();
if ($zip->open($apk_file) === TRUE) {
$xml = $zip->getFromName($xml_file);
$zip->close();
if ($xml){
try {
return $this->parseString($xml);
}catch (Exception $e){
}
}
}
return false;
}
public function parseString($xml){
$this->xml = $xml;
$this->length = strlen($xml);
$this->root = $this->parseBlock(self::AXML_FILE);
return true;
}
public function getXML($node=NULL, $lv=-1){
if ($lv == -1) $node = $this->root;
if (!$node) return '';
if ($node['type'] == self::END_TAG) $lv--;
$xml = @($node['line'] == 0 || $node['line'] == $this->line) ? '' : "\n".str_repeat(' ', $lv);
$xml .= $node['tag'];
$this->line = @$node['line'];
foreach ($node['child'] as $c){
$xml .= $this->getXML($c, $lv+1);
}
return $xml;
}
public function getPackage(){
return $this->getAttribute('manifest', 'package');
}
public function getVersionName(){
return $this->getAttribute('manifest', 'android:versionName');
}
public function getVersionCode(){
return $this->getAttribute('manifest', 'android:versionCode');
}
public function getAppName(){
return $this->getAttribute('manifest/application', 'android:name');
}
public function getMainActivity(){
for ($id=0; true; $id++){
$act = $this->getAttribute("manifest/application/activity[{$id}]/intent-filter/action", 'android:name');
if (!$act) break;
if ($act == 'android.intent.action.MAIN') return $this->getActivity($id);
}
return NULL;
}
public function getActivity($idx=0){
$idx = intval($idx);
return $this->getAttribute("manifest/application/activity[{$idx}]", 'android:name');
}
public function getAttribute($path, $name){
$r = $this->getElement($path);
if (is_null($r)) return NULL;
if (isset($r['attrs'])){
foreach ($r['attrs'] as $a){
if ($a['ns_name'] == $name) return $this->getAttributeValue($a);
}
}
return NULL;
}
//----------------------
// 类型常量定义
//----------------------
const AXML_FILE = 0x00080003;
const STRING_BLOCK = 0x001C0001;
const RESOURCEIDS = 0x00080180;
const START_NAMESPACE = 0x00100100;
const END_NAMESPACE = 0x00100101;
const START_TAG = 0x00100102;
const END_TAG = 0x00100103;
const TEXT = 0x00100104;
const TYPE_NULL =0;
const TYPE_REFERENCE =1;
const TYPE_ATTRIBUTE =2;
const TYPE_STRING =3;
const TYPE_FLOAT =4;
const TYPE_DIMENSION =5;
const TYPE_FRACTION =6;
const TYPE_INT_DEC =16;
const TYPE_INT_HEX =17;
const TYPE_INT_BOOLEAN =18;
const TYPE_INT_COLOR_ARGB8 =28;
const TYPE_INT_COLOR_RGB8 =29;
const TYPE_INT_COLOR_ARGB4 =30;
const TYPE_INT_COLOR_RGB4 =31;
const UNIT_MASK = 15;
private static $RADIX_MULTS = array(0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010);
private static $DIMENSION_UNITS = array("px","dip","sp","pt","in","mm","","");
private static $FRACTION_UNITS = array("%","%p","","","","","","");
private $xml='';
private $length = 0;
private $stringCount = 0;
private $styleCount = 0;
private $stringTab = array();
private $styleTab = array();
private $resourceIDs = array();
private $ns = array();
private $cur_ns = NULL;
private $root = NULL;
private $line = 0;
//----------------------
// 内部私有函数
//----------------------
private function getElement($path){
if (!$this->root) return NULL;
$ps = explode('/', $path);
$r = $this->root;
foreach ($ps as $v){
if (preg_match('/([^\[]+)\[([0-9]+)\]$/', $v, $ms)){
$v = $ms[1];
$off = $ms[2];
}else {
$off = 0;
}
foreach ($r['child'] as $c){
if ($c['type'] == self::START_TAG && $c['ns_name'] == $v){
if ($off == 0){
$r = $c; continue 2;
}else {
$off--;
}
}
}
// 没有找到节点
return NULL;
}
return $r;
}
private function parseBlock($need = 0){
$o = 0;
$type = $this->get32($o);
if ($need && $type != $need) throw new Exception('Block Type Error', 1);
$size = $this->get32($o);
if ($size < 8 || $size > $this->length) throw new Exception('Block Size Error', 2);
$left = $this->length - $size;
$props = false;
switch ($type){
case self::AXML_FILE:
$props = array(
'line' => 0,
'tag' => '<?xml version="1.0" encoding="utf-8"?>'
);
break;
case self::STRING_BLOCK:
$this->stringCount = $this->get32($o);
$this->styleCount = $this->get32($o);
$o += 4;
$strOffset = $this->get32($o);
$styOffset = $this->get32($o);
$strListOffset = $this->get32array($o, $this->stringCount);
$styListOffset = $this->get32array($o, $this->styleCount);
$this->stringTab = $this->stringCount > 0 ? $this->getStringTab($strOffset, $strListOffset) : array();
$this->styleTab = $this->styleCount > 0 ? $this->getStringTab($styOffset, $styListOffset) : array();
$o = $size;
break;
case self::RESOURCEIDS:
$count = $size / 4 - 2;
$this->resourceIDs = $this->get32array($o, $count);
break;
case self::START_NAMESPACE:
$o += 8;
$prefix = $this->get32($o);
$uri = $this->get32($o);
if (empty($this->cur_ns)){
$this->cur_ns = array();
$this->ns[] = &$this->cur_ns;
}
$this->cur_ns[$uri] = $prefix;
break;
case self::END_NAMESPACE:
$o += 8;
$prefix = $this->get32($o);
$uri = $this->get32($o);
if (empty($this->cur_ns)) break;
unset($this->cur_ns[$uri]);
break;
case self::START_TAG:
$line = $this->get32($o);
$o += 4;
$attrs = array();
$props = array(
'line' => $line,
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o)),
'flag' => $this->get32($o),
'count' => $this->get16($o),
'id' => $this->get16($o)-1,
'class' => $this->get16($o)-1,
'style' => $this->get16($o)-1,
'attrs' => &$attrs
);
$props['ns_name'] = $props['ns'].$props['name'];
for ($i=0; $i < $props['count']; $i++){
$a = array(
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o)),
'val_str' => $this->get32($o),
'val_type' => $this->get32($o),
'val_data' => $this->get32($o)
);
$a['ns_name'] = $a['ns'].$a['name'];
$a['val_type'] >>= 24;
$attrs[] = $a;
}
// 处理TAG字符串
$tag = "<{$props['ns_name']}";
foreach ($this->cur_ns as $uri => $prefix){
$uri = $this->getString($uri);
$prefix = $this->getString($prefix);
$tag .= " xmlns:{$prefix}=\"{$uri}\"";
}
foreach ($props['attrs'] as $a){
$tag .= " {$a['ns_name']}=\"".
$this->getAttributeValue($a).
'"';
}
$tag .= '>';
$props['tag'] = $tag;
unset($this->cur_ns);
$this->cur_ns = array();
$this->ns[] = &$this->cur_ns;
$left = -1;
break;
case self::END_TAG:
$line = $this->get32($o);
$o += 4;
$props = array(
'line' => $line,
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o))
);
$props['ns_name'] = $props['ns'].$props['name'];
$props['tag'] = "</{$props['ns_name']}>";
if (count($this->ns) > 1){
array_pop($this->ns);
unset($this->cur_ns);
$this->cur_ns = array_pop($this->ns);
$this->ns[] = &$this->cur_ns;
}
break;
case self::TEXT:
$o += 8;
$props = array(
'tag' => $this->getString($this->get32($o))
);
$o += 8;
break;
default:
throw new Exception('Block Type Error', 3);
break;
}
$this->skip($o);
$child = array();
while ($this->length > $left){
$c = $this->parseBlock();
if ($props && $c) $child[] = $c;
if ($left == -1 && $c['type'] == self::END_TAG){
$left = $this->length;
break;
}
}
if ($this->length != $left) throw new Exception('Block Overflow Error', 4);
if ($props){
$props['type'] = $type;
$props['size'] = $size;
$props['child'] = $child;
return $props;
}else {
return false;
}
}
private function getAttributeValue($a){
$type = &$a['val_type'];
$data = &$a['val_data'];
switch ($type){
case self::TYPE_STRING:
return $this->getString($a['val_str']);
case self::TYPE_ATTRIBUTE:
return sprintf('?%s%08X', self::_getPackage($data), $data);
case self::TYPE_REFERENCE:
return sprintf('@%s%08X', self::_getPackage($data), $data);
case self::TYPE_INT_HEX:
return sprintf('0x%08X', $data);
case self::TYPE_INT_BOOLEAN:
return ($data != 0 ? 'true' : 'false');
case self::TYPE_INT_COLOR_ARGB8:
case self::TYPE_INT_COLOR_RGB8:
case self::TYPE_INT_COLOR_ARGB4:
case self::TYPE_INT_COLOR_RGB4:
return sprintf('#%08X', $data);
case self::TYPE_DIMENSION:
return $this->_complexToFloat($data).self::$DIMENSION_UNITS[$data & self::UNIT_MASK];
case self::TYPE_FRACTION:
return $this->_complexToFloat($data).self::$FRACTION_UNITS[$data & self::UNIT_MASK];
case self::TYPE_FLOAT:
return $this->_int2float($data);
}
if ($type >=self::TYPE_INT_DEC && $type < self::TYPE_INT_COLOR_ARGB8){
return (string)$data;
}
return sprintf('<0x%X, type 0x%02X>', $data, $type);
}
private function _complexToFloat($data){
return (float)($data & 0xFFFFFF00) * self::$RADIX_MULTS[($data>>4) & 3];
}
private function _int2float($v) {
$x = ($v & ((1 << 23) - 1)) + (1 << 23) * ($v >> 31 | 1);
$exp = ($v >> 23 & 0xFF) - 127;
return $x * pow(2, $exp - 23);
}
private static function _getPackage($data){
return ($data >> 24 == 1) ? 'android:' : '';
}
private function getStringTab($base, $list){
$tab = array();
foreach ($list as $off){
$off += $base;
$len = $this->get16($off);
$mask = ($len >> 0x8) & 0xFF;
$len = $len & 0xFF;
if ($len == $mask){
if ($off + $len > $this->length) throw new Exception('String Table Overflow', 11);
$tab[] = substr($this->xml, $off, $len);
}else {
if ($off + $len * 2 > $this->length) throw new Exception('String Table Overflow', 11);
$str = substr($this->xml, $off, $len * 2);
$tab[] = mb_convert_encoding($str, 'UTF-8', 'UCS-2LE');
}
}
return $tab;
}
private function getString($id){
if ($id > -1 && $id < $this->stringCount){
return $this->stringTab[$id];
}else {
return '';
}
}
private function getNameSpace($uri){
for ($i=count($this->ns); $i > 0; ){
$ns = $this->ns[--$i];
if (isset($ns[$uri])){
$ns = $this->getString($ns[$uri]);
if (!empty($ns)) $ns .= ':';
return $ns;
}
}
return '';
}
private function get32(&$off){
$int = unpack('V', substr($this->xml, $off, 4));
$off += 4;
return array_shift($int);
}
private function get32array(&$off, $size){
if ($size <= 0) return NULL;
$arr = unpack('V*', substr($this->xml, $off, 4 * $size));
if (count($arr) != $size) throw new Exception('Array Size Error', 10);
$off += 4 * $size;
return $arr;
}
private function get16(&$off){
$int = unpack('v', substr($this->xml, $off, 2));
$off += 2;
return array_shift($int);
}
private function skip($size){
$this->xml = substr($this->xml, $size);
$this->length -= $size;
}
}

View File

@@ -0,0 +1,103 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/3/19} {13:57}
*/
namespace wanghua\general_utility_tools_php\coin;
use wanghua\general_utility_tools_php\http\Curl;
class Coin
{
public $apiKey = 'f94a2a9c-28be-47c3aaa-9c29-1e83b309d076';
public $url = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest';
/**
* desc
'limit'=>'1000',
'sort'=>'market_cap',
'sort_dir'=>'desc',
* @param array $params
* @return array|bool|int|string
*/
function getAll(array $params){
$params_str = http_build_query($params);
$url = $this->url.'?'.$params_str;
$header = [
'X-CMC_PRO_API_KEY:'.$this->apiKey,
'accept: application/json',
];
return Curl::curl_get($url,10,$header);
}
/**
* desc
*
*https://coinmarketcap.com/api/documentation/v1/#section/Introduction
* 'https://pro-api.coinmarketcap.com/v2/cryptocurrency/ohlcv/historical'
* authorwh
*/
function k(){
$url = $this->url;
$time_start = input('time_start');
$time_end = input('time_end');
$type = input('type');
$header = [
'X-CMC_PRO_API_KEY:'.$this->apiKey,
];
$arr = explode('=',$type);
$params = [
$arr[0]=>$arr[1],//币种
'time_start'=>$time_start,
'time_end'=>$time_end,
'count'=>7,
];
$url = $url.'?'.http_build_query($params);
return Curl::curl_get($url,10,$header);
}
private function curl_request($url, $method = 'GET',$data=null,$header=array(),$call_back=null)
{
set_time_limit(30);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
if($method == 'POST'){
if($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if($call_back){
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $call_back);
}
$result = curl_exec($ch);
if (curl_errno($ch)) {
return [
'status' => 'error',
'message' => 'curl 错误信息: ' . curl_error($ch)
];
}
curl_close($ch);
return $result;
}
}

View File

@@ -0,0 +1,390 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/12/17} {14:30}
*/
namespace wanghua\db\general_utility_tools_php;
use wanghua\db\general_utility_tools_php\SqlOperateLog;
use think\Container;
use think\Db;
use think\Paginator;
/**
* 环境要求:
* ThinkPHP5.1+,PHP7.0+,MySQL8.0+
*
*
* 查询缓存器、监控器
* 【使用框架链式操作时,配合此类才能发挥完美效果】
* 举例
* function getMemberInIds(string $ids){
$obj = Db::name($this->table)->whereIn("id",$ids)->where(["is_deleted"=>0]);
return DbCacheUtility::getAll($obj);
}
* Class DbCacheUtility
* @package libraries
*/
class DbCacheUtility
{
const NoCacheTime = 0;
const LowCacheTime = 60 * 1;
const NormalCacheTime = 60 * 10;
const HighCacheTime = 60 * 60;
const LongCacheTime = 60 * 60 * 24;
/**
* desc
* authorwh
* @param $object 查询对象
* @param int $cacheDuration
* @param bool $is_log
* @return mixed
*/
public static function getOne($object, $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = $object->fetchSql(true)->find();
$md5_name = md5($last_sql);
$key = 'get_one:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = $object->fetchSql(false)->find();
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'get_one', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $object 查询对象
* @param int $cacheDuration
* @param bool $is_log
* @return mixed
*/
public static function getAll($object, $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = $object->fetchSql(true)->select();
$md5_name = md5($last_sql);
$key = 'get_all:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = $object->fetchSql(false)->select();
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'get_all', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $object
* @param int $page
* @param int $listRows
* @param bool $simple
* @param array $config
* @param int $cacheDuration
* @param bool $is_log
* @return mixed
*/
static function paginates($object, $page=1, $listRows = null, $simple = false, $config = [], $cacheDuration = self::NoCacheTime, $is_log = true){
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = $object->limit(abs(1*$page-1) * $listRows, $listRows)->fetchSql(true)->select();
$md5_name = md5($last_sql);
$key = 'paginates:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = $object->fetchSql(false)->paginate($listRows, $simple, $config);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'paginates', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function queryScalar($sql, $bind = [], $field, $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = self::presetBind($sql, $bind);
$md5_name = md5($last_sql);
$key = 'query_scalar:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = Db::query($sql, $bind);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_scalar', $is_cache);
}
return $result ? $result[0][$field] : [];
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function queryOne($sql, $bind = [], $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = self::presetBind($sql, $bind);
$md5_name = md5($last_sql);
$key = 'query_one:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = Db::query($sql, $bind);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_one', $is_cache);
}
return $result ? $result[0] : [];
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function queryAll($sql, $bind = [], $cacheDuration = self::NoCacheTime, $is_log = true)
{
$begin_time = Tools::getMillisecond();
$is_cache = 0;
$last_sql = self::presetBind($sql, $bind);
//echo $last_sql;die;
$md5_name = md5($last_sql);
$key = 'query_all:' . $md5_name;
if ($cacheDuration == 0) {
if(cache($key)){
cache($key, null);
}
}
if (!(cache($key))) {
$result = Db::query($sql, $bind);
if ($cacheDuration != 0) {
cache($key, $result, $cacheDuration);
}
} else {
$is_cache = 1;
$result = cache($key);
}
$end_time = Tools::getMillisecond();
if($is_log){
SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_all', $is_cache);
}
return $result;
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return int
* @throws \think\db\exception\BindParamException
* @throws \think\exception\PDOException
*/
static function execute($sql, $bind = []){
return Db::execute($sql, $bind);
}
/**
* desc
* authorwh
* @param $sql
* @param array $bind
* @return mixed
*/
static function presetBind($sql, $bind = []){
if($bind){
foreach ($bind as $key=>$val){
$sql = str_replace(':'.$key, $val, $sql);
}
}
return $sql;
}
static function paginate($sql, $bind, $total, $listRows = null, $simple = false, $config = [])
{
//if (is_int($simple)) {
// $total = $simple;
// $simple = false;
//}
$paginate = Container::get('config')->pull('paginate');
if (is_array($listRows)) {
$config = array_merge($paginate, $listRows);
$listRows = $config['list_rows'];
} else {
$config = array_merge($paginate, $config);
$listRows = $listRows ?: $config['list_rows'];
}
/** @var Paginator $class */
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);
$page = isset($config['page']) ? (int) $config['page'] : call_user_func([
$class,
'getCurrentPage',
], $config['var_page']);
$page = $page < 1 ? 1 : $page;
$config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']);
//if (!isset($total) && !$simple) {
//
//
// unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
//
// $bind = $this->bind;
// $total = $this->count();
// $results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
//} elseif ($simple) {
// $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
// $total = null;
//} else {
// $results = $this->page($page, $listRows)->select();
//}
$results = DbCacheUtility::queryAll($sql, $bind);
return $class::make($results, $listRows, $page, $total, $simple, $config);
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/5/22} {16:43}
*/
namespace wanghua\db\general_utility_tools_php;
use think\cache\driver\Redis as RedisDriver;
use think\Db;
use think\facade\Cache;
/**
* Redis操作工具
*
* 环境要求:
* ThinkPHP5.1+,PHP7.0+,MySQL8.0+
*
*
* Class RedisUtility
* @package libraries
*/
class RedisUtility
{
protected static $redisHandle = null;
/**
* desc初始化Redis对象
* authorwh
* @return object|null
*/
static function redisObject(){
if(empty(self::$redisHandle)){
//初始化驱动拿到句柄
self::$redisHandle = (new RedisDriver(config('cache.redis')))->handler();
}
return self::$redisHandle;
}
/**
* desc设置 0 永久有效
* authorwh
* @param $key_name 键名
* @param $value 值 json数据
* @return bool
*/
static function set($key_name, $value){
return Cache::store('redis')->set($key_name, json_encode($value, JSON_UNESCAPED_UNICODE));
}
/**
* desc重置
* authorwh
* @param $key_name
* @return bool
*/
static function reset($key_name){
return Cache::store('redis')->set($key_name, null);
}
/**
* desc设置过期时间
* authorwh
* @param $key_name 键名
* @param $value 值 json数据
* @param int $expire 过期时间 0 永久有效
* @return bool
*/
static function setExpire($key_name, $value, $expire=0){
return Cache::store('redis')->set($key_name, json_encode($value), $expire);
}
/**
* desc存储hash类型
* authorwh
* @param $key
* @param $field
* @param $value
*/
static function hSet($key, $field, $value){
return self::redisObject()->hSet($key, $field, json_encode($value, JSON_UNESCAPED_UNICODE));
}
/**
* desc删除指定的键。
* authorwh
* @param $key
* @return mixed
*/
static function delete($key){
return self::redisObject()->delete($key);
}
/**
* desc从存储在键上的哈希中移除一个值。
* 如果哈希表不存在,或者键不存在,则返回 FALSE 。
* authorwh
* @param $key
* @param $hashKey1
* @return mixed
*/
static function hDel($key, $hashKey1){
return self::redisObject()->hDel($key, $hashKey1);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/10/31} {9:25}
*/
namespace wanghua\db\general_utility_tools_php;
use think\Db;
use think\facade\Log;
/**
* 环境要求:
* ThinkPHP5.1+,PHP7.0+,MySQL8.0+
*
* 日志操作类配合DbCacheUtility.php使用。
* 使用前先在系统config.php文件中添加配置:
* 1、is_sql_slow_log:是否记录sql慢日志true or false
* 2、新建表log_sql_operate字段action,is_cache,duration_time,type,is_cache,sql,md5_name
* 3、如果什么错都没有报但是程序就是没反应则去runtime目录中找错误。
*
* Class SqlOperateLog
* @package wanghua\general_utility_tools_php
*/
class SqlOperateLog
{
static function add($sql,$end_time,$begin_time,$md5_name,$type,$is_cache){
$data = [
'action'=>request()->baseUrl(),
'duration_time'=>$end_time - $begin_time,
'type'=>$type,
'is_cache'=>$is_cache?1:0,
'sql'=>$sql,
'md5_name'=>$md5_name,
];
try{
if(config('app.is_sql_slow_log') && ($end_time - $begin_time) >= config('app.is_sql_slow_log')){
Db::table('log_sql_operate')->insert($data);
}
}catch (\Exception $e){
//DATABASE BOOM
//tp6
//Log::error('========[数据库异常:DATABASE BOOM]========'.$e->getTraceAsString());
//Log::close();
//tp5.1
Log::write('========[DATABASE ERROR:DATABASE BOOM]========【'.$e->getMessage().'】'.$e->getTraceAsString());
}
}
}

View File

@@ -0,0 +1,262 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/4/26} {9:45}
*/
namespace wanghua\general_utility_tools_php\db\es;
use wanghua\general_utility_tools_php\Date;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 【es原生操作库】
* 【仅支持框架ThinkPHP5+、 PHP7.2+】
*
* BaseElasticsearch.php类库的替代库
*
* 使用步骤:
* 初始化设置ip、索引前缀-普通索引请设置为空字符串)
* 设置索引范围例如查询本月如果索引不是按日期规律设置请使用普通索引设置方法setIndexNormal并设置前缀为空字符串
* 设置查询方法eg: _search
* 设置查询参数(查询条件)
* 执行
* 返回结果
* Class ElasticsearchUtility
* @package app\admin\logic
*/
class Elasticsearch
{
protected $es_log_file = 'es_log';
//最终拼装的请求地址
protected $post_url = ''; //eg: $url = 'http://49.4.3.4:9111/qa-item-2021.04/_search';
//查询参数
protected $query_param = [];
//查询方法
protected $query_method = '_search';
//文档索引
protected $index = '';
//服务器ip
protected $ip = '';
//索引前缀(优先设置)
protected $index_sign = '';//eg: qa-stat-
//设置查询日期格式符号
protected $doc_date_sign = '.';
//设置是哪个方法调用本es库用于日志记录
protected $exe_func = '';
/**
* ElasticsearchUtility constructor.
* @param string $ip_port 协议+服务器ip+端口号,必须
* @param string $index_sign 当es索引为有规律的日期索引时可使用前缀并配合日期索引设置方法体验更佳
*/
public function __construct(string $ip_port, string $index_sign='')
{
$this->ip = false===strpos($ip_port,'http')?'http://'.$ip_port:$ip_port;
$this->index_sign = $index_sign;
}
/**
* desc设置查询方法默认_search
* authorwh
* @param $method_name
*/
function setQueryMethod($method_name){
$this->query_method = $method_name;
}
/**
* desc根据日期索引查询文档
* 注:默认一个月
*
* eg:'qa-stat-2021.01';
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
* authorwh
*/
function setIndexDefault(){
$this->setRequestIndex(date('Y'.$this->doc_date_sign.'m'));
}
/**
* desc设置要查询的日期文档索引
*
* 1、按年检索文档[建议5年左右]
*
* eg: qa-stat-2018.*,qa-stat-2019.*,qa-stat-2020.*,qa-stat-2021.*
*
* 注意当索引过长es会抛出索引太长异常
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time 开始时间 eg2010-01-01 08:05:55
* @param string $end_time 结束时间 eg2021-12-31 12:05:00
* @return string
*/
function setIndexYearDate(string $start_time, string $end_time){
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'*', strtotime($start_time));
$sign = ',';//分隔符
$m = date('Y', strtotime($end_time)) - date('Y', strtotime($start_time));
//拼装查询索引
for ($i=0; $i<$m; $i++){
$index .= $sign.($this->index_sign.(date('Y', strtotime($start_time))+$i).$this->doc_date_sign.'*');
}
$this->setRequestIndex($index);
}
/**
* desc设置日期索引
*
* 检索全部日期索引文档
*
* 例如es设置的索引为
* qa-stat-2021.01
qa-stat-2021.02
qa-stat-2021.03
qa-stat-2021.04
*
* 实际查询自动设置为qa-stat-*
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @return string
*/
function setIndexAllDate(){
$this->setRequestIndex('*');
}
/**
* desc设置普通索引
*
* 调用此方法请在初始化时设置索引前缀index_sign为""空字符串
*
* authorwh
* @param string $index
*/
function setIndexNormal(string $index){
$this->setRequestIndex($index);
}
/**
* desc设置跨月份索引
*
* eg: qa-stat-2021.01,qa-stat-2021.02
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time eg:'2020-11'
* @param string $end_time eg:'2021-01'
* @return string eg: qa-stat-2020.11,qa-stat-2020.12,qa-stat-2021.01
*/
function setIndexDate(string $start_time, string $end_time){
$date = new Date();
//计算月份
$m = $date->dateCutMonth($start_time, $end_time);
////解决跨年且不足1个月时索引设置错误问题
//if($m == 0 && (date('Y', strtotime($end_time)) > date('Y', strtotime($start_time)))){
// $m = 1;
//}
//格式
$date->date_format = 'Y-m';
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'m', strtotime($start_time));
$tmp_time = $start_time;
$sign = ',';//分隔符
//拼装查询索引
for ($i=0; $i<$m; $i++){
$tmp_time = $date->addTime(1, 'M', strtotime($tmp_time));
$index .= $sign.$this->index_sign.(date('Y'.$this->doc_date_sign.'m', strtotime($tmp_time)));
}
$this->setRequestIndex($index);
}
/**
* desc设置查询参数
* authorwh
* @param string $query_param_json
*/
function setQueryParam(string $query_param_json){
$this->query_param = $query_param_json;
$this->post_url = $this->ip.'/'.$this->index.'/'.$this->query_method;
}
/**
* desc获取查询参数用于调试
* authorwh
* @return array
*/
function getQueryParam(){
return [
'ip'=>$this->ip,
'index_sign'=>$this->index_sign,
'index'=>$this->index,
'query_method'=>$this->query_method,
'post_url'=>$this->post_url,
'query_param'=>$this->query_param,
];
}
/**
* desc执行es查询
* authorwh
* @return array|bool|int|string
* @throws \Exception
*/
function execute(){
if(empty($this->ip)) throw new \Exception('请设置ip');
//if(empty($this->index_sign)) throw new \Exception('请设置索引前缀');
if(empty($this->index)) throw new \Exception('请设置索引');
if(empty($this->query_method)) throw new \Exception('请设置查询方法');
if(empty($this->query_param)) throw new \Exception('请设置查询参数');
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'request_url'=>$this->post_url,'IN'=>" | IN: ",'query_params'=>$this->query_param, 'input'=>input()], $this->es_log_file);
$result = Tools::curl_post($this->post_url, $this->query_param);
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'OUT'=>" | OUT: ", 'result'=>$result], $this->es_log_file);
return $result;
}
/**
* desc设置是哪个方法调用本es库用于日志记录
* authorwh
* @param string $function
*/
function setExecuteFunction(string $function){
$this->exe_func = $function;
}
/**
* desc设置请求文档索引
* authorwh
* @param string $index
*/
protected function setRequestIndex(string $index){
$this->index = $this->index_sign.$index;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
## 仅适用于tp5+, php7+
## 表管理
## 字段管理

View File

@@ -0,0 +1,304 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {10:57}
*/
namespace wanghua\general_utility_tools_php\db\mysql\lib;
use think\Db;
class Field
{
/**
* desc创建字段存在则修改字段
* authorwh
* @param array $table
*/
function createfield(array $table){
$table_name = $table['tablename'];
//新增字段
$dec_num = '';
if(in_array(strtoupper($table['type']), ['TINYINT','SMALLINT','MEDIUMINT','INT','BIGINT','FLOAT','DOUBLE','DECIMAL'])){
$table['default'] = '0';//整型默认值 浮点类型自动转为0.00
if(in_array(strtoupper($table['type']), ['FLOAT','DOUBLE','DECIMAL'])){
$dec_num = ','.$table['decimals_size'];//浮点类型小数位位数
}
}elseif ($table['type'] == 'enum'){
//枚举值不转换
}else{
$table['default'] = '""';
}
if(in_array(strtolower($table['type']), ['timestamp','datetime','date','time','text','mediumtext','longtext'])){
$table['size'] = 0;//重写 默认0
$table['default'] = 'null';//重写
}
//是否可以负数
if(in_array(strtolower($table['type']), ['int','tinyint','bigint','smallint', 'double', 'float', 'decimal']) && !empty($post['is_unsigned'])){//不是
$is_unsigned = 'unsigned';
}else{//是 表单可选值
$is_unsigned = '';
}
if (in_array($table['fields_name'], ['create_time', 'update_time'])){
if($table['fields_name'] == 'create_time'){
$table['default'] = 'CURRENT_TIMESTAMP';
}
if($table['fields_name'] == 'update_time'){
$table['default'] = 'NULL ON UPDATE CURRENT_TIMESTAMP';
}
}
//验证字段是否存在
$f = DB::table($table_name)->getTableFields();
if(in_array($table['fields_name'], $f)){
//枚举类型
if ($table['type'] == 'enum'){
$enum_str = '';
$enum_default = '';
//组合枚举值
if($table['default']){
$enum_str.='ENUM('.$table['default'].')';
$enum_default = explode(',', $table['default'])[0];
}
$sql2 = "ALTER TABLE {$table_name} MODIFY {$table['fields_name']} {$enum_str} NOT NULL DEFAULT {$enum_default} COMMENT '{$table['title']}';";
}else{
$sql2 = "ALTER TABLE {$table_name} MODIFY COLUMN {$table['fields_name']} {$table['type']}(".$table['size'].$dec_num.") {$is_unsigned} DEFAULT {$table['default']} COMMENT '{$table['title']}';";
}
}else{
if(in_array($table['type'], ['text','longtext'])){
$sql2 = "ALTER TABLE {$table_name} ADD COLUMN {$table['fields_name']} {$table['type']} COMMENT '{$table['title']}' AFTER id;";
}elseif ($table['type'] == 'enum'){//枚举类型
$enum_str = '';
$enum_default = '';
//组合枚举值
if($table['default']){
$enum_str.='ENUM('.$table['default'].')';
$enum_default = explode(',', $table['default'])[0];
}
$sql2 = "ALTER TABLE {$table_name} ADD {$table['fields_name']} {$enum_str} NOT NULL DEFAULT {$enum_default} COMMENT '{$table['title']}' AFTER id;";
}else{
$sql2 = "ALTER TABLE {$table_name} ADD COLUMN {$table['fields_name']} {$table['type']}(".$table['size'].$dec_num.") {$is_unsigned} DEFAULT {$table['default']} COMMENT '{$table['title']}' AFTER id;";
}
}
DB::execute($sql2);
}
/**
* desc删除字段
* authorwh
* @param $tablename
* @param $fieldname
*/
function dropFieldName($tablename, $fieldname){
$sql = "ALTER TABLE {$tablename} DROP COLUMN {$fieldname};";
DB::execute($sql);
}
/**
* desc改注释
* authorwh
* @param $tablename
* @param $comment
* @return bool
*/
function updateComment($tablename,$field, $comment){
$sql = "ALTER TABLE {$tablename} MODIFY COLUMN {$field} INT COMMENT '{$comment}';";
Db::execute($sql);
}
/**
* desc获取数据表中所有字段的数据类型含其它属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldsDataType(string $dbname,string $tablename){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'";
return Db::query($sql);
}
/**
* desc获取数据表中某个字段的数据类型含这个字段的其它属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldDataType(string $dbname,string $tablename,string $fieldname){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0];
}
/**
* desc获取数据表中某个字段的数据类型
*
* 不含这个字段的其它属性
*
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldDataTypeVal(string $dbname,string $tablename,string $fieldname){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0]['F_DATATYPE'];
}
/**
* desc获取数据表中某个字段的属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldAttrVal(string $dbname,string $tablename,string $fieldname,string $attr){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0][$attr];
}
}

View File

@@ -0,0 +1,162 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {10:48}
*/
namespace wanghua\general_utility_tools_php\db\mysql\lib;
use think\Db;
use wanghua\general_utility_tools_php\tool\Tools;
class Table
{
/**
* desc创建表初始化默认字段
* authorwh
* @param $data
* @return bool
*/
function createTable($data){
$tablename = $data['tablename'];
$title = $data['title'];
//创建表
$sql = "CREATE TABLE IF NOT EXISTS {$tablename}(
id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='{$title}' ;";
Db::execute($sql);
}
/**
* desc改注释
* authorwh
* @param $tablename
* @param $comment
* @return bool
*/
function updateComment($tablename, $comment){
$sql = "ALTER TABLE {$tablename} COMMENT '{$comment}';";
Db::execute($sql);
}
/**
* desc改表名
* authorwh
* @param $tablename
* @param $new_table_name
* @return bool
*/
function updateTableName($tablename, $new_table_name){
$sql = "ALTER TABLE {$tablename} RENAME TO {$new_table_name}";
Db::execute($sql);
}
/**
* desc查询当前数据库所有的表名
* authorwh
* @return mixed
*/
function getTables(){
return array_column(Db::query('SHOW TABLES;'), 'Tables_in_'.config('database.database'));
}
/**
* desc获取数据表字所有段名
* authorwh
* @param $tablename
* @return array
*/
function getTableFields($tablename){
$dbname = config('database.database');
$sql = "SELECT COLUMN_NAME column_name,COLUMN_COMMENT column_comment,DATA_TYPE data_type
FROM information_schema.columns WHERE TABLE_NAME='{$tablename}' AND table_schema='{$dbname}'";
return array_column(Db::query($sql), 'column_name');
}
/**
* desc获取表的属性
* Name:
表名称
Engine:
表的存储引擎
Version:
版本
Row_format:
行格式。对于MyISAM引擎这可能是DynamicFixed或Compressed。动态行的行长度可变例如Varchar或Blob类型字段。固定行是指行长度不变例如Char和Integer类型字段
Rows:
表中的行数。对于MyISAM和其他存储引擎这个值是精确的对于innoDB存储引擎这个值通常是估算的
Avg_row_length:
平均每行包括的字节数
Data_length:
整个表的数据量(以字节为单位)
Max_data_length:
表可以容纳的最大数据量,该值和存储引擎相关
Index_length:
索引占用磁盘的空间大小(以字节为单位)
Data_free:
对于MyISAM引擎表示已经分配但目前没有使用的空间。这部分空间包含之前被删除的行以及后续可以被insert利用到的空间
Auto_increment:
下一个Auto_increment的值
Create_time:
表的创建时间
Update_time:
表的最近更新时间
Check_time:
使用 check table 或myisamchk工具最后一次检查表的时间
Collation:
表的默认字符集和字符排序规则
Checksum:
如果启用,保存的是整个表的实时校验和
Create_options:
创建表时指定的其他选项
Comment:
包含了其他额外信息对于MyISAM引擎保存的是表在创建时带的注释。如果表使用的是innodb引擎 保存的是InnoDB表空间的剩余空间。如果是一个视图注释里面包含了VIEW字样。
*
* @param $tablename 表名(无表名则查询所有表)
*
* authorwh
* @return mixed
*/
function getTableInfo(string $tablename=''){
if($tablename){
$sql = "SHOW TABLE STATUS WHERE Name = 'fa_agent';";
return Db::query($sql);
}
$sql = 'show table status;';
return Db::query($sql);
}
/**
* desc检查某个表是否存在
*
* authorwh
* @param string $dbname
* @param string $tablename
* @return bool
*/
function isExistTable(string $dbname,string $tablename){
$sql = "SELECT TABLE_SCHEMA,TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA ='$dbname'
AND TABLE_NAME = '$tablename';";
return empty(Db::query($sql));
}
/**
* desc获取表注释
*
* authorwh
*/
function getTableComment($table){
$prefix = config('database.prefix');
$table = $prefix.$table;
$sql = 'show table status;';
$arr = Db::query($sql);
$tmp = Tools::key_val_arr($arr,'Name','Comment');
return empty($tmp[$table])?'':$tmp[$table];
}
}

View File

@@ -0,0 +1,15 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/4/5} {10:49}
*/
namespace wanghua\general_utility_tools_php\douyin;
class BaseDouYin
{
}

View File

@@ -0,0 +1,193 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/4/4} {12:36}
*/
namespace wanghua\general_utility_tools_php\douyin;
use wanghua\general_utility_tools_php\http\Curl;
use wanghua\general_utility_tools_php\tool\Tools;
class DouYinMiniGame extends BaseDouYin
{
public $appid = 'tt3c974a2aa3a6ee7c02';
public $secret = '5c2414e47a18b9655a164b14ef31ac7cb58179ca';
/**
* desc每2小时更新一次
*
* https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/server/interface-request-credential/get-access-token
*
* {"err_no":0,"err_tips":"success","data":{"access_token":"0801121846384c316e52536e5a76396769565145696f597742673d3d","expires_in":7200,"expiresAt":1712208063,"expires_at":1712208063}}
*
* authorwh
*/
function getAccessToken(){
$url = "https://minigame.zijieapi.com/mgplatform/api/apps/v2/token";
$method = 'POST';
//$Scope = 'open.ttgame.mgplatform';
$params = [
'appid'=>$this->appid,
'secret'=>$this->secret,
'grant_type'=>'client_credential',
];
$header = [
'Accept: application/json',
'content-type: application/json'
];
$res = Curl::curl_request($url,$method,json_encode($params),$header);
$json_arr = json_decode($res, true);
if($json_arr['err_no']){
return Tools::set_res($json_arr['err_no'],$json_arr['err_tips']);
}
//保存
session('ses_dou_yin_access_token',$json_arr['data']);
return Tools::set_ok('ok',$json_arr['data']);
}
/**
* desc实时获取token
* authorwh
* @return array
*/
function realTimeGetAccessToken(){
$json_arr = session('ses_dou_yin_access_token');
if(empty($json_arr)){
return $this->getAccessToken();
}
if(time() >= $json_arr['expiresAt']){
//过期
return $this->getAccessToken();
}
return Tools::set_ok('ok',$json_arr);
}
/**
* desc前端tt.login触发调用
*
* authorwh
*/
function jscode2session($code='',$anonymous_code=''){
$url = 'https://minigame.zijieapi.com/mgplatform/api/apps/jscode2session';
//$Scope = 'open.ttgame.mgplatform';
$method = 'GET';
if(empty($code) && empty($anonymous_code)){
return Tools::set_fail('tt.login 接口返回的匿名登录凭证code 和 anonymous_code 至少要有一个)');
}
$params = [
'appid'=>$this->appid,
'secret'=>$this->secret,
];
if($code){
$params['code'] = $code;
}
if($anonymous_code){
$params['anonymous_code'] = $anonymous_code;
}
$header = [
'Accept: application/json',
'content-type: application/json'
];
$url .= '?'.http_build_query($params);
//dump($url);
Tools::log_to_write_txt(['input'=>$url]);
$res = Curl::curl_request($url,$method,[],$header);
Tools::log_to_write_txt(['output'=>$res]);
//dump($res);
$json_arr = json_decode($res, true);
//dump($json_arr);die;
if($json_arr['error']){
return Tools::set_res($json_arr['errcode'],$json_arr['errmsg']);
}
//保存
session('ses_dou_yin_js_code_2_session',$json_arr);
return Tools::set_ok('ok',$json_arr);
}
/**
* desc创建二维码
* 接口说明
* 获取小程序/小游戏的二维码。该二维码可通过任意 app 扫码打开,
* 能跳转到开发者指定的对应字节系 app 内拉起小程序/小游戏,并传入开发者指定的参数。
* 通过该接口生成的二维码,永久有效,暂无数量限制。
*
* ⚠ Tip在使用该功能之前请记得先配置您的默认分享文案和图片配置方式可参考论坛。
* ⚠ Tip小程序的 path 要 encode 一次,如 pages%3fparam%3dtrue小游戏的 path 为 JSON 字符串,
* 如{"param":true},否则会导致取不到。
*
* 参数:
* code和anonymous_code二选一 必须
* appname 可选,目标打开应用名称 默认douyin
* background 可选背景色rgb格式英文逗号隔开默认透明色
* path 可选,小程序/小游戏启动参数,小程序则格式为 encode({path}?{query}),小游戏则格式为 JSON 字符串,默认为空
* width 宽度 可选,二维码宽度,单位 px最小 280px最大 1280px默认为 430px
* line_color 可选二维码线条颜色默认为黑色rgb格式英文逗号隔开默认黑色
* set_icon 可选,是否展示小程序/小游戏 icon默认不展示传yes展示no不展示默认no
*
* authorwh
*/
function createQRCode($path='',$appname='',$width='',$background='',$line_color='',$set_icon='no'){
try {
$url = 'https://minigame.zijieapi.com/mgplatform/api/apps/qrcode';
//$Scope = 'open.ttgame.mgplatform';
$method = 'POST';
$json_arr = $this->realTimeGetAccessToken();
if(empty($json_arr)){
return Tools::set_fail('请重新授权');
}
$params = [
'access_token'=>$json_arr['data']['access_token'],
];
if($appname){
$params['appname'] = $appname;
}
if($width){
$params['width'] = $width;
}
//if($path){
//小程序/小游戏启动参数,小程序则格式为 encode({path}?{query}),小游戏则格式为 JSON 字符串,默认为空
$params['path'] = $path;
//}
//英文逗号隔开r,g,b格式
if($background){
$background_rgb = explode(',',$background);
$params['r'] = $background_rgb[0];
$params['g'] = $background_rgb[1];
$params['b'] = $background_rgb[2];
}
if($line_color){
$line_color_rgb = explode(',',$line_color);
$params['r'] = $line_color_rgb[0];
$params['g'] = $line_color_rgb[1];
$params['b'] = $line_color_rgb[2];
}
$params['set_icon'] = $set_icon=='yes';
$header = [
'Accept: application/json',
'content-type: application/json'
];
Tools::log_to_write_txt(['input'=>input(),'$params'=>$params]);
$res = Curl::curl_request($url,$method,json_encode($params),$header);
Tools::log_to_write_txt(['output'=>$res]);
//流
return $res;
}catch (\Exception $e){
Tools::error_txt_log($e);
return Tools::set_fail();
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/10} {15:51}
*/
namespace wanghua\general_utility_tools_php\encrypt;
/**
* 3DES,三重数据加密算法
*
* Class TripleDES
* @package wanghua\general_utility_tools_php\encrypt
*/
class TripleDES
{
/**
* desc加密
*
* authorwh
* @param string $value
* @param $key
* @return string
*/
public function encrypt(string $value, $key)
{
$value = self::PaddingPKCS7($value);
//AES-128-ECB|不能用 AES-256-CBC|16 AES-128-CBC|16 BF-CBC|8 aes-128-gcm|需要加$tag DES-EDE3-CBC|8
$cipher = "DES-EDE3";
// if (in_array($cipher, openssl_get_cipher_methods())) {}
$result = openssl_encrypt($value, $cipher, $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, "");
return base64_encode($result);
}
/**
* @title 解密
*
* @param string $str 要传的参数
* @param $key
* @return bool|false|string
*
*/
public function decrypt(string $str, $key)
{
$decrypted = openssl_decrypt(base64_decode($str), 'DES-EDE3', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, "");
$ret = self::UnPaddingPKCS7($decrypted);
return $ret;
}
/**
* desc
* authorwh
* @param $data
* @return string
*/
private function PaddingPKCS7($data)
{
//$block_size = mcrypt_get_block_size('tripledes', 'cbc');//获取长度
//$block_size = openssl_cipher_iv_length('tripledes', 'cbc');//获取长度
$block_size = 8;
$padding_char = $block_size - (strlen($data) % $block_size);
$data .= str_repeat(chr($padding_char), $padding_char);
return $data;
}
/**
* desc
* authorwh
* @param $text
* @return false|string
*/
private function UnPaddingPKCS7($text)
{
$pad = ord($text{strlen($text) - 1});
if ($pad > strlen($text)) {
return false;
}
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
return false;
}
return substr($text, 0, -1 * $pad);
}
}

View File

@@ -0,0 +1,20 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {19:05}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class AuthError
{
const PERMISSION_DENIED = [60000, '权限拒绝'];
const ACCESS_REFUSE_ERROR = [60100, '访问被拒绝'];//请求不合法资源
const ACCESS_TIMES_ERROR = [60200, '访问次数限制'];
}

View File

@@ -0,0 +1,84 @@
<?php
/*
* description
* authorwh
* email
* createTime{2019/11/12} {21:01}
*/
namespace wanghua\general_utility_tools_php\errorcode;
/**
* 系统级错误, 业务错误代码放在业务层,不要放在此处
* Class SystemError
* @package errorcode
*/
class Code
{
//region 系统级别错误
const SUCCESS = [200, 'SUCCESS'];
const REQUEST_ERROR = [10000, '请求错误'];//一般url资源错误url错误、敏感参数错误等
const REQUEST_TYPE_ERROR = [10001, '请求类型错误'];//ajax get post
const FAILED = [10002, '操作失败'];
const SOURCE_NOT_FIND = [10003, '资源找不到'];
const SYSTEM_ERROR = [500, '系统错误'];
const PERMISSION_DENIED = [10005, '权限拒绝'];
const ACCESS_REFUSE_ERROR = [10006, '访问被拒绝'];//请求不合法资源
const ACCESS_TIMES_ERROR = [10007, '访问次数限制'];
//endregion 系统级别错误
//region 短信错误
const MESSAGE_SEND_ERROR = [13500, '短信发送失败'];
const MESSAGE_SEND_ULTRALIMIT = [13600, '短信发送超限'];
const MESSAGE_VERIFY_FAILED = [13700, '短信验证失败'];
const MESSAGE_NUM_LITTLE = [13800, '短信余额不足'];
const MESSAGE_SEND_EXCEPTION = [13900, '短信发送异常'];
//endregion
//region 文件上传
const UPLOAD_FAILED = [14000, '上传失败'];
const UPLOAD_FILE_IS_EMPTY = [14000, '无文件上传'];
const UPLOAD_FILE_SIZE_EMPTY = [14100, '文件内容为空'];
const UPLOAD_FILE_SIZE_ERROR = [14200, '文件大小超过限制'];
const UPLOAD_FILE_MIME_ERROR = [14300, '文件类型错误'];
const UPLOAD_FILE_EXT_ERROR = [14500, '文件扩展名错误'];
const FILE_FROM_ERROR = [14520, '来源错误'];
//endregion
//region 数据库错误
const DATA_IS_EMPTY = [50000, '未获取到相关数据'];
const UPDATE_ERROR = [50001, '修改异常'];
const INSERT_ERROR = [50002, '数据保存异常'];
const SELECT_ERROR = [50003, '查询异常'];
const DATA_REPEAT_ERROR = [50005, '数据重复'];
const DATA_INSERT_ERROR = [50006, '数据入库异常'];
//endregion
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:49}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class DbError
{
//region 数据库错误
const DATA_IS_EMPTY = [16000, '未获取到相关数据'];
const UPDATE_ERROR = [16100, '修改异常'];
const INSERT_ERROR = [16200, '数据保存异常'];
const SELECT_ERROR = [16300, '查询异常'];
const DATA_REPEAT_ERROR = [16400, '数据重复'];
const DATA_INSERT_ERROR = [16500, '数据入库异常'];
//endregion
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:47}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class EmailError
{
//region 邮件错误
const EMAIL_RECEIVER_IS_EMPTY = [23100, '未设置收信人'];
const EMAIL_SEND_FAILED = [23200, '邮件发送失败'];
//endregion
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:48}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class FileUploadError
{
//region 文件上传
const UPLOAD_FAILED = [24000, '上传失败'];
const UPLOAD_FILE_IS_EMPTY = [24100, '无文件上传'];
const UPLOAD_FILE_SIZE_EMPTY = [24200, '文件内容为空'];
const UPLOAD_FILE_SIZE_ERROR = [24300, '文件大小超过限制'];
const UPLOAD_FILE_MIME_ERROR = [24400, '文件类型错误'];
const UPLOAD_FILE_EXT_ERROR = [24500, '文件扩展名错误'];
const FILE_SOURCE_ERROR = [24600, '文件来源错误'];
//endregion
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:59}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class LoginError
{
const OFF_LINE_ERROR = [30100, '未登录'];
const LOGIN_SESSION_INVALID = [30200, '登录已过期,请重新登录'];
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/3} {14:14}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class RequestError
{
//region 请求错误
const SUCCESS = [200, 'SUCCESS'];
const REQUEST_TYPE_ERROR = [50601, '请求类型错误'];//ajax get post
const SOURCE_NOT_FIND = [50400, '资源找不到'];
const SYSTEM_ERROR = [50500, '系统错误'];
const REQUEST_ERROR = [50600, '请求错误'];//一般url资源错误url错误、敏感参数错误等
const FAILED = [50602, '请求失败'];
//endregion 请求错误
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:49}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class SmsError
{
//region 短信错误
const MESSAGE_SEND_ERROR = [43500, '短信发送失败'];
const MESSAGE_SEND_ULTRALIMIT = [43600, '短信发送超限'];
const MESSAGE_VERIFY_FAILED = [43700, '短信验证失败'];
const MESSAGE_NUM_LITTLE = [43800, '短信余额不足'];
const MESSAGE_SEND_EXCEPTION = [43900, '短信发送异常'];
//endregion
}

View File

@@ -0,0 +1,15 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/7/1} {18:50}
*/
namespace wanghua\general_utility_tools_php\errorcode;
class SystemError
{
}

View File

@@ -0,0 +1,264 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/4/26} {9:45}
*/
namespace wanghua\general_utility_tools_php\es;
use wanghua\general_utility_tools_php\Date;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 废弃,即将删除
*
* 【es原生操作库】
* 【仅支持框架ThinkPHP5+、 PHP7.2+】
*
* BaseElasticsearch.php类库的替代库
*
* 使用步骤:
* 初始化设置ip、索引前缀-普通索引请设置为空字符串)
* 设置索引范围例如查询本月如果索引不是按日期规律设置请使用普通索引设置方法setIndexNormal并设置前缀为空字符串
* 设置查询方法eg: _search
* 设置查询参数(查询条件)
* 执行
* 返回结果
* Class ElasticsearchUtility
* @package app\admin\logic
*/
class Elasticsearch
{
protected $es_log_file = 'es_log';
//最终拼装的请求地址
protected $post_url = ''; //eg: $url = 'http://49.4.3.4:9111/qa-item-2021.04/_search';
//查询参数
protected $query_param = [];
//查询方法
protected $query_method = '_search';
//文档索引
protected $index = '';
//服务器ip
protected $ip = '';
//索引前缀(优先设置)
protected $index_sign = '';//eg: qa-stat-
//设置查询日期格式符号
protected $doc_date_sign = '.';
//设置是哪个方法调用本es库用于日志记录
protected $exe_func = '';
/**
* ElasticsearchUtility constructor.
* @param string $ip_port 协议+服务器ip+端口号,必须
* @param string $index_sign 当es索引为有规律的日期索引时可使用前缀并配合日期索引设置方法体验更佳
*/
public function __construct(string $ip_port, string $index_sign='')
{
$this->ip = false===strpos($ip_port,'http')?'http://'.$ip_port:$ip_port;
$this->index_sign = $index_sign;
}
/**
* desc设置查询方法默认_search
* authorwh
* @param $method_name
*/
function setQueryMethod($method_name){
$this->query_method = $method_name;
}
/**
* desc根据日期索引查询文档
* 注:默认一个月
*
* eg:'qa-stat-2021.01';
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
* authorwh
*/
function setIndexDefault(){
$this->setRequestIndex(date('Y'.$this->doc_date_sign.'m'));
}
/**
* desc设置要查询的日期文档索引
*
* 1、按年检索文档[建议5年左右]
*
* eg: qa-stat-2018.*,qa-stat-2019.*,qa-stat-2020.*,qa-stat-2021.*
*
* 注意当索引过长es会抛出索引太长异常
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time 开始时间 eg2010-01-01 08:05:55
* @param string $end_time 结束时间 eg2021-12-31 12:05:00
* @return string
*/
function setIndexYearDate(string $start_time, string $end_time){
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'*', strtotime($start_time));
$sign = ',';//分隔符
$m = date('Y', strtotime($end_time)) - date('Y', strtotime($start_time));
//拼装查询索引
for ($i=0; $i<$m; $i++){
$index .= $sign.($this->index_sign.(date('Y', strtotime($start_time))+$i).$this->doc_date_sign.'*');
}
$this->setRequestIndex($index);
}
/**
* desc设置日期索引
*
* 检索全部日期索引文档
*
* 例如es设置的索引为
* qa-stat-2021.01
qa-stat-2021.02
qa-stat-2021.03
qa-stat-2021.04
*
* 实际查询自动设置为qa-stat-*
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @return string
*/
function setIndexAllDate(){
$this->setRequestIndex('*');
}
/**
* desc设置普通索引
*
* 调用此方法请在初始化时设置索引前缀index_sign为""空字符串
*
* authorwh
* @param string $index
*/
function setIndexNormal(string $index){
$this->setRequestIndex($index);
}
/**
* desc设置跨月份索引
*
* eg: qa-stat-2021.01,qa-stat-2021.02
*
* 注如果es设置的索引没有规律也不是日期作为索引请设置普通索引setIndexNormal且请在初始化时设置索引前缀index_sign为""字符串
*
* authorwh
* @param string $start_time eg:'2020-11'
* @param string $end_time eg:'2021-01'
* @return string eg: qa-stat-2020.11,qa-stat-2020.12,qa-stat-2021.01
*/
function setIndexDate(string $start_time, string $end_time){
$date = new Date();
//计算月份
$m = $date->dateCutMonth($start_time, $end_time);
////解决跨年且不足1个月时索引设置错误问题
//if($m == 0 && (date('Y', strtotime($end_time)) > date('Y', strtotime($start_time)))){
// $m = 1;
//}
//格式
$date->date_format = 'Y-m';
//默认查询索引
$index = date('Y'.$this->doc_date_sign.'m', strtotime($start_time));
$tmp_time = $start_time;
$sign = ',';//分隔符
//拼装查询索引
for ($i=0; $i<$m; $i++){
$tmp_time = $date->addTime(1, 'M', strtotime($tmp_time));
$index .= $sign.$this->index_sign.(date('Y'.$this->doc_date_sign.'m', strtotime($tmp_time)));
}
$this->setRequestIndex($index);
}
/**
* desc设置查询参数
* authorwh
* @param string $query_param_json
*/
function setQueryParam(string $query_param_json){
$this->query_param = $query_param_json;
$this->post_url = $this->ip.'/'.$this->index.'/'.$this->query_method;
}
/**
* desc获取查询参数用于调试
* authorwh
* @return array
*/
function getQueryParam(){
return [
'ip'=>$this->ip,
'index_sign'=>$this->index_sign,
'index'=>$this->index,
'query_method'=>$this->query_method,
'post_url'=>$this->post_url,
'query_param'=>$this->query_param,
];
}
/**
* desc执行es查询
* authorwh
* @return array|bool|int|string
* @throws \Exception
*/
function execute(){
if(empty($this->ip)) throw new \Exception('请设置ip');
//if(empty($this->index_sign)) throw new \Exception('请设置索引前缀');
if(empty($this->index)) throw new \Exception('请设置索引');
if(empty($this->query_method)) throw new \Exception('请设置查询方法');
if(empty($this->query_param)) throw new \Exception('请设置查询参数');
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'request_url'=>$this->post_url,'IN'=>" | IN: ",'query_params'=>$this->query_param, 'input'=>input()], $this->es_log_file);
$result = Tools::curl_post($this->post_url, $this->query_param);
Tools::log_to_write_txt(['exe_func'=>$this->exe_func,'OUT'=>" | OUT: ", 'result'=>$result], $this->es_log_file);
return $result;
}
/**
* desc设置是哪个方法调用本es库用于日志记录
* authorwh
* @param string $function
*/
function setExecuteFunction(string $function){
$this->exe_func = $function;
}
/**
* desc设置请求文档索引
* authorwh
* @param string $index
*/
protected function setRequestIndex(string $index){
$this->index = $this->index_sign.$index;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:01}
*/
namespace wanghua\general_utility_tools_php\exception;
use think\Exception;
class BaseException extends Exception
{
}

View File

@@ -0,0 +1,27 @@
# 系统业务级异常类
1. api异常
2. 请求异常
3. api异常
4. 响应异常
5. 待扩展......
##使用说明
例1
if(empty($params)){
//参数错误,抛出空参数异常
throw new RequestException(RequestException::EMPTY_PARAM_ERROR);
}
例2
//假设返回结果格式为:['code'=>200, 'msg'=>'操作成功', 'data'=>[]]
if(is_null($result['code'])){
//无code字段抛出响应格式错误
throw new ResponseException(ResponseException::RESPONSE_FORMAT_ERROR);
}

View File

@@ -0,0 +1,60 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/03/08} {14:17}
*/
namespace wanghua\general_utility_tools_php\exception;
use think\exception\Handle;
use think\Request;
use wanghua\general_utility_tools_php\SundryConfig;
use wanghua\general_utility_tools_php\tool\EmailTool;
use wanghua\general_utility_tools_php\tool\Tools;
class SystemException extends Handle
{
public function render(\Exception $e)
{
// 参数验证错误
//if ($e instanceof ValidateException) {
// return json($e->getError(), 422);
//}
// 请求异常
//if ($e instanceof HttpException && request()->isAjax()) {
// return response($e->getMessage(), $e->getStatusCode());
//}
Tools::log_to_write_txt([
'error'=>'系统错误.'.$e->getMessage(),
'input'=>Request::instance()->input(),
'error_info'=>$e->getTraceAsString()
]);
$title = '新的【系统异常】,请立即处理';
//邮件通知管理员
$mail_content = '<p>error'.$e->getMessage().'</p>';
$mail_content .= '<p>url'.request()->url(true).'</p>';
$mail_content .= '<p>ip'.request()->ip().'</p>';
$mail_content .= '<p>详情请登录宝塔查看日志</p>';
$emails = SundryConfig::val('admin_error_log_email');
if(config('sys_env') == 'PROD'){//线上环境才发错误邮件
EmailTool::email_to_person($title,$mail_content,$emails);
}
if(request()->LogObj){
request()->LogObj->flush();
}
if(request()->ServeLogObj){
request()->ServeLogObj->flush();
}
//可以在此交由系统处理
return parent::render($e);
}
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:04}
*/
namespace wanghua\general_utility_tools_php\exception\api;
use wanghua\general_utility_tools_php\exception\BaseException;
class ApiException extends BaseException
{
const API_RESPONSE_FORMAT_ERROR='The api response result is incorrectly formatted: code is not exist.';
const EMPTY_PARAM_ERROR ='param is empty.';
const EMPTY_URL_ERROR ='url of param is empty.';
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:13}
*/
namespace wanghua\general_utility_tools_php\exception\request;
use wanghua\general_utility_tools_php\exception\BaseException;
class RequestException extends BaseException
{
const EMPTY_PARAM_ERROR ='param is empty.';
const EMPTY_URL_ERROR ='url of param is empty.';
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/14} {10:48}
*/
namespace wanghua\general_utility_tools_php\exception\response;
use wanghua\general_utility_tools_php\exception\BaseException;
class ResponseException extends BaseException
{
const RESPONSE_FORMAT_ERROR='The response result is incorrectly formatted.';
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/1/16} {17:15}
*/
namespace wanghua\general_utility_tools_php\fastadmin;
use app\common\model\TabConf;
use think\Db;
/**
* 为fastadmin框架定制的功能
*
* Class FastadminTools
* @package wanghua\general_utility_tools_php\fastadmin
*/
class FastadminTools
{
/**
* desc线上需要自动隐藏的菜单
*
* 当你有这种需求:
* 我的菜单只需要本地显示,线上或非本地环境不需要显示时,就可以使用此方法。
* 为什么需要此方法?
* 因为,我每次在本地增加功能并新增菜单之后,都需要把本地的菜单同步到线上,但是命令管理菜单,或者有些只需要开发者才能
* 动的菜单,需要在线上隐藏。我又不想每次同步之后去改菜单表。
* 手动去菜单表改也是可以,但是次数多了就很麻烦,有时候会忘记。
*
* 如:命令管理菜单、开发者相关配置
*
* authorwh
* @param array $menu_arr 需要隐藏的菜单数组
* @param string $sys_env
* @return bool
*/
static function auto_hidden_menu_online(array $menu_arr, string $sys_env){
foreach ($menu_arr as $menu_name){
if($sys_env == 'LOCAL'){
//本地显示
Db::table('fa_auth_rule')->where('name',$menu_name)->data(['status'=>'normal'])->update();
}else{
Db::table('fa_auth_rule')->where('name',$menu_name)->data(['status'=>'hidden'])->update();
}
}
return true;
}
}

View File

@@ -0,0 +1,236 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/2/4} {16:59}
*/
namespace wanghua\general_utility_tools_php\file;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 文件、文件夹操作类
*
* eg读取文件夹、复制文件等
*
* Class FileOperate
* @package wanghua\general_utility_tools_php\file
*/
class File
{
public $file_ext = '*.*';//扩展名
public $unzip_path = '';//解压路径
/**
* desc读取文件和文件夹
*
* authorwh
* @param $folderPath 读取目标路径下的文件夹和文件
*
* @param $dirs 存储文件路径 eg:D:\wanghua\old_files
*
//array(3) {
// [0] => array(1) {
// ["path"] => string(25) "D:\wanghua\test_files/111"
// }
// [1] => array(1) {
// ["path"] => string(25) "D:\wanghua\test_files/222"
// }
//}
*
*
*
*
* @param $file_name_arr 存储文件路径、文件名称
*
//array(64) {
// [0] => array(3) {
// ["path"] => string(25) "D:\wanghua\test_files/111"
// ["path_file"] => string(33) "D:\wanghua\test_files/111/111.txt"
// ["filename"] => string(7) "111.txt"
// }
// [1] => array(3) {
// ["path"] => string(25) "D:\wanghua\test_files/111"
// ["path_file"] => string(37) "D:\wanghua\test_files/111/1111111.txt"
// ["filename"] => string(11) "1111111.txt"
// }
//}
*/
function read_folder($folderPath,&$dirs,&$file_name_arr) {
// 获取指定目录下所有文件和文件夹
$files = scandir($folderPath);
foreach ($files as $file) {
// 如果当前项为文件夹且不是"."或者".."
if (is_dir("$folderPath/$file") && !in_array($file, array('.', '..'))) {
$dirs[] = ['path'=>$folderPath.'/'.$file];//此时的$file是文件夹名称
// 调用自身进行递归操作
$this->read_folder("$folderPath/$file",$dirs,$file_name_arr);
} elseif (!in_array($file, array('.', '..'))){ // 如果当前项为文件而非"."或者".."
$file_name_arr[] = ['path'=>$folderPath,'path_file'=>"$folderPath/$file",'filename'=>$file];
}
}
}
/**
* desc复制文件
*
* authorwh
* @param array $sourceFileArr 源文件路径数组 path,path_file,filename
*
* @param string $destination_path 目标存放路径 D:\wanghua\test_files\new_files
*
* @param array $cp_type_arr 允许复制的文件后缀 eg:['jpg','png,'spine']
*
* @param bool $is_cover 是否覆盖 默认false不覆盖会以源文件名+微秒时间戳格式作为新的文件名
*
* @param bool $is_continue 相同文件是否跳过复制 默认 true
*
* 实战案例:
*
function testcopy(){
$source_dir = 'D:\wanghua\test_files';//源文件路径
$destination_path = 'D:\wanghua\test_files\new_files';//目标文件路径
$dirs = [];//存储文件路径
$file_name_arr = [];//含文件路径、名称
$file_obj = new File();
$file_obj->read_folder($source_dir,$dirs,$file_name_arr);
//dump($dirs);
//dump($file_name_arr);die;
$file_obj->copy($file_name_arr,$destination_path,['jpg','png,'spine']);
}
*
*/
function copy(array $sourceFileArr, string $destination_path, $cp_type_arr=['jpg','png'], $is_cover=false, $is_continue=false){
//设置不超时
set_time_limit(0);
//源文件路径 => 目标文件路径
foreach ($sourceFileArr as $key=>$file_arr){
if(!file_exists($file_arr['path_file'])){
continue;
}
//验证目标文件夹路径
if(!file_exists($destination_path)){
mkdir($destination_path,0777,true);
}
$name_arr = explode('.',$file_arr['filename']);
//验证符合条件的文件类型
if(!in_array($name_arr[1],$cp_type_arr)){
continue;
}
if(file_exists($destination_path.'/'.$file_arr['filename'])){
if($is_continue){
continue;//相同文件跳过复制
}
//覆盖
if($is_cover){
copy($file_arr['path_file'], $destination_path.'/'.$file_arr['filename']);
}else{
//不覆盖,名称区分
$microsecond = Tools::getMicrosecond();
copy($file_arr['path_file'], $destination_path.'/'.$name_arr[0].'('.$microsecond.').'.$name_arr[1]);
}
}else{
copy($file_arr['path_file'], $destination_path.'/'.$file_arr['filename']);
}
}
}
/**
* desc递归删除目录
*
* $dir 物理路径
*
* authorwh
*/
function recursive_delete($dir) {
if (!is_dir($dir)) {
return unlink($dir);
}
$handle = opendir($dir);
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $file;
if (is_dir($path)) {
$this->recursive_delete($path);
} else {
unlink($path);
}
}
closedir($handle);
rmdir($dir);
}
/**
* desc解压ZIP文件并存储在本地
* authorwh
* @param resource $file_stream 文件流
* 例如:$response = $client->request('POST', $url, [
'headers' => $headers,
'body' => $body,
]);
// 处理响应
//$file_stream = $response->getBody();
* @param string $save_path 存储路径
* @return array
* @throws \Exception
*/
function unzip($file_stream,$save_path){
$zpi_file_name = 'zip_tmp_file_'.Tools::rand_str(20).'.zip';
//这个目录将会在结束时删除
$zipTempPath = $save_path.'/tmp_zip_files/'; // 临时存放ZIP文件的路径
//压缩文件保存目录
if(!file_exists($zipTempPath)){
mkdir($zipTempPath,0777,true);
}
$zpi_file = $zipTempPath.$zpi_file_name;
// 保存ZIP文件到临时路径
file_put_contents($zpi_file, $file_stream);
$this->unzip_path = $zipTempPath.'unzip_files/';
//解压目录
if(!file_exists( $this->unzip_path)){
mkdir( $this->unzip_path,0777,true);
}
// 解压ZIP文件
$zip = new \ZipArchive();
if ($zip->open($zpi_file) === TRUE) {
$zip->extractTo( $this->unzip_path);//移动到临时解压目录
$zip->close();
//遍历所有文件(file_ext后缀自行指定),遍历部分文件,如:*.json,*.zip,*.mp4,*.mp3等
$wavFiles = $this->glob_by_ext( $this->unzip_path.$this->file_ext);
//$urls = [];
//// 遍历解压后的文件生成URL
//foreach ($wavFiles as $wavFilePath) {
// // 生成外部访问的URL
// $downloadUrl = request()->domain() . explode('public',$wavFilePath)[1];
// $urls[] = $downloadUrl;
//}
//// 返回所有WAV文件的URL
//return ['wav_urls' => $urls];
return $wavFiles;
} else {
throw new \Exception('Failed to open ZIP file.');
}
}
/**
* desc获取指定扩展类型的文件列表
*
* authorwh
* @param string $file_ext 如: wav,pm4,mp3,json,zip
* @return array|false
*/
function glob_by_ext($file_ext='*'){
return glob( $this->unzip_path.'*.'.$file_ext);
}
}

View File

@@ -0,0 +1,5 @@
## 文件处理
#### 文件夹读取,文件复制
#### 文件上传

View File

@@ -0,0 +1,764 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/4/3} {9:23}
*/
namespace wanghua\general_utility_tools_php\file\upload;
use wanghua\general_utility_tools_php\oss\alicloud\Objects;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Utils;
use wanghua\general_utility_tools_php\huawei\obs\InitObs;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\oss\huawei\obs\Config;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 上传组件
* 支持单图,多图,单文件,多文件
* Class FileUpload
* @package libraries
*/
class FileUpload
{
//文件扩展
protected $file_ext = 'ico,jpg,jpeg,png,gif,pdf,zip,rar,xls,xlsx,doc,docx';
//图片扩展
protected $img_ext = 'ico,jpg,jpeg,png,gif';
public $unique_key = '';//唯一key临时文件会存储至该目录推荐使用用户id
public $file_lists_urls = [];//要读取的文件地址列表
/**
* 文件存放目录
*
* 注意:后面不要“/”
* 注意:后面不要“/”
* 注意:后面不要“/”
* @var string
*/
private $file_dir = '/uploads';
/**
* desc默认目录/uploads
*
* eg/uploads/user/head_image 或 /files_manage/goods/images
*
* 注意:后面不要“/”
* 注意:后面不要“/”
* 注意:后面不要“/”
*
* authorwh
* @param $file_dir
*/
function setFileDir($file_dir){
$this->file_dir = $file_dir;
}
/**
* desc单图
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 大小,默认2M
* @param string $ext 允许的扩展
* @return array
*/
function image($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file($file_upload_name);
if(empty($file)){
return Tools::set_fail('请上传文件');
}
// 移动到框架应用根目录/uploads/ 目录下
$outer_req_url = $this->file_dir.'/image';
$physics_path = 'public'.$outer_req_url;
$root_path = explode('vendor',__DIR__)[0];
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($file)){
return Tools::set_res(1, '上传文件不存在');
}
if(empty($ext)) $ext = $this->img_ext;
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
return Tools::set_res(0, '上传成功', $result);
}else{
// 上传失败获取错误信息
return Tools::set_res(1, $file->getError());
}
}
/**
* desc多图
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 大小,默认2M
* @param string $ext 允许的扩展
* @return array
*/
function images($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
// 获取表单上传文件
$files = request()->file($file_upload_name);
if(empty($files)){
return Tools::set_fail('请上传文件');
}
$outer_req_url = $this->file_dir.'/image';
$physics_path = 'public'.$outer_req_url;
$root_path = explode('vendor',__DIR__)[0];
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($ext)) $ext = $this->img_ext;
$result = [];
$error = [];
foreach($files as $file){
// 移动到框架应用根目录/uploads/ 目录下
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result[] = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
}else{
//忽略错误
// 上传失败获取错误信息
$error[] = $file->getError();
}
}
$msg = '上传成功';
$code = 0;
if(count($error) > 0){
$msg = '上传失败;提示:'.implode(',', $error);//顺带返回错误信息
$code = 1;
}
return Tools::set_res($code, $msg, $result);
}
/**
* desc单文件
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 大小,默认10M
* @param string $ext 允许的扩展
* @return array
*/
function file($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file($file_upload_name);
if(empty($file)){
return Tools::set_fail('请上传文件');
}
// 移动到框架应用根目录/uploads/ 目录下
$outer_req_url = $this->file_dir.'/file';
$physics_path = 'public'.$outer_req_url;
$root_path = explode('vendor',__DIR__)[0];
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($file)){
return Tools::set_res(1, '上传文件不存在');
}
if(empty($ext)) $ext = $this->file_ext;
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
return Tools::set_res(0, '上传成功', $result);
}else{
// 上传失败获取错误信息
return Tools::set_res(1, $file->getError());
}
}
/**
* desc多文件
*
* 【必须确认php.ini中upload_max_filesize的配置足够大否则上传失败但不会报错
*
* authorwh
* @param string $file_upload_name 表单文件元素name值
* @param int $size 单文件大小,默认10M
* @param string $ext 允许的扩展
* @return array
*/
function files($file_upload_name = 'file_upload', $size=10, $ext=''){
if(!$this->uploadMaxFilesizeCheck($size)){
return Tools::set_res(1, '上传文件受限');
}
$root_path = explode('vendor',__DIR__)[0];
// 获取表单上传文件
$files = request()->file($file_upload_name);
if(empty($files)){
return Tools::set_fail('请上传文件');
}
$outer_req_url = $this->file_dir.'/file';
$physics_path = 'public'.$outer_req_url;
$upload_dir = $root_path.$physics_path;
if(!is_dir($upload_dir)){
mkdir($upload_dir, 0777, true);
}
if(empty($ext)) $ext = $this->file_ext;
$result = [];
$error = [];
foreach($files as $file){
// 移动到框架应用根目录/uploads/ 目录下
$info = $file->validate(['size'=>$size * 1024 * 1024, 'ext'=>$ext])->move($upload_dir);
if($info){
//原文件名
$source_file_name = $info->getInfo('name');
//文件类型
$file_type = $info->getInfo('type');
// 输出扩展名 jpg
$extension = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$save_name = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
$new_filename = $info->getFilename();
$relative_url = $outer_req_url.'/'.str_replace('\\', '/', $save_name);
$result[] = [
'prefix'=>request()->domain().$outer_req_url,//访问前缀
'source_file_name'=>$source_file_name,
'file_type'=>$file_type,
'extension'=>$extension,
'save_name'=>str_replace('\\', '/', $save_name),
'new_filename'=>$new_filename,
'real_path'=>$this->dealPath($root_path.'public'.$relative_url),//物理路径
'outer_req_url'=>request()->domain().$relative_url,
'outer_req_relative_url'=>$relative_url,
'size'=>$info->getSize(),
];
}else{
//忽略错误
// 上传失败获取错误信息
$error[] = $file->getError();
}
}
$msg = '上传成功';
$code = 0;
if(count($error) > 0){
$msg = '上传失败;提示:'.implode(',', $error);//顺带返回错误信息
$code = 1;
}
return Tools::set_res($code, $msg, $result);
}
/**
* desc单文件或多文件上传至阿里云OSS服务
*
* authorwh
* @param array $config 配置
* //阿里云oss配置
'aliyun_oss_config' => [
//项目应用名称
'bucket'=>'wanlliuyinli-adm',//每创建一个bucket必须标明前缀代表这个bucket属于哪个项目
'UserPrincipalName'=>'wanghua@1113242774600735.onaliyun.com',
'Password'=>'0osE4cGo%tllnP1|uOQADPhM}Y?obR4U',
'AccessKeyId'=>'LTAI5tPqn1n7jugviVoGqFfa',
'AccessKeySecret'=>'BRoB5TdcUAFEuIR11BbN3R47Cm4Yep',
//https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints?spm=a2c4g.11186623.0.0.41ae2effA6oZar
'region_id'=>'cn-chengdu',//这里配置不能包含oss-前缀否则提示Invalid signing region in Authorization header
//西南1成都
//oss-cn-chengdu
//oss-cn-chengdu.aliyuncs.com
//oss-cn-chengdu-internal.aliyuncs.com
'endpoint'=>'oss-cn-chengdu.aliyuncs.com',//成都节点
'sts_endpoint'=>'sts.cn-chengdu.aliyuncs.com',//sts服务节点
]
* @param string $unique_id 唯一id可以是用户id可以是其它用于区分的标识默认存放在项目名称/控制器/方法/(唯一标识,可为空)/年月日/*.jpg
* @param string $file_upload_name 文件上传控件的name值必须带[]例如file_upload[]
* @param string $oss_type oss类型可选值ali_cloud 阿里云hua_wei 华为云(待扩展)
* @param int $size 单文件大小,默认10M
* @param string $ext 允许的文件扩展名
* @return array
*/
function filesUploadToAliCloudOss($config,$unique_id='',$file_upload_name = 'file_upload',$size=10, $ext=''){
return Mmodel::catch(function ()use ($config,$unique_id,$file_upload_name,$size,$ext){
$ini_upload_max_filesize = str_replace('M','',ini_get('upload_max_filesize'));
if(($size > $ini_upload_max_filesize)){
return Tools::set_res(1, '上传文件size受限不能超过upload_max_filesize='.ini_get('upload_max_filesize').'M');
}
// 获取表单上传文件
$files = request()->file($file_upload_name);
if(empty($files)){
return Tools::set_fail('请上传文件');
}
//扩展
if(empty($ext)) $ext = $this->file_ext;
//初始化阿里云OSS客户端
$oss_obj= new Objects($config);
//默认存储地址
$file_save_dir = $this->setFileSaveDir($unique_id);
$urls = [];
foreach($files as $file){
if(!$file->isValid()){
return Tools::set_fail('文件不合法');
}
$check_size = $file->checkSize($size*1024*1024);
if(!$check_size){
return Tools::set_fail('文件大小超限');
}
$check_res = $file->checkExt($ext);
if(!$check_res){
return Tools::set_fail('请上传合法文件');
}
$extension = explode('.',$file->getInfo('name'))[1];
$new_filename = 'file_'.Tools::rand_str(18).'.'.explode('.',$file->getInfo('name'))[1];
//必须加后缀
$filename = $file_save_dir.$new_filename;
// 从文件流上传
$res = $oss_obj->fileUpload($filename, $file->getInfo('tmp_name'));//上传之前的临时文件物理地址
$urls[] = [
'prefix'=>explode('.com',$res['info']['url'])[0].'.com',//访问前缀
'source_file_name'=>$file->getInfo('name'),//源文件名
'size'=>$file->getInfo('size'),//大小(b)
'extension'=>$extension,//扩展名
'file_type'=>$file->getInfo('type'),//文件后缀
'save_name'=>$filename,//保存文件名
'new_filename'=>$new_filename,//新文件名
'real_path'=>$res['info']['url'],//实际路径
'outer_req_url'=>$res['info']['url'],//外部访问地址
'outer_req_relative_url'=>$res['info']['url'],//外部访问相对地址
];
}
return Tools::set_ok('ok', $urls);
});
}
/**
* desc单文件上传至阿里云OSS
*
* authorwh
* @param array $config 配置
* //阿里云oss配置
'aliyun_oss_config' => [
//项目应用名称
'bucket'=>'wanlliuyinli-adm',//每创建一个bucket必须标明前缀代表这个bucket属于哪个项目
'UserPrincipalName'=>'wanghua@1113242774600735.onaliyun.com',
'Password'=>'0osE4cGo%tllnP1|uOQADPhM}Y?obR4U',
'AccessKeyId'=>'LTAI5tPqn1n7jugviVoGqFfa',
'AccessKeySecret'=>'BRoB5TdcUAFEuIR11BbN3R47Cm4Yep',
//https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints?spm=a2c4g.11186623.0.0.41ae2effA6oZar
'region_id'=>'cn-chengdu',//这里配置不能包含oss-前缀否则提示Invalid signing region in Authorization header
//西南1成都
//oss-cn-chengdu
//oss-cn-chengdu.aliyuncs.com
//oss-cn-chengdu-internal.aliyuncs.com
'endpoint'=>'oss-cn-chengdu.aliyuncs.com',//成都节点
'sts_endpoint'=>'sts.cn-chengdu.aliyuncs.com',//sts服务节点
]
* @param string $unique_id 唯一id可以是用户id可以是其它用于区分的标识默认存放在项目名称/控制器/方法/(唯一标识,可为空)/年月日/*.jpg
* @param string $file_upload_name 文件上传控件的name值例如file_upload
* @param string $oss_type oss类型可选值ali_cloud 阿里云hua_wei 华为云(待扩展)
* @param int $size 单文件大小,默认10M
* @param string $ext 允许的文件扩展名
* @return array
*/
function fileUploadToAliCloudOss($config,$unique_id='',$file_upload_name = 'file_upload',$size=10, $ext=''){
return Mmodel::catch(function ()use ($config,$unique_id,$file_upload_name,$size,$ext){
$ini_upload_max_filesize = str_replace('M','',ini_get('upload_max_filesize'));
if(($size > $ini_upload_max_filesize)){
return Tools::set_res(1, '上传文件size受限不能超过upload_max_filesize='.ini_get('upload_max_filesize').'M');
}
// 获取表单上传文件
$file = request()->file($file_upload_name);
if(empty($file)){
return Tools::set_fail('请上传文件');
}
//扩展
if(empty($ext)) $ext = $this->file_ext;
//初始化阿里云OSS客户端
$oss_obj= new Objects($config);
//默认存储地址
$file_save_dir = $this->setFileSaveDir($unique_id);
$urls = [];
if(!$file->isValid()){
return Tools::set_fail('文件不合法');
}
$check_size = $file->checkSize($size*1024*1024);
if(!$check_size){
return Tools::set_fail('文件大小超限');
}
$check_res = $file->checkExt($ext);
if(!$check_res){
return Tools::set_fail('请上传合法文件');
}
$extension = explode('.',$file->getInfo('name'))[1];
$new_filename = 'file_'.Tools::rand_str(18).'.'.explode('.',$file->getInfo('name'))[1];
//必须加后缀
$filename = $file_save_dir.$new_filename;
// 从文件流上传
$res = $oss_obj->fileUpload($filename, $file->getInfo('tmp_name'));//上传之前的临时文件物理地址
$urls[] = [
'prefix'=>explode('.com',$res['info']['url'])[0].'.com',//访问前缀
'source_file_name'=>$file->getInfo('name'),//源文件名
'size'=>$file->getInfo('size'),//大小(b)
'extension'=>$extension,//扩展名
'file_type'=>$file->getInfo('type'),//文件后缀
'save_name'=>$filename,//保存文件名
'new_filename'=>$new_filename,//新文件名
'real_path'=>$res['info']['url'],//实际路径
'outer_req_url'=>$res['info']['url'],//外部访问地址
'outer_req_relative_url'=>$res['info']['url'],//外部访问相对地址
];
return Tools::set_ok('ok', $urls);
});
}
/**
* desc设置文件保存目录
* authorwh
* @param string $unique_id 唯一id可以是用户id可以是其它用于区分的标识
* @return string
*/
function setFileSaveDir($unique_id=''){
if($unique_id){
$unique_id = $unique_id.'/';
}
//项目名称
$project_name = Tools::get_project_name();
//控制器方法路径
$ctl = strtolower(request()->controller().'/'.request()->action());
return $project_name.'/'.$ctl.'/'.$unique_id.date('Ymd').'/';//日期
}
/**
* desc检测size是否在ini配置范围
*
* authorwh
* @param $size
* @return bool
*/
private function uploadMaxFilesizeCheck($size){
$ini_upload_max_filesize = str_replace('M','',ini_get('upload_max_filesize'));
return $size<=$ini_upload_max_filesize;
}
/**
* desc上传base64格式图片【文件不用base64传输】
* authorwh
* @param string $file_upload_name
* @param int $size
* @param array $ext
* @return array
* @throws \Exception
*/
function uploadBase64Img($file_upload_name = 'file_upload', $size=10, $ext=''){
$source = input($file_upload_name, 'file_upload');
$file_size = strlen($source);
if($file_size > $size * 1024 * 1024){
return Tools::set_res(1, '上传失败,大小限制');
}
if(preg_match('/^(data:\s*image\/(\w+);base64,)/', $source, $result) < 1){
return Tools::set_res(1, '上传文件不合法');
}
$type = $result[2];
if(empty($ext)) $ext = $this->file_ext;
if(!in_array($type, explode($ext))){
return Tools::set_res(1, '上传失败,类型限制');
}
$result = base64_image_content($source);
if(false === $result){
return Tools::set_res(1, '上传失败');
}
return Tools::set_res(0, '上传成功', $result);
}
/**
* desc批量上传base64格式图片【文件不用base64传输】
* authorwh
* @param string $file_upload_name
* @param int $size
* @param array $ext
* @return array
* @throws \Exception
*/
function uploadBase64Imgs($file_upload_name = 'file_upload', $size=10, $ext=''){
$source_list = input($file_upload_name, 'file_upload');
if(!is_array($source_list)){
return Tools::set_res(1, '参数错误');
}
if(empty($ext)) $ext = $this->file_ext;
$result = [];
$error = [];
foreach ($source_list as $source){
$file_size = strlen($source);
if($file_size > $size * 1024 * 1024){
$error[] = '上传失败,大小限制';
continue;
}
if(preg_match('/^(data:\s*image\/(\w+);base64,)/', $source, $temp) < 1){
$error[] = '上传文件不合法';
continue;
}
$type = $temp[2];
if(!in_array($type, explode(',', $ext))){
$error[] = '类型限制';
continue;
}
$up_result = base64_image_content($source);
if(false === $up_result){
$error[] = '上传失败';
continue;
}
$result[] = $up_result;
}
$msg = '上传成功';
$code = 0;
if(count($error) > 0){
$msg = '上传失败;提示:'.implode(',', $error);//顺带返回错误信息
$code = 1;
}
return Tools::set_res($code, $msg, $result);
}
/**
* desc优化路径格式
* authorwh
*/
private function dealPath(string $path){
$str = str_replace('\\','/',$path);
return str_replace('//','/',$str);
}
/**
* desccurl上传文件php上传文件到远程服务器地址
*
* [php curl模拟文件上传]
*
* 依赖库:"guzzlehttp/guzzle": "^7.8",
*
* authorwh
* @param $url
* @param $params
* @param false[] $config
*/
function uploadCurlFiles($url,$params, $config=['verify' => false]){
if(empty($this->unique_key)){
throw new \Exception('UNIQUE_KEY 不能为空');
}
if(empty($this->file_lists_urls)){
throw new \Exception('文件地址列表不能为空');
}
// 创建 Guzzle HTTP 客户端实例
$client = new Client($config);
// 定义要上传的文件列表
$files = [
//[
// 'name' => 'file1', // 服务器接收的文件字段名
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\dc1c441d81d48565ac6817b89d0f8bef.mp4', 'r'), // 文件路径
// 'filename' => 'dc1c441d81d48565ac6817b89d0f8bef.mp4', // 文件名,可自定义
//],
//[
// 'name' => 'files',
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\f80179b23b60619fba8033bfd64f8817.mp4', 'r'),
// 'filename' => 'f80179b23b60619fba8033bfd64f8817.mp4',
//],
//['name'=>'prompt','contents'=>$prompt],
//['name'=>'tts_url','contents'=>$tts_url]
// 可以继续添加更多文件...
];
//print_r($params);
foreach ($params as $key=>$val){
$files[] = ['name'=>$key,'contents'=>$val];
}
$controller = strtolower(request()->controller());
$action = strtolower(request()->action());
$save_path = Tools::get_root_path() . "public/uploads/{$controller}/{$action}/".$this->unique_key;
if(!file_exists($save_path)){
mkdir($save_path,0777,true);
}
//文件远程可访问地址列表
foreach ($this->file_lists_urls as $name=>$video_url) {
//处理可用地址可能存储在本地或oss
$video_url = $this->get_usable_url($video_url);
$files[] = [
'name'=>"files",//服务器接收的文件字段名
//读取地址文件流(本地地址或远程地址均可,本地真实物理地址也行)
'contents'=>Utils::tryFopen($video_url, 'r'),
'filename'=>$name,//可自定义 文件名
];
}
// 构建多部分表单数据
$multipartStream = new MultipartStream($files);
$boundary = $multipartStream->getBoundary();
$headers = [
'Content-Type' => "multipart/form-data; boundary={$boundary}",
];
// 准备请求体
$body = $multipartStream->getContents();
// 发起 POST 请求
try {
$postinfo = [
'headers' => $headers,
'body' => $body,
];
//上传
$response = $client->request('POST', $url, $postinfo);
// 处理响应
//$responseBody = (string) $response->getBody();
// 检查状态码
//if ($response->getStatusCode() !== 200) {
// return false;
//}
return $response;//返回上传结果
} catch (\GuzzleHttp\Exception\RequestException $e) {
// 错误处理
//echo "Error: " . $e->getMessage();
Tools::error_txt_log($e);
return false;
}
}
/**
* desc获取视频可用地址兼容远程地址或本地地址
* authorwh
* @param $video_url
* @return string|string[]
*/
private function get_usable_url($video_url){
if(strpos($video_url,'http')!==false){
return $video_url;//远程地址
}
return Tools::get_root_path().$video_url;//本地地址
}
}

View File

@@ -0,0 +1 @@
## 文件上传类库

View File

@@ -0,0 +1,210 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/25} {15:45}
*/
namespace wanghua\general_utility_tools_php\framework;
use app\common\model\TabConf;
use think\Cache;
use think\Controller;
use think\Db;
use think\Exception;
use think\Request;
use wanghua\general_utility_tools_php\api\ApiDocument;
use wanghua\general_utility_tools_php\tool\MySqlTools;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 基础架构
*
* Class BaseController
* @package app\index\controller
*/
class BaseController extends Controller
{
public function __construct()
{
parent::__construct();
//校验系统维护状态 start
$chm = $this->checkMaintain();
if ($chm['is_maintain']) {
if ($chm['users_ids']) {
//解析openid
if (!in_array(api_user_info('id'), explode(',', $chm['users_ids']))) {
//白名单之外维护中
//Tools::log_to_write_txt([
// '维护测试'=>$chm['users_ids'],
// 'my'=>index_user_openid()
//]);
return $this->error($chm['msg']);
}
} else {
//不存在,直接维护中
return $this->error($chm['msg'] . '');
}
}
//校验系统维护状态 end
}
/**
* desc检查路径维护状态
*
* “===”完全匹配不能使用like
*
* authorwh
*/
protected function checkMaintain(){
$configs = Db::table(TabConf::$fa_sys_maintain_config)
->where('status','1')
->cache()
->select();
//模块
$strmodule = request()->module();
foreach ($configs as $config){
if($strmodule == $config['url']){
//模块维护中
return ['is_maintain'=>true,'msg'=>$config['msg'],'users_ids'=>$config['users_ids']];
}
}
//模块/控制器
$strcontroller = strtolower(request()->module().'/'.request()->controller());
foreach ($configs as $config){
if($strcontroller == $config['url']){
//模块维护中
return ['is_maintain'=>true,'msg'=>$config['msg'],'users_ids'=>$config['users_ids']];
}
}
//模块/控制器/方法
$straction = strtolower(request()->module().'/'.request()->controller().'/'.request()->action());
foreach ($configs as $config){
if($straction == $config['url']){
//模块维护中
return ['is_maintain'=>true,'msg'=>$config['msg'],'users_ids'=>$config['users_ids']];
}
}
//未维护
return ['is_maintain'=>false,'msg'=>'服务运行中'];
}
/**
* desc清空系统缓存
*
/index/Test/clearCache
*
* authorwh
*/
function clearCache(){
Cache::clear();
}
/**
* eg/index/test/buildTablesConf
*
* desc构建统一的表名配置
*
* authorwh
*/
function buildTablesConf(){
(new MySqlTools())->buildTablesConf();
}
/**
* desc创建&更新接口文档
*
* 默认存放在/public/api_docs/api_list.md
*
* authorwh
*/
function buildApiDoc()
{
$obj = new ApiDocument();
//根据自己的实际情况设置直接继承类(仅供参考)
$obj->extends_base_class = 'app\\api\\controller\\BaseHttpApi,wanghua\\general_utility_tools_php\\framework\\base\\BaseWechatAuthController';
//设置过滤的类(仅供参考)
$obj->setFilterClass([
'BaseCommonController',
'BaseHttpApi',
'BaseWssApi',
'Wsspush',
'BaseController',
'BaseAuthController',
//'BaseWechatAuthController',
'BasePublicController'
]);
$obj->setFilterFunction([
'buildApiDoc',
'buildTablesConf',
'checkfailed',
'clearCache',
'defaultAuth',
'operateLog',
'checkMaintain'
]);
//构建接口文档
$obj->buildDoc();
//生成html
$html = $obj->buildApiDocHtml();
$path = Tools::get_root_path().'/public/api_docs/';
if(!file_exists($path)){
mkdir($path,0777,true);
}
file_put_contents($path.'api_list.html',$html);
echo "<a href='/api_docs/api_list.html' target='_blank'>api_list.html</a>";
}
/**
* desc用户操作日志
* authorwh
*/
protected function operateLog($msg,$users_id=0,$username='',$name=''){
$tbname = 'fa_users_operate_log';
$sql = "
CREATE TABLE `{$tbname}` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`users_id` int(10) unsigned DEFAULT '0' COMMENT '用户ID',
`username` varchar(15) DEFAULT '' COMMENT '用户名',
`name` varchar(20) DEFAULT '' COMMENT '名称',
`url` varchar(60) DEFAULT '' COMMENT 'URL',
`msg` varchar(90) DEFAULT '' COMMENT '做了什么',
`ip` varchar(30) DEFAULT '' COMMENT '登录ip',
`input` text COMMENT '提交参数',
`create_time` timestamp NULL DEFAULT NULL COMMENT '登陆时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='用户操作日志';";
if(cache('cache_db_tables_now_project')){
$table_arr = cache('cache_db_tables_now_project');
}else{
$table_arr = Tools::get_tables();
}
//表是否存在
if(!in_array($tbname,$table_arr)){
cache('cache_db_tables_now_project',$table_arr);
Db::execute($sql);
}
$data = [
'users_id'=>$users_id,
'username'=>$username,
'name'=>$name,
'url'=>request()->url(),
'msg'=>$msg,
'ip'=>request()->ip(),
'input'=>json_encode(input(),JSON_UNESCAPED_UNICODE),
];
Db::table($tbname)->data($data)->insert();
}
}

View File

@@ -0,0 +1 @@
### this classes only to thinkphp5+

View File

@@ -0,0 +1,80 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/27} {10:35}
*/
namespace wanghua\general_utility_tools_php\framework\base;
use wanghua\general_utility_tools_php\framework\BaseController;
use think\Exception;
use think\Request;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 基础权限架构
* Class BaseAuthController
* @package library\framework\base
*/
class BaseAuthController extends BaseController
{
public $debug = false;
//权限错误重定向URL
protected $auth_err_redirect_url = '/index/err/fail';
public function __construct()
{
parent::__construct();
$err_redirect_url = config('service_framework_config.auth_err_redirect_url');
if($err_redirect_url){
$this->auth_err_redirect_url = $err_redirect_url;
}
}
/**
* desc默认鉴权
* authorwh
* @return bool
*/
function defaultAuth(){
$params = input();
if(empty($params['nonce'])){
Tools::log_to_write_txt(['服务被拒绝,鉴权参数缺失:nonce。params'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied']);
}
if(empty($params['timestamp'])){
Tools::log_to_write_txt(['服务被拒绝,鉴权参数缺失:timestamp。params'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied.']);
}
if(empty($params['sign'])){
Tools::log_to_write_txt(['服务被拒绝,鉴权参数缺失:sign。params'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied。']);
}
$sign = $params['sign'];
unset($params['sign']);
if(Tools::signature($params) != $sign){
Tools::log_to_write_txt(['签名失败,服务被拒绝.'=>input()]);
//跳转至错误中转控制器
return $this->response($this->auth_err_redirect_url,['title'=>'服务被拒绝. permission denied!']);
}
return true;
}
protected function response($url,$params){
if(Request::instance()->isAjax() || Request::instance()->isPost()){
return json(Tools::set_res(500,$params['title'],['url'=>$url]));
}
//跳转至错误中转控制器
return $this->redirect(url($url,$params));
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/25} {15:48}
*/
namespace wanghua\general_utility_tools_php\framework\base;
use wanghua\general_utility_tools_php\framework\BaseController;
use think\Request;
/**
* common controller have no permission
*
* 外部公用架构
*
* 无权限校验的公用架构
*
* Class PublicController
* @package app\index\controller
*/
class BasePublicController extends BaseController
{
}

View File

@@ -0,0 +1,194 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/6/23} {0:12}
*/
namespace wanghua\general_utility_tools_php\framework\base;
use app\common\model\TabConf;
use think\Db;
use think\Request;
use wanghua\general_utility_tools_php\framework\BaseController;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 微信专用权限架构
*
* Class BaseWechatAuthController
* @package wanghua\general_utility_tools_php\framework\base
*/
class BaseWechatAuthController extends BaseController
{
protected $wechat_user_table_name = 'fa_users';
public function __construct()
{
parent::__construct();
//首页提示语
$this->assign('index_msg', cache('index_msg_alert_cache_time'));
//线上环境加载微信授权
if (config('sys_env') == 'PROD') {
$wx_user_info = session('wx_user_info');
if (empty($wx_user_info['openid'])) {
//重定向之前保存当前url, 在获取授权信息之后,回跳到授权之前的网页地址
session('redirect_before_url_session', request()->url(true));
$ver = Tools::get_thinkphp_version();
if($ver == '5.0'){
$url = url('index/Wexinauth/usrAuth','',false,true);
}else{
$url = request()->domain().url('index/Wexinauth/usrAuth');
}
//没有则重定向去授权
return $this->redirect($url);
}
$this->saveWechatUser($wx_user_info);
}
}
/**
* desc周期更新当前用户信息
* authorwh
* @param $wx_user_info
*/
private function saveWechatUser($wx_user_info)
{
try {
$wechat_user = $this->getWxUserByOpenid($wx_user_info['openid']);
if (empty($wechat_user)) {
return $this->insertInfo($wx_user_info);
}
//扩展,按周期更新,而不是不更新
if (empty($wechat_user['update_time']) || time() - strtotime($wechat_user['update_time']) > 5 * 3600) {
return $this->updateUser($wx_user_info);
}
} catch (\Exception $e) {
Tools::log_to_write_txt([
'error' => '存储异常.' . $e->getMessage(),
'wx_user_info' => $wx_user_info,
'error_info' => $e->getTraceAsString()
]);
}
}
/**
* desc新增微信用户信息
* authorwh
* @param $wx_user_info \app\index\model\微信用户
*
* {
* "openid":"or9D2vs863Ky5Py2ovkAiu9XFLO4",
* "nickname":"起源果蔬副食大华",
* "sex":0,
* "language":"",
* "city":"",
* "province":"",
* "country":"",
* "headimgurl":"https://thirdwx.qlogo.cn/mmopen/vi_32/joiaA475nx3fJiaqx0ibdnWo4A7Q3uCgu2hsribI0ATLItORjuUgCSP8mCaBkqL61ibGojib4pQYX1djUhZpF5zoqpSg/132",
* "privilege":[]
* }
*/
private function insertInfo(array $wx_user_info)
{
if (isset($wx_user_info['privilege'])) {
$wx_user_info['privilege'] = json_encode($wx_user_info['privilege']);
}
$data = [
'nickname' => $wx_user_info['nickname'],
'country' => $wx_user_info['country'],
'province' => $wx_user_info['province'],
'city' => $wx_user_info['city'],
'headimage' => $wx_user_info['headimgurl'],
'language' => $wx_user_info['language'],
'openid' => $wx_user_info['openid'],
'unionid' => isset($wx_user_info['unionid']) ? $wx_user_info['unionid'] : '',
'privilege' => $wx_user_info['privilege'],
'sex' => $wx_user_info['sex'],
'arm_group' => '',
'score' => 0,//积分
'group_buy_earnings' => 0,//拼团收益
'water_drop_balance' => 0,//水滴
];
Db::table($this->wechat_user_table_name)
->data($data)
->insert();
}
/**
* desc更新当前用户信息
*
* authorwh
*/
private function updateUser($wx_user_info)
{
$data = [
'nickname' => $wx_user_info['nickname'],
'headimage' => $wx_user_info['headimgurl'],
'unionid' => isset($wx_user_info['unionid']) ? $wx_user_info['unionid'] : '',
];
Db::table($this->wechat_user_table_name)
->data($data)
->where('openid', index_user_openid())
->update();
}
/**
* desc根据unionid获取用户信息
* authorwh
* @param string $unionid
*/
private function getWxUserByUnionid(string $unionid)
{
return Db::table($this->wechat_user_table_name)
->field('id')
->where('unionid', $unionid)
->find();
}
/**
* desc获取微信用户信息
* authorwh
* @param string $openid
*/
private function getWxUserByOpenid(string $openid)
{
return Db::table($this->wechat_user_table_name)
//->field('id')
->where('openid', $openid)
->find();
}
/**
* desc根据openid获取昵称
* authorwh
* @param string $openid
*/
private function getNicknameByOpenid(string $openid)
{
return Db::table($this->wechat_user_table_name)
->where('openid', $openid)
->value('nickname');
}
}

View File

@@ -0,0 +1,213 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/3/22} {9:42}
*/
namespace wanghua\general_utility_tools_php\ftp;
/**
* 利用ftp操作服务器文件增删改查下载
* Class Ftp
* @package wanghua\general_utility_tools_php\ftp
*/
class Ftp
{
public $off; // 返回操作状态(成功/失败)
public $conn_id; // FTP连接
/**
* FTP连接
* $host -- FTP主机
* $port -- 端口
* $user -- 用户名
* $password -- 密码
*/
function __construct(string $host,string $port='21',string $user,string $password)
{
set_time_limit(10);//防止连接超时
$this->conn_id = ftp_connect($host, $port);
if(!$this->conn_id) throw new \Exception("FTP服务器连接失败");
$login_res = ftp_login($this->conn_id, $user, $password);
if(!$login_res) throw new \Exception("FTP服务器登陆失败");
ftp_pasv($this->conn_id, false); // true打开被动模拟默认关闭如果总是失败可试试true
}
/**
* desc打开被动模拟登录后使用
* authorwh
*/
function set_ftp_pasv(){
ftp_pasv($this->conn_id, true); // true打开被动模拟
}
/**
* 上传文件
* param $path 本地文件路径[包含文件名]
* param $newPath 目标目录[包含文件名][默认存放在ftp用户被授权的根路径]
* param bool $type 若目标目录不存在则新建
*/
function up_file($path, $newPath, $type = true)
{
if ($type) $this->dir_mkdirs($newPath);
$this->off = ftp_put($this->conn_id, $newPath, $path, FTP_BINARY);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件上传失败,请检查权限及路径是否正确!'];
return ['code'=>200, 'msg'=>'文件移动成功'];
}
/**
* 移动文件
* 特别注意:
* $path 如果是当前目录,则必须以 “/”开始
* 正确:/test2.zip
* 错误test2.zip
* param $path 原路径(含文件名)
* param $newPath 新路径(含文件名)
* param bool $type 若目标目录不存在则新建
*/
function move_file($path, $newPath, $ftpbasedir, $type = true)
{
if ($type) $this->ftp_mksubdirs($ftpbasedir,$newPath);
$this->off = ftp_rename($this->conn_id, $path, $newPath);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件移动失败,请检查权限及原路径是否正确!'];
return ['code'=>200, 'msg'=>'文件移动成功'];
}
/**
* 文件改名
* param $path 原路径
* param $newPath 新路径
*/
function rename_file($path, $newPath)
{
$this->off = ftp_rename($this->conn_id, $path, $newPath);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件改名失败,请检查权限及原路径是否正确!'];
return ['code'=>200, 'msg'=>'文件改名成功'];
}
/**
* 复制文件
* 说明由于FTP无复制命令,本方法变通操作为:下载后再上传到新的路径
* param $path 原路径
* param $newPath 新路径
* param bool $type 若目标目录不存在则新建
*/
function copy_file($path, $newPath, $type = true)
{
$downPath = "c:/tmp.dat";
$this->off = ftp_get($this->conn_id, $downPath, $path, FTP_BINARY);// 下载
if (!$this->off)
return ['code'=>500, 'msg'=>'文件复制失败,请检查权限及原路径是否正确!'];
$this->up_file($downPath, $newPath, $type);
return ['code'=>200, 'msg'=>'文件复制成功'];
}
/**
* 删除文件
* param $path 路径
*/
function del_file($path)
{
$this->off = ftp_delete($this->conn_id, $path);
if (!$this->off)
return ['code'=>500, 'msg'=>'文件删除失败,请检查权限及路径是否正确!'];
return ['code'=>200, 'msg'=>'文件删除成功'];
}
/**
* desc删除给定目录
* 注:
* 删除多个目录需重复调用
* authorwh
* param $dir
* return bool
*/
function del_dir($dir){
$children = ftp_nlist ($this->conn_id, $dir);
if(!$children) {
ftp_rmdir($this->conn_id, $dir);
return true;
}
throw new \Exception('请确保目录中无内容');
}
/**
* desc生成目录
* authorwh
* param $ftpcon
* param $ftpbasedir
* param $ftpath
*/
function ftp_mksubdirs($ftpbasedir,$ftpath){
ftp_chdir($this->conn_id, $ftpbasedir); // /var/www/uploads
$parts = explode('/',$ftpath); // 2013/06/11/username
if(strpos($parts[count($parts)-1], '.')){
unset($parts[count($parts)-1]);
}
foreach($parts as $part){
if(!ftp_chdir($this->conn_id, $part)){
ftp_mkdir($this->conn_id, $part);
ftp_chdir($this->conn_id, $part);
//ftp_chmod($this->conn_id, 0777, $part);
}
}
}
/**
* desc使用ftp连接方式下载文件
* authorwh
* param string $local_file 文件本地的路径(如果文件已经存在,则会被覆盖)。
* param string $remote_file 文件的远程路径。[默认查找ftp用户被授权的根路径]
* return array
*/
function download(string $local_file, string $remote_file){
// 进行ftp下载[默认保存在本地根目录]
if (!ftp_get($this->conn_id, $local_file, $remote_file, FTP_BINARY)) {
return ['code'=>500, 'msg'=>'ftp download fail'];
}
return ['code'=>200, 'msg'=>'ftp download success'];
}
/**
* desc判断ftp文件大小, 如果>-1, 说明文件存在;否则不存在.
* authorwh
* param $remote_file
* return int
*/
function ftp_size($remote_file){
return ftp_size($this->conn_id, $remote_file);
}
/**
* desc返回当前目录
* authorwh
* return false|string
*/
function ftp_pwd(){
return ftp_pwd($this->conn_id);
}
/**
* 关闭FTP连接
*/
function close()
{
ftp_close($this->conn_id);
}
}

View File

@@ -0,0 +1,21 @@
# PHP ftp类库
#### 实现用ftp方式上传、复制、移动、重命名、删除、下载文件
##### 案例代码:
```
$ftp = new Ftp();//初始化
//$ftp->up_file('D:\whua\test.zip','test.zip'); // 上传文件
//$ftp->rename_file('test.zip','test-rename.zip'); // 移动文件
//$ftp->move_file('/test2.zip','/testdir2/test2.zip', '/www/wwwroot/testftp/'); // 移动文件2
//$ftp->copy_file('test.zip','test-copy.zip'); // 复制文件
//$ftp->del_file('/www/aaa.txt'); // 删除具体文件
$ftp->del_dir('/www'); // 删除目录(错误示范),必须精确到具体文件夹删除
$ftp->del_dir('/ftp/test/copy-test/a-dir'); // 删除目录(正确示范),必须精确到具体文件夹删除,批量删除请多次调用
//下载文件
//$ftp->download('D:\whua\projects\zc_game_admin3.0\data\local.zip', 'test.zip');
//$ftp->close(); // 关闭FTP连接
//dump($ftp->ftp_pwd());die;
//dump($ftp->ftp_size('test2.zip'));die;
```

View File

@@ -0,0 +1,35 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/3/26} {16:06}
*/
namespace wanghua\general_utility_tools_php\gpt\chat;
class BaseChat
{
/**
* desc将字符串里含有json_encode后的一维数组提取出来
* @param $str
* @return string
*/
function dealstr($str){
$rep_str = str_replace("\n",'',$str);
$rep_str = str_replace("\r",'',$rep_str);
$rep_str = str_replace(" ",'',$rep_str);
$start_sit = strpos($rep_str,"[{");
$end_sit = strpos($rep_str,"}]");
$last_json_str = substr($rep_str,$start_sit,$end_sit-$start_sit);
$last_json_str.="}]";
return $last_json_str;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,175 @@
<?php namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\phpmailer\Exception;
class ChatGPT extends BaseChat
{
public $url = '';
public $apiKey = '';
public $model = '';
private $messages = [];
public function __construct()
{
}
/**
* desc定制个性
* authorwh
* @param array $customize
*/
function setCustomize($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
function setBefore($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
function setAfter($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
private function check()
{
if (empty($this->url) || empty($this->apiKey) || empty($this->model)) {
throw new Exception('PARAMS ERROR');
}
}
function chat($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
$answer .= $data;
echo $data;
ob_flush();
flush();
return strlen($data);
});
return $answer;
}
function returnAnswer($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
$answer .= $data;
return strlen($data);
});
return $answer;
}
private function curlPostChat($question = '', $config = [], $callback)
{
$url = $this->url;
$apiKey = $this->apiKey;
$model = $this->model;
$headers = ["Authorization: Bearer $apiKey", 'Accept: application/json', 'Content-Type: application/json',];
$post_msg_body = ["model" => $model, 'stream' => true,];
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$postData = json_encode($post_msg_body);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_exec($ch);
}
function getchatgptresponse($question = '', $config = [])
{
$url = $this->url;
$apiKey = $this->apiKey;
$model = $this->model;
$headers = ["Authorization: Bearer $apiKey", "Content-Type: application/json"];
$post_msg_body = ["model" => $model, 'stream' => false];
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$post_msg_body = json_encode($post_msg_body);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_msg_body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_error($ch)) {
return ['code' => curl_errno($ch), 'msg' => curl_error($ch)];
} else {
curl_close($ch);
return ['code' => 200, 'msg' => 'cURL ok', 'data' => $response];
}
}
private function parseData($data)
{
if (@json_decode($data)->choices[0]->message->content) {
return json_decode($data)->choices[0]->message->content;
}
$data = str_replace('data: {', '{', $data);
$data = rtrim($data, "\n\n");
if (strpos($data, 'data: [DONE]') !== false) {
return 'data: [DONE]';
} else {
if (false !== strpos($data, "\n\n")) {
$exp_arr = explode("\n\n", $data);
$str = '';
try {
for ($i = 0; $i < count($exp_arr) - 1; $i++) {
$jsondecode_arr = json_decode($exp_arr[$i], true);
$str .= $jsondecode_arr['choices'][0]['delta']['content'];
}
return $str;
} catch (\Exception $e) {
return $str;
}
}
$data = @json_decode($data, true);
if (!is_array($data)) {
return '';
}
if ($data['choices'][0]['finish_reason'] == 'stop') {
return 'data: [DONE]';
} elseif ($data['choices'][0]['finish_reason'] == 'length') {
return 'data: [CONTINUE]';
}
return $data['choices'][0]['delta']['content'] ?? '';
}
}
}

View File

@@ -0,0 +1,141 @@
<?php namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
class F37A49CDED4199801B05D5FFFAF78E010 extends BaseChat
{
public $url = '';
public $apiKey = '';
public $model = '';
public $chatId = '';
public $detail = false;
private $messages = [];
public function __construct()
{
}
function s8EB5A47634F626B142D2A93E9BAF009F($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
function s3ASCD163240094B8C736D3A1A5CSD($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
function s326723D163240094B8C736D3A1A5CAD3($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
private function c2111E682402A3955B616FA5795DEDE4F()
{
if (empty($this->url) || empty($this->apiKey)) {
throw new Exception('PARAMS ERROR');
}
}
function cAA8AF3EBE14831A7CD1B6D1383A03755($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->c8ECA4C82960B4611B076D51EF8ADF979($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
echo $data;
ob_flush();
flush();
return strlen($data);
});
}
private function c8ECA4C82960B4611B076D51EF8ADF979($question = '', $config = [], $callback)
{
$this->c2111E682402A3955B616FA5795DEDE4F();
$url = $this->url;;
$apiKey = $this->apiKey;
$model = $this->model;
$chatId = $this->chatId;
$detail = $this->detail;
$headers = ["Authorization: Bearer $apiKey", 'Accept: application/json', 'Content-Type: application/json',];
$post_msg_body = ['detail' => $detail, 'stream' => true,];
if ($detail) {
$post_msg_body['detail'] = $detail;
}
if ($chatId) {
$post_msg_body['chatId'] = $chatId;
}
if ($model) {
$post_msg_body['model'] = $model;
}
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$postData = json_encode($post_msg_body);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_exec($ch);
}
private function pCD736318A127E12426EED8700FF2BF28($data)
{
if (@json_decode($data)->choices[0]->message->content) {
return json_decode($data)->choices[0]->message->content;
}
$data = str_replace('data: {', '{', $data);
$data = rtrim($data, "\n\n");
if (strpos($data, 'data: [DONE]') !== false) {
return 'data: [DONE]';
} else {
if (false !== strpos($data, "\n\n")) {
$exp_arr = explode("\n\n", $data);
$str = '';
try {
for ($i = 0; $i < count($exp_arr) - 1; $i++) {
$jsondecode_arr = json_decode($exp_arr[$i], true);
$str .= $jsondecode_arr['choices'][0]['delta']['content'];
}
return $str;
} catch (\Exception $e) {
return $str;
}
}
$data = @json_decode($data, true);
if (!is_array($data)) {
return '';
}
if ($data['choices'][0]['finish_reason'] == 'stop') {
return 'data: [DONE]';
} elseif ($data['choices'][0]['finish_reason'] == 'length') {
return 'data: [CONTINUE]';
}
return $data['choices'][0]['delta']['content'] ?? '';
}
}
}

View File

@@ -0,0 +1,146 @@
<?php namespace wanghua\general_utility_tools_php\gpt\chat;
use wanghua\general_utility_tools_php\phpmailer\Exception;
use wanghua\general_utility_tools_php\tool\Tools;
class FastGPT extends BaseChat
{
public $url = '';
public $apiKey = '';
public $model = '';
public $chatId = '';
public $detail = false;
private $messages = [];
public function __construct()
{
}
/**
* desc定制个性
* authorwh
* @param array $customize
*/
function setCustomize($customize = [])
{
if ($customize) {
foreach ($customize as $item) {
$this->messages[] = $item;
}
}
}
function setBefore($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
function setAfter($describe = [])
{
if ($describe) {
foreach ($describe as $item) {
$this->messages[] = $item;
}
}
}
private function check()
{
if (empty($this->url) || empty($this->apiKey)) {
throw new Exception('PARAMS ERROR');
}
}
function chat($question = '', $config = [], &$answer_json_arr)
{
$answer = '';
$this->curlPostChat($question, $config, function ($ch, $data) use ($question, &$answer, &$answer_json_arr) {
$answer_json_arr[] = $data;
echo $data;
ob_flush();
flush();
return strlen($data);
});
}
private function curlPostChat($question = '', $config = [], $callback)
{
$this->check();
$url = $this->url;;
$apiKey = $this->apiKey;
$model = $this->model;
$chatId = $this->chatId;
$detail = $this->detail;
$headers = ["Authorization: Bearer $apiKey", 'Accept: application/json', 'Content-Type: application/json',];
$post_msg_body = ['detail' => $detail, 'stream' => true,];
if ($detail) {
$post_msg_body['detail'] = $detail;
}
if ($chatId) {
$post_msg_body['chatId'] = $chatId;
}
if ($model) {
$post_msg_body['model'] = $model;
}
if ($config) {
foreach ($config as $key => $val) {
$post_msg_body[$key] = $val;
}
}
if ($question) {
$this->messages[] = ["role" => "user", "content" => $question];
}
$post_msg_body['messages'] = $this->messages;
$postData = json_encode($post_msg_body);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
curl_exec($ch);
}
private function parseData($data)
{
if (@json_decode($data)->choices[0]->message->content) {
return json_decode($data)->choices[0]->message->content;
}
$data = str_replace('data: {', '{', $data);
$data = rtrim($data, "\n\n");
if (strpos($data, 'data: [DONE]') !== false) {
return 'data: [DONE]';
} else {
if (false !== strpos($data, "\n\n")) {
$exp_arr = explode("\n\n", $data);
$str = '';
try {
for ($i = 0; $i < count($exp_arr) - 1; $i++) {
$jsondecode_arr = json_decode($exp_arr[$i], true);
$str .= $jsondecode_arr['choices'][0]['delta']['content'];
}
return $str;
} catch (\Exception $e) {
return $str;
}
}
$data = @json_decode($data, true);
if (!is_array($data)) {
return '';
}
if ($data['choices'][0]['finish_reason'] == 'stop') {
return 'data: [DONE]';
} elseif ($data['choices'][0]['finish_reason'] == 'length') {
return 'data: [CONTINUE]';
}
return $data['choices'][0]['delta']['content'] ?? '';
}
}
}

View File

@@ -0,0 +1,224 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/4/19} {11:14}
*/
namespace wanghua\general_utility_tools_php\html;
/**
* PHP操作html元素【不包含修改】
* Class Html
* @package wanghua\general_utility_tools_php\html
*/
class Html
{
/**
* desc提取a标签
* 返回标签和标签包含的内容
* authorwh
* @param $html
* @return null
*/
function getAElement(string $html){
$a_array = null;
$reg="/<a .*?>.*?<\/a>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取div标签
* authorwh
* @param string $html
* @return null
*/
function getDiv(string $html){
$a_array = null;
$reg="/<div .*?>.*?<\/div>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取img标签
* authorwh
* @param string $html
* @return null
*/
function getImg(string $html){
$a_array = null;
$reg="/<img .*?>.*?<\/img>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取span标签
* authorwh
* @param string $html
* @return null
*/
function getSpan(string $html){
$a_array = null;
$reg="/<span .*?>.*?<\/span>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取ul标签
* authorwh
* @param string $html
* @return null
*/
function getUl(string $html){
$a_array = null;
$reg="/<ul .*?>.*?<\/ul>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取li标签
* authorwh
* @param string $html
* @return null
*/
function getLi(string $html){
$a_array = null;
$reg="/<li .*?>.*?<\/li>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取table标签
* authorwh
* @param string $html
* @return null
*/
function getTable(string $html){
$a_array = null;
$reg="/<table .*?>.*?<\/table>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取table tr标签
* authorwh
* @param string $html
* @return null
*/
function getTableTr(string $html){
$a_array = null;
$reg="/<tr .*?>.*?<\/tr>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取table td标签
* authorwh
* @param string $html
* @return null
*/
function getTableTd(string $html){
$a_array = null;
$reg="/<td .*?>.*?<\/td>/";
preg_match_all($reg,$html,$a_array);
return $a_array;
}
/**
* desc提取a标签属性
* authorwh
* @param $html
* @return array
*/
function getAAttribute($html){
$aarray = null;
$reg1="/<a .*?>.*?<\/a>/";
preg_match_all($reg1,$html,$aarray);
$hrefarray=null;//这个存放的是匹配出来的href的链接地址
$acontent=null;//存放匹配出来的a标签的内容
$reg2="/href=\"([^\"]+)/";
$a_arr = [];
for($i=0;$i<count($aarray[0]);$i++){
preg_match_all($reg2,$aarray[0][$i],$hrefarray);
//这里输出的就是遍历出来的所有a标签的链接
$a_arr['url'] = $hrefarray[1][0];
//拿出《a》标签的内容
$reg3="/>(.*)<\/a>/";
preg_match_all($reg3,$aarray[0][$i],$acontent);
//这里输出的就是a标签的文字了
$a_arr['text'] = $acontent[1][0];
}
return $a_arr;
}
/**
* desc提取img标签src(提取图片链接)
* authorwh
* @param $html
* @return array
*/
function getImgAttribute($html){
$aarray = null;
$reg1="/<img .*?>.*?<\/img>/";
preg_match_all($reg1,$html,$aarray);
$hrefarray=null;//这个存放的是匹配出来的href的链接地址
$acontent=null;//存放匹配出来的a标签的内容
$reg2="/src=\"([^\"]+)/";
$a_arr = [];
for($i=0;$i<count($aarray[0]);$i++){
preg_match_all($reg2,$aarray[0][$i],$hrefarray);
//这里输出的就是遍历出来的所有a标签的链接
$a_arr['url'] = $hrefarray[1][0];
//拿出《a》标签的内容
$reg3="/>(.*)<\/img>/";
preg_match_all($reg3,$aarray[0][$i],$acontent);
//这里输出的就是a标签的文字了
$a_arr['text'] = $acontent[1][0];
}
return $a_arr;
}
/**
* desc删除字符串
* authorwh
*/
function clearHtml($html, $str){
return str_replace($str,'',$html);
}
/**
* desc正则提取html元素的内容
*
* 用法eg
* $temp = '
<div class="num">1</div>
<div class="num">2</div>
<div class="num">3</div>
<div class="num">4</div>
<div class="num1">3</div>
<div class="num2">4</div>
<div class="num">5</div>';
$result = $this->get_tag_data($html,"div","class","num");
*
* authorwh
* @param $html
* @param $tag
* @param $class
* @param $value
* @return mixed
*/
function getTagData($html,$tag,$class,$value){
//$value 为空则获取class=$class的所有内容
$regex = $value ? "/<$tag.*?$class=\"$value\".*?>(.*?)<\/$tag>/is" : "/<$tag.*?$class=\".*?$value.*?\".*?>(.*?)<\/$tag>/is";
preg_match_all($regex,$html,$matches,PREG_PATTERN_ORDER);
//返回数组 ,指定标签内容
return $matches[1];
}
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/11/8} {15:05}
*/
namespace wanghua\general_utility_tools_php\html;
use wanghua\general_utility_tools_php\tool\Tools;
class Htmlcontent
{
/**
* [curlHtml 获取页面信息]
* 使用curl获取html页面内容
* @param [type] $url [网址]
* @return [type] [description]
*/
function curlHtml($url)
{
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "{$url}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding: gzip, deflate, br",
"Accept-Language: zh-CN,zh;q=0.9",
"Cache-Control: no-cache",
"Connection: keep-alive",
"Pragma: no-cache",
"Upgrade-Insecure-Requests: 1",
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
"cache-control: no-cache"
),
));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) return false;
else return $response;
}
/**
* [get_tag_data 使用正则获取html内容中指定的内容]
* @param [type] $html [爬取的页面内容]
* @param [type] $tag [要查找的标签]
* @param [type] $attr [要查找的属性名]
* @param [type] $value [属性名对应的值]
* @return [type] [description]
*/
function get_tag_data($html, $tag, $attr, $value)
{
// var_dump($tag, $attr, $value);
$regex = "/<$tag.*?$attr=\".*?$value.*?\".*?>(.*?)<\/$tag>/is";
// var_dump($regex);
preg_match_all($regex, $html, $matches, PREG_PATTERN_ORDER);
//var_dump($matches);die;
$data = isset($matches[1]) ? $matches[1] : '';
// var_dump($data);die;
return $data;
}
/**
* [get_html_data 使用xpath对获取到的html内容进行处理]
* @param [type] $html [爬取的页面内容]
* @param [type] $path [Xpath语句]
* @param integer $tag [类型 0内容 1标签内容 自定义标签]
* @param boolean $type [单个 还是多个(默认单个时输出单个)]
* @return [type] [description]
*/
function get_html_data($html, $path, $tag = 1, $type = true)
{
$dom = new \DOMDocument();
@$dom->loadHTML("<?xml encoding='UTF-8'>" . $html); // 从一个字符串加载HTML并设置UTF8编码
$dom->normalize(); // 使该HTML规范化
$xpath = new \DOMXPath($dom); //用DOMXpath加载DOM用于查询
$contents = $xpath->query($path); // 获取所有内容
$data = [];
foreach ($contents as $value) {
if ($tag == 1) {
$data[] = $value->nodeValue; // 获取不带标签内容
} elseif ($tag == 2) {
$data[] = $dom->saveHtml($value); // 获取带标签内容
} else {
$data[] = $value->attributes->getNamedItem($tag)->nodeValue; // 获取attr内容
}
}
if (count($data) == 1) {
$data = $data[0];
}
return $data;
}
//调用
public function get_content($url,$tag,$attr='',$value='')
{
//$url = input('url') ?? '';
//$tag = g('tag') ?? '';
//$attr = g('attr') ?? '';
//$value = g('value') ?? '';
if (empty($url) || empty($tag)) {
return Tools::set_res(4, '缺少关键参数!');
}
$html = $this->curlHtml($url);
//使用正则获取
$data = $this->get_tag_data($html, $tag, $attr, $value);
//Xpath方法暂未搞明白
// $data = $this->get_html_data($html, $path, $tag = 1, $type = true);
return Tools::set_res(1, 'ok!', $data);
}
}

View File

@@ -0,0 +1,575 @@
<?php
/*
* desc
*
* authorwh
* email
* createTime{2022/03/30} {09:59}
*/
namespace wanghua\general_utility_tools_php\http;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Utils;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* desccUrl网络库【推荐】
*
* authorwh
* Class Curl
* @package wanghua\general_utility_tools_php\http
*/
class Curl
{
/**
* desc[推荐] curl请求
*
* 如果是文件流则需要用getContents()方法获取
* eg: $res->getBody()->getContents();
*
* authorwh
* @param $url
* @param string $method
* @param false[] $config
* @param array $headers
* @return mixed
*/
static function request($url,$method='POST',$config=['verify' => false],$headers=[]){
$client = new Client($config);
//$headers = [
// //'User-Agent' => 'Apifox/1.0.0 (https://apifox.com)',
// //'Accept' => '*/*',
// //'Host' => 'vits_simple.excn.top',
// //'Connection' => 'keep-alive'
//];
$request = new Request($method, $url, $headers);
$res = $client->sendAsync($request)->wait();
//$res->getBody()->getContents();//如果是文件流则需要用getContents()方法获取
return $res->getBody();
}
/**
* descphp curl远程上传文件提交到远程服务器适用于上传多个文件(文件类型不限)
*
* 应用场景:
* 前端->后端A->后端B
* 说明前端发起文件上传后端A接收到文件后将文件使用guzzlehttp/guzzle上传到后端B后端B将文件处理
*
* 当前版本依赖:"guzzlehttp/guzzle": "^7.8"
*
* authorwh
* @param $url 远程地址
* @param $params 提交参数
* @param array $config 配置项
*/
function curlRemoteUploadFiles($url,$params, $config=['verify' => false]){
// 创建 Guzzle HTTP 客户端实例
$client = new Client($config);
// 定义要上传的文件列表
$files = [
//[
// 'name' => 'file1', // 服务器接收的文件字段名
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\dc1c441d81d48565ac6817b89d0f8bef.mp4', 'r'), // 文件路径
// 'filename' => 'dc1c441d81d48565ac6817b89d0f8bef.mp4', // 文件名,可自定义
//],
//[
// 'name' => 'files',
// 'contents' => Utils::tryFopen('D:\wanghua\projects\big_world_projects\universal_gravitation\public\uploads\20240506\f80179b23b60619fba8033bfd64f8817.mp4', 'r'),
// 'filename' => 'f80179b23b60619fba8033bfd64f8817.mp4',
//],
//['name'=>'prompt','contents'=>$prompt],
//['name'=>'tts_url','contents'=>$tts_url]
// 可以继续添加更多文件...
];
foreach ($params as $key=>$val){
$files[] = ['name'=>$key,'contents'=>$val];
}
$controller = \request()->controller();
$action = \request()->action();
$save_path = Tools::get_root_path() . "public/uploads/{$controller}/{$action}/";
$files_obj = \request()->file('files');
foreach ($files_obj as $k=>$file) {
if ($file->check()) {
$save_res = $file->move($save_path);
$fileinfo = $file->getInfo();
// 使用CURLFile包装文件路径以供上传
$filename = $save_path.date('Ymd').'/'.$save_res->getFilename();
$files[] = [
'name'=>"files",//服务器接收的文件字段名
'contents'=>Utils::tryFopen($filename, 'r'),
'filename'=>$fileinfo['name'],//可自定义
];
}
//else {
// return json(['code' => -1, 'msg' => '文件上传失败: ' . $file->getError()]);
//}
}
// 构建多部分表单数据
$multipartStream = new MultipartStream($files);
$boundary = $multipartStream->getBoundary();
$headers = [
'Content-Type' => "multipart/form-data; boundary={$boundary}",
];
// 准备请求体
$body = $multipartStream->getContents();
// 发起 POST 请求
try {
$response = $client->request('POST', $url, [
'headers' => $headers,
'body' => $body,
]);
// 处理响应
$responseBody = (string) $response->getBody();
//echo "Server response: " . $responseBody;
return Tools::set_ok('ok',$responseBody);
} catch (\GuzzleHttp\Exception\RequestException $e) {
// 错误处理
//echo "Error: " . $e->getMessage();
return Tools::set_fail("Error: " . $e->getMessage());
}
}
/**
* @deprecated 弃用
* GET
*
* authorwh
* @param string $url
* @param int $timeout
* @return bool|int|string
*/
static function curl_get(string $url, int $timeout = 10, $header=[])
{
$header = $header?:array(
'Accept: application/json',
);
$curl = curl_init();
//curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
//curl_setopt($curl, CURLOPT_SSLVERSION, 3);
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
// 超时设置,以秒为单位
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//print "Error: ".curl_errno($curl).'-' . curl_error($curl);
//返回错误码
return ['code' => curl_errno($curl), 'msg' => curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code' => 200, 'msg' => 'cURL ok', 'data' => $data];
}
}
/**
* @deprecated 弃用
* POST 表单
*
* authorwh
* @param string $url 是请求的链接
* @param array $postdata 传输的数据,数组格式
* @return bool|int|string
*/
static function curl_post(string $url, array $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code'=>200, 'msg'=>'cURL ok', 'data'=>$data];
}
}
/**
* @deprecated 弃用
* POST 查询参数
*
* authorwh
* @param string $url 是请求的链接
* @param array $postdata 传输的数据http_build_query查询参数格式
* @return bool|int|string
*/
static function curl_post_build_query(string $url, array $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($postdata));
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code'=>200, 'msg'=>'ok', 'data'=>$data];
}
}
/**
* @deprecated 弃用
* POST json
*
* authorwh
* @param string $url 是请求的链接
* @param array $postdata 传输的数据json格式
* @return 返回数组格式
*/
static function curl_post_json(string $url, array $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postdata,JSON_UNESCAPED_UNICODE));
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code'=>200, 'msg'=>'cURL ok', 'data'=>$data];
}
}
/**
* @deprecated 弃用
* POST json ,成功直接返回请求结果,错误返回数组
*
* 注:此方法不是表单提交
*
* authorwh
* @param string $url 是请求的链接
* @param array $postdata 传输的数据最终会转换为json格式请求
* @return 直接返回请求数据适合请求第三方返回BUFFER数据流的接口
*/
static function curl_post_json_return_buffer(string $url, array $postdata, $header=[]) {
$timeout = 4;
$connect_timeout = 1;
$set_time_limit = 5;
if($timeout + $connect_timeout < $set_time_limit) throw new \Exception('脚本超时值必须大于等于连接超时与请求处理超时之和');
set_time_limit($set_time_limit);
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
//发起连接前等待的时间如果设置为0则无限等待。
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE );
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postdata,JSON_UNESCAPED_UNICODE));
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code'=>curl_errno($curl), 'msg'=>curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return $data;
}
}
/**
* @deprecated 弃用
* POST [流]
*
* [请求Java接口]
*
* 思考1、Java端如果用文件流的方式去获取数据调用此方法
*
* @param $url
* @param $postdata
* @param int $timeout
* @return array
* @link
* @example
* @see
*/
static function java_curl_post_file_request($url, $postdata, $timeout = 10, $header=[])
{
$header = $header?:array(
'Accept: application/json',
);
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
// 超时设置
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
//返回错误码
return ['code' => curl_errno($curl), 'msg' => curl_error($curl)];
} else {
//关闭句柄
curl_close($curl);
// 返回的内容
return ['code' => 200, 'msg' => 'ok', 'data' => $data];
}
}
/**
* @deprecated 弃用
* 统一请求 GET/POST请求
*
* 注此设置允许header重定向适合部分请求头中携带参数的接口如请求头携带token
*
* @param String $url 接口地址
*/
static function curl_request($url, $method = 'GET',$data=null,$header=array(),$call_back=null)
{
set_time_limit(30);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
if($method == 'POST'){
if($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if($call_back){
//使用此特性前除非你清楚理解回调函数,否则不推荐
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $call_back);
}
$result = curl_exec($ch);
if (curl_errno($ch)) {
return [
'status' => 'error',
'message' => 'curl 错误信息: ' . curl_error($ch)
];
}
curl_close($ch);
return $result;
}
/**
* @deprecated 弃用
* descphp curl模拟文件上传
* post
* authorwh
* @param $url 提交地址
* @param $params 表单参数
* @param $files 上传文件
* @param $header 请求头
* @return array
*/
static function curlFileUpload($url,$params,$files,$header)
{
// 初始化cURL会话
$ch = curl_init();
$header = $header ?: array(
'Accept: application/json',
'Content-Type: multipart/form-data'
);
// 合并文件和其他字段数据
$data = array_merge($params, $files);
// 设置cURL选项
curl_setopt($ch, CURLOPT_URL, $url);
// 设置请求头
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
// 执行cURL请求
$result = curl_exec($ch);
if (curl_errno($ch)) {
return ['code' => curl_errno($ch), 'msg' => curl_error($ch)];
}
//关闭句柄
curl_close($ch);
// 返回的内容
return ['code' => 200, 'msg' => 'cURL ok', 'data' => $result];
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/1/17} {13:07}
*/
namespace wanghua\general_utility_tools_php\image;
use wanghua\general_utility_tools_php\tool\Tools;
class Image
{
/**
* desc将二进制字符串转为PNG图片
*
* 应用场景当你的图片来源是buffer二进制时
*
* 实战场景微信小程序获取小程序码调用获取微信小程序码接口时微信接口返回的是图片Buffer二进制
* 这时候需要前端转换为图片,有时候与你和做的前端技术需要你转换为图片,再把图片路径传给他直接使用
*
* authorwh
* @param $binary 图片的Buffer二进制数据
* @param $img_save_path 系统保存路径传public网站根目录下面的路径
* @param $filename 要保存的文件名称,必须带后缀名
*/
function binaryToImage($binary,$img_save_path,$filename){
// 获取二进制数据(这里假设从文件或其他地方获得)
//$binaryData = file_get_contents($binary); // 替换成真正的路径
// 创建图像资源
$imageResource = imagecreatefromstring($binary);
$img_save_path = Tools::get_root_path().'public/'.$img_save_path;
if(!file_exists($img_save_path)){
mkdir($img_save_path,0777,true);
}
// 保存图像到指定位置
imagepng($imageResource, $img_save_path.$filename); // 替换成想要保存的路径及名称
// 释放内存并关闭图像资源
imagedestroy($imageResource);
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/10} {18:00}
*/
namespace wanghua\general_utility_tools_php\log;
/**
* @deprecated 预计2025年01月01日移除
*
* 使用本类之前将fa_zc_log.sql导入数据库配置可根据实际需求修改
* Class BaseLog
* @package wanghua\general_utility_tools_php\log
*/
class BaseLog
{
/**
* 日志存储类型:文件、数据库(mysql)
* 如果是文件类型则在runtime目录中tool_log目录按日期生成文件
* eg:/runtime/tool_log/2020-01-01.log
* @var string
*/
protected static $log_type = 'file';//file or db
/**
* desc入参
* authorwh
* @param string $title
* @param string $content
*/
static function in($content='', $title=''){
$content = $content?$content:input();
$title = is_string($title)?$title:json_encode($title, JSON_UNESCAPED_UNICODE);
$content = is_string($content)?$content:json_encode($content);
$title = date('Ymd H:i:s').' ___IN | '.$title;
self::ready($title, $content);
}
/**
* desc出参
* authorwh
* @param string $title
* @param string $content
*/
static function out($content='', $title=''){
$title = is_string($title)?$title:json_encode($title, JSON_UNESCAPED_UNICODE);
$content = is_string($content)?$content:json_encode($content);
$title = date('Ymd H:i:s').' __OUT | '.$title;
self::ready($title, $content);
}
/**
* desc错误信息
* authorwh
* @param string $title
* @param string $content
*/
static function err($content='', $title=''){
$content = $content?$content:input();
$title = is_string($title)?$title:json_encode($title, JSON_UNESCAPED_UNICODE);
$content = is_string($content)?$content:json_encode($content);
$title = date('Ymd H:i:s').' ERROR | '.$title;
self::ready($title, $content);
}
/**
* desc
* authorwh
* @param $title
* @param $content
*/
protected static function ready($title, $content){
self::$log_type != 'file'?:(new File())->write($title, $content);
self::$log_type != 'db'?:(new Mysql())->write($title, $content);
}
}

View File

@@ -0,0 +1,134 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/14} {11:51}
*/
namespace wanghua\general_utility_tools_php\log;
/**
* 日志驱动
*
* only for thinkphp!
*
* eg:
*
$dr = new Driver();
//$dr->setDriver('file');
$dr->write(['start'=>11]);
$dr->write(['end'=>222]);
$dr->write(['result'=>333]);
$dr->flush();//must
*
* Class Driver
* @package wanghua\general_utility_tools_php\log
*/
class Driver
{
private $driver;
private $lib_path = '';//驱动路径
protected $static = null;
public function __construct(string $driver='')
{
//driver
$sys_log_type = $driver?$driver:\think\Config::get('sys_log_type');
//default driver
$this->driver = isset($sys_log_type)&&$sys_log_type!=''?$sys_log_type:'file';
//init driver
$this->initDriver();
}
/**
* descchoose driver
*
* authorwh
* @param string $driver
*/
function setDriver(string $driver){
//重新设置驱动
$this->driver = $driver;
//init driver
$this->initDriver();
}
/**
* descget driver name
*
* authorwh
* @return string
*/
function getDriverName(){
return $this->driver;
}
/**
* desc
* authorwh
*/
private function initDriver(){
$this->driver = $this->driverPath().ucfirst($this->driver);
$this->static = new $this->driver;
}
/**
* desc设置日志路径
* authorwh
* @param string $logDir
*/
function setLogDir(string $logDir){
$this->static->setLogDir($logDir);
}
function getLogDir(){
return $this->static->getLogDir();
}
/**
* descset driver lib path
*
* authorwh
* @param $lib_path
*/
function setLibPath($lib_path){
$this->lib_path = $lib_path;
}
/**
* descinit
*
* authorwh
* @return string
*/
private function driverPath(){
if($this->lib_path) return $this->lib_path;
return 'wanghua\general_utility_tools_php\log\driver\\';
}
/**
* descgroup data
*
* authorwh
* @param $data
*/
function write($data){
$this->static->write($data);
}
/**
* descoutput data
*
* authorwh
* @return mixed
*/
function flush(){
return $this->static->flush();
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/11} {10:08}
*/
namespace wanghua\general_utility_tools_php\log;
/**
* @deprecated 预计2025年01月01日移除
* Class File
* @package wanghua\general_utility_tools_php\log
*/
class File
{
//目录
protected $log_dir = '';
public function __construct()
{
$vendor_dir = __DIR__;
$index = strpos($vendor_dir, 'vendor')?:0;
$this->log_dir = (substr($vendor_dir,0,$index)).'runtime/toollog/'.date('Ymd');
file_exists($this->log_dir)?:mkdir($this->log_dir, 0777, true);
$this->log_dir = $this->log_dir.'/'.date('Y-m-d-H').'.log';
}
/**
* desc
* authorwh
* @param $title
* @param string $content
*/
function write(string $title, string $content=''){
$string = "\r\n".$title.' '. $content;
file_put_contents($this->log_dir, $string, FILE_APPEND);
}
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/10} {18:00}
*/
namespace wanghua\general_utility_tools_php\log;
use think\Db;
/**
* @deprecated 预计2025年01月01日移除
* Class Log
* @package wanghua\general_utility_tools_php\log
*/
class Log extends BaseLog
{
}

View File

@@ -0,0 +1,158 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/12/07} {10:48}
*/
namespace wanghua\general_utility_tools_php\log;
use wanghua\general_utility_tools_php\phpmailer\Mail;
use wanghua\general_utility_tools_php\phpmailer\SMTP;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 预计2025年01月01日移除
*
* 文件日志专用
*
* 邮件配置必须
*
* Class Logger
* @package app\common\tools
*/
class Logger
{
protected static $debug = 0;//调试模式
//发件服务器 start
//qq邮件服务器配置参考https://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=331
protected static $send_server_nickname = '系统异常';//发送方邮件服务器昵称 必须
protected static $send_server_username = '';//发送方邮件服务器账号 必须 //'1003076966@qq.com';
protected static $send_server_pwd = '';//发送方邮件服务器密码 必须 //'pdosdyjuurowbcfc';
protected static $send_server_host = 'smtp.qq.com';//发送方邮件服务器 qq服务器smtp.qq.com 必须
//发件服务器 end
//收件邮箱 start
protected static $receiver_email = '';//收件人邮箱 必须 eg: 1003076966@qq.com
protected static $receiver_nickname = '收件人昵称';//收件人昵称 必须
//收件邮箱 end
protected static $filter = [];//过滤指定日志不发送至email
/**
* desc开启调试模式
* authorwh
*/
static function open_debug(){
self::$debug = SMTP::DEBUG_SERVER;
}
/**
* desc关闭调试模式
* authorwh
*/
static function close_debug(){
self::$debug = SMTP::DEBUG_OFF;
}
/**
* 【必须】
* desc设置邮件服务器配置
* authorwh
* @param $send_server_nickname
* @param $send_server_username
* @param $send_server_pwd
* @param $send_server_host
*/
static function set_email_config(string $send_server_nickname,string $send_server_username,string $send_server_pwd,string $send_server_host){
self::$send_server_nickname = $send_server_nickname;
self::$send_server_username = $send_server_username;
self::$send_server_pwd = $send_server_pwd;
self::$send_server_host = $send_server_host;
}
/**
* 【必须】
* desc设置收件人配置
* authorwh
* @param $receiver_email
* @param $receiver_nickname
*/
static function set_receiver_config(string $receiver_email,string $receiver_nickname){
self::$receiver_email = $receiver_email;
self::$receiver_nickname = $receiver_nickname;
}
/**
* desc过滤指定日志不发送至email
* authorwh
*/
static function set_filter(array $filter=[]){
self::$filter = $filter;
}
/**
* desc写入普通日志
* authorwh
* @param array $data
* @param string $log_file_name
* @return array
*/
static function write_text(array $data,string $log_file_name){
try {
$result = ['code'=>200,'msg'=>'ok'];
Tools::log_to_write_txt($data,$log_file_name);
return $result;//含 code、msg、data结构
}catch (\Exception $e){
//一般不会出错
return ['code'=>500,'msg'=>'写入普通日志,日志记录失败.','data'=>['error'=>$e->getMessage(),'error_info'=>$e->getTraceAsString()]];
}
}
/**
* desc记录普通日志同时记录邮件日志
*
* authorwh
* @param array $data 如果data里面的一维数组的key包含了被过滤的配置则当次不发送邮件
* @param string $log_file_name
* @return array
*/
static function write_text_email(array $data,string $log_file_name){
$keys = array_keys($data);
try {
Tools::log_to_write_txt($data,$log_file_name);
if(self::$filter){
$is_send = true;//发送
foreach ($keys as $key){
//过滤不发送邮件的key,可以是任意字符串
if(in_array($key, self::$filter)){
$is_send = false;
}
}
if($is_send){
return self::send($data);
}
}
return self::send($data);
}catch (\Exception $e){
//一般不会出错
return ['code'=>500,'msg'=>'普通日志邮件日志记录失败.','data'=>['error'=>$e->getMessage(),'error_info'=>$e->getTraceAsString()]];
}
}
/**
* desc
* authorwh
* @param array $data
* @param string $log_file_name
* @return array
*/
protected static function send(array $data = []){
$body = json_encode($data,JSON_UNESCAPED_UNICODE);
$body = str_replace('\\"','"',$body);
$body = str_replace('\\n"','',$body);
$mail = new Mail(self::$send_server_nickname,self::$send_server_host,self::$send_server_username,self::$send_server_pwd);
$mail->debug = self::$debug;
return $mail->send(self::$receiver_email,self::$receiver_nickname,'EMAIL TITLE',$body);
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/11} {10:23}
*/
namespace wanghua\general_utility_tools_php\log;
/**
* @deprecated 预计2025年01月01日移除
*
* 仅支持ThinkPHP框架
* Class Mysql
* @package wanghua\general_utility_tools_php\log
*/
class Mysql
{
//日志数据表前缀,如果没有前缀,可能和已有表名称冲突
protected static $prefix = 'fa_sys_';
//日志表名
protected static $table = 'log';
public function __construct()
{
}
/**
* desc
* authorwh
* @param $title
* @param string $content
*/
function write($title, $content=''){
Db::table(self::$prefix.self::$table)->insert([
'title'=>$title, 'content'=>$content,
]);
}
}

View File

@@ -0,0 +1,90 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/14} {16:04}
*/
namespace wanghua\general_utility_tools_php\log\driver;
/**
* desc日志驱动基类
*
* 用法参考general_utility_tools_php\src\log\Driver.php类
*
* authorwh
* Class Base
* @package wanghua\general_utility_tools_php\log\driver
*/
class Base
{
protected $reqid = '';
public function __construct()
{
$this->reqid = $this->initReqID();
}
/**
* desc获取日志目录
*
* 子类根据情况实现EmailMySQL这种不需要目录则不实现
*
* authorwh
* @return string
*/
function getLogDir(){
return '';
}
/**
* 获取当前系统时间(精确到毫秒)
* @return float
*/
protected function getMillisecond()
{
list($t1, $t2) = explode(' ', microtime());
return sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
}
/**
* 获取当前系统时间(精确到微秒)
* @return float
*/
protected function getMicrosecond()
{
list($t1, $t2) = explode(' ', microtime());
return sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000000);
}
/**
* desc初始化请求id
*
* authorwh
* @return float
*/
function initReqID(){
return $this->getMicrosecond();
}
/**
* desc获取请求id
*
* authorwh
* @return float
*/
function getReqID(){
return $this->reqid;
}
function write($content){
}
function flush(){
}
function getLogData(){
}
}

View File

@@ -0,0 +1,206 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/12/07} {10:48}
*/
namespace wanghua\general_utility_tools_php\log\driver;
use wanghua\general_utility_tools_php\phpmailer\Mail;
use wanghua\general_utility_tools_php\phpmailer\SMTP;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 邮件日志驱动
*
* 说明:邮件发送到指定邮箱,使用之前请配置邮件账号、密码、服务器等必要参数
*
* 用法参考general_utility_tools_php\src\log\Driver.php类
*
* 1. 邮件配置必须;
* 2. 支持普通文本和html文本;
*
*
* Class Logger
* @package app\common\tools
*/
class Email extends Base
{
//file:文本(写文本文件), mysql:数据库, esdb:es数据库(直接存es数据库)esapi:es接口(调用接口向es存储数据)
private $driver = 'file';
protected $debug = 0;//调试模式
//发件服务器 start
//qq邮件服务器配置参考https://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=331
protected $send_server_nickname = '系统异常';//发送方邮件服务器昵称 必须
protected $send_server_username = '';//发送方邮件服务器账号 必须 //'1003076966@qq.com';
protected $send_server_pwd = '';//发送方邮件服务器密码 必须 //'pdosdyjuurowbcfc';
protected $send_server_host = 'smtp.qq.com';//发送方邮件服务器 qq服务器smtp.qq.com 必须
//发件服务器 end
//收件邮箱 start
protected $receiver_email = '';//收件人邮箱 必须 eg: 1003076966@qq.com
protected $receiver_nickname = '收件人昵称';//收件人昵称 必须
//收件邮箱 end
protected $filter = [];//过滤指定日志不发送至email
public $port = 465;//国内qq服务器亚马逊 25、587 或 2587
public $protocol = 'ssl';//国内QQ是ssl亚马逊TLS
public $sender_email = '';//发送方邮箱
/**
* desc开启调试模式
* authorwh
*/
function open_debug(){
$this->debug = SMTP::DEBUG_SERVER;
}
/**
* desc关闭调试模式
* authorwh
*/
function close_debug(){
$this->debug = SMTP::DEBUG_OFF;
}
/**
* 【必须】
* desc设置邮件服务器配置
* authorwh
* @param $send_server_nickname
* @param $send_server_username
* @param $send_server_pwd
* @param $send_server_host
*/
function set_email_config(string $send_server_nickname,string $send_server_username,string $send_server_pwd,string $send_server_host){
$this->send_server_nickname = $send_server_nickname;
$this->send_server_username = $send_server_username;
$this->send_server_pwd = $send_server_pwd;
$this->send_server_host = $send_server_host;
}
/**
* 【必须】
* desc设置收件人配置
* authorwh
* @param $receiver_email
* @param $receiver_nickname
*/
function set_receiver_config($receiver_email,string $receiver_nickname){
$this->receiver_email = $receiver_email;
$this->receiver_nickname = $receiver_nickname;
}
/**
* desc过滤指定日志不发送至email
* authorwh
*/
function set_filter(array $filter=[]){
$this->filter = $filter;
}
/**
* desc记录普通日志同时记录邮件日志
*
* authorwh
* @param array $data 如果data里面的一维数组的key包含了被过滤的配置则当次不发送邮件
* @param string $log_file_name
* @return array
*/
function write_text_email(array $data,string $log_file_name){
$keys = array_keys($data);
try {
Tools::log_to_write_txt($data,$log_file_name);
if($this->filter){
$is_send = true;//发送
foreach ($keys as $key){
//过滤不发送邮件的key,可以是任意字符串
if(in_array($key, $this->filter)){
$is_send = false;
}
}
if($is_send){
return $this->send($data);
}
}
return $this->send($data);
}catch (\Exception $e){
//一般不会出错
return ['code'=>500,'msg'=>'普通日志邮件日志记录失败.','data'=>['error'=>$e->getMessage(),'error_info'=>$e->getTraceAsString()]];
}
}
/**
* desc记录普通日志同时记录邮件日志
*
* authorwh
* @param array $data 如果data里面的一维数组的key包含了被过滤的配置则当次不发送邮件
* @param string $log_file_name
* @return array
*/
function write_text(string $title,string $body){
return $this->sendText($title, $body);
}
/**
* desc记录普通日志同时记录邮件日志
*
* authorwh
* @param array $data 如果data里面的一维数组的key包含了被过滤的配置则当次不发送邮件
* @param string $log_file_name
* @return array
*/
function write_text_aws(string $title,string $body){
return $this->sendTextAWS($title, $body);
}
/**
* desc
* authorwh
* @return array|void
*/
function write($content){
return $this->sendText($content['title'], $content['body']);
}
/**
* desc数组格式
* authorwh
* @param array $data
* @param string $log_file_name
* @return array
*/
protected function send(array $data = []){
$body = json_encode($data,JSON_UNESCAPED_UNICODE);
$body = str_replace('\\"','"',$body);
$body = str_replace('\\n"','',$body);
$mail = new Mail($this->send_server_nickname,$this->send_server_host,$this->send_server_username,$this->send_server_pwd,$this->send_server_username,$this->port,$this->protocol);
$mail->debug = $this->debug;
return $mail->send($this->receiver_email,$this->receiver_nickname,'EMAIL TITLE',$body);
}
/**
* desc文本格式
* authorwh
* @param array $data
* @param string $log_file_name
* @return array
*/
protected function sendText(string $title='邮件标题',string $body='邮件正文'){
$mail = new Mail($this->send_server_nickname,$this->send_server_host,$this->send_server_username,$this->send_server_pwd,$this->send_server_username,$this->port,$this->protocol);
$mail->debug = $this->debug;
return $mail->send($this->receiver_email,$this->receiver_nickname,$title,$body);
}
/**
* desc文本格式
* authorwh
* @param array $data
* @param string $log_file_name
* @return array
*/
protected function sendTextAWS(string $title='邮件标题',string $body='邮件正文'){
$mail = new Mail($this->send_server_nickname,$this->send_server_host,$this->send_server_username,$this->send_server_pwd,$this->sender_email,$this->port,$this->protocol);
$mail->debug = $this->debug;
return $mail->sendAWS($this->receiver_email,$this->receiver_nickname,$title,$body);
}
}

View File

@@ -0,0 +1,157 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/11} {10:08}
*/
namespace wanghua\general_utility_tools_php\log\driver;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 文件日志驱动
*
* 说明:日志记录在文件中,默认目录 runtime/log你可以自定义目录参考setLogDir方法都将记录在默认目录中。
*
* 用法参考general_utility_tools_php\src\log\Driver.php类
*
* Class File
* @package wanghua\general_utility_tools_php\log\lib
*
* 普通使用方法:
* $LogObj = new Driver();
* $LogObj->write('333333');
* $LogObj->write('444444');
* $LogObj->write('55555');
* $LogObj->flush();
*
* tp框架使用方法
* 1、注册全局LogObj对象属性参考example/tags.php
* 2、使用全局LogObj对象功能
* eg:
* //写入数据到内存
* request()->LogObj->write('333333');
* request()->LogObj->write('测试');
* request()->LogObj->write('444444');
* //持久化存储
* request()->LogObj->flush();
*
*
* tp框架行为使用方法
* 1、在应用目录中添加行为定义文件LoggerBehavior.php参考example/LoggerBehavior.php
* 2、注册全局LogObj对象属性
* 3、注册行为(侦听标签位)
*
*/
class File extends Base
{
//目录
private $log_dir = 'runtime/log/sys_log';//默认日志目录
private $log_data = [];
public function __construct()
{
parent::__construct();
}
/**
* desc设置日志目录
*
* write之前调用
*
* authorwh
* @param $log_dir
*/
function setLogDir($log_dir){
$this->log_dir = 'runtime/log/'.$log_dir;
}
/**
* desc获取日志目录
*
* authorwh
* @return string
*/
function getLogDir(){
return $this->log_dir;
}
/**
* desc写日志
*
* 先压到内存中调用flush方法写入磁盘建议一次不要压太多
*
* authorwh
* @param $content
*/
function write($content){
$ip = request()->ip();
$write_time = $this->getMicrosecond();
$date = date('Y-m-d H:i:s');
$tmp = [];
$tmp['all'] = 0;
$tmp['date'] = $date;
$tmp['ip'] = $ip;
$tmp['url'] = request()->baseUrl();
$tmp['reqid'] = $this->reqid;
$tmp_data = [];
if($this->log_data){
$use_time = $write_time-$this->log_data['content'][count($this->log_data['content'])-1]['time'];
$tmp_data['total'] = (array_sum(array_column($this->log_data['content'], 'use')) + $use_time);
$tmp_data['use'] = ($write_time-$this->log_data['content'][count($this->log_data['content'])-1]['time']);
$tmp_data['time'] = $write_time;
$tmp_data['log_info'] = $content;
}else{
$tmp_data['total'] = 0;
$tmp_data['use'] = 0;
$tmp_data['time'] = $write_time;
$tmp_data['log_info'] = $content;
}
$tmp['input'] = $_POST;
$this->log_data['base'] = $tmp;
$this->log_data['content'][] = $tmp_data;
}
/**
* desc刷新日志数据到文件
*
* 仅调用一次
*
* authorwh
*/
function flush(){
//计算单次request消耗
$this->log_data['base']['all'] = array_sum(array_column($this->log_data['content'], 'use'));
//dir
$this->log_dir = Tools::get_root_path().$this->log_dir.'/'.date('Ymd');
is_dir($this->log_dir)?:mkdir($this->log_dir, 0777, true);
$log_file = $this->log_dir.'/log'.date('YmdH').'.txt';
$base = json_encode($this->log_data['base'], JSON_UNESCAPED_UNICODE);
$content = json_encode($this->log_data['content'], JSON_UNESCAPED_UNICODE);
$content_str = str_replace('{"total"',"\n{'total'",$content);
$content_str = str_replace('"log_info":',"'log_info':\n",$content_str);
$strdata = "\n".$base ."\n".$content_str;
file_put_contents($log_file, "\n".$strdata, FILE_APPEND);
}
/**
* desc获取当次请求日志数据
*
* 注需在write方法之后调用否则可能不准确
*
* authorwh
* @return array
*/
function getLogData(){
return $this->log_data;
}
}

View File

@@ -0,0 +1,158 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/11/11} {10:23}
*/
namespace wanghua\general_utility_tools_php\log\driver;
/**
* MySQL日志驱动
*
* 说明日志记录在mysql数据库把日志记录在数据库中不建议记录大量日志。
* 推荐记录诸如系统生命周期日志、临时调试日志等少量日志因为日志将占用大量空间存在数据库中可能降低MySQL效率
*
* 用法参考general_utility_tools_php\src\log\Driver.php类
*
* 仅支持ThinkPHP框架
*
* DDL:
Create Table If Not Exists `t_sys_period_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`url` varchar(150) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'URL(不含查询参数)',
`time` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '当前时间(微秒)',
`use` int(10) unsigned DEFAULT '0' COMMENT '消耗(微秒)',
`all` int(10) unsigned DEFAULT '0' COMMENT '总消耗(微秒)',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '当前时间',
`ip` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'IP',
`reqid` varchar(16) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求ID',
`user_agent` varchar(3000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '代理',
`input` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '输入',
`log_info` text COLLATE utf8mb4_general_ci COMMENT '日志内容',
PRIMARY KEY (`id`),
KEY `index_create_time` (`create_time`) USING BTREE,
KEY `index_time` (`time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统周期日志';
*
* Class Mysql
* @package wanghua\general_utility_tools_php\log
*/
class Mysql extends Base
{
//日志数据
private $log_data = [];
//日志数据表前缀:
//如果没有前缀,可能和已有表名称冲突
private $table = 't_sys_period_log';//默认表
public function __construct()
{
parent::__construct();
//初始化日志
$this->initLogTable();
}
/**
* desc初始化日志表
*
* authorwh
* @return mixed
*/
private function initLogTable(){
$ddl = "Create Table If Not Exists `t_sys_period_log` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`url` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'URL(不含查询参数)',
`time` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '当前时间(微秒)',
`use` int unsigned DEFAULT '0' COMMENT '消耗(微秒)',
`all` int unsigned DEFAULT '0' COMMENT '总消耗(微秒)',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '当前时间',
`ip` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'IP',
`reqid` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求ID',
`user_agent` varchar(3000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '代理',
`input` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '输入',
`log_info` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '日志内容',
PRIMARY KEY (`id`),
KEY `index_create_time` (`create_time`) USING BTREE,
KEY `index_time` (`time`) USING BTREE,
KEY `index_all` (`all`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统周期日志';";
if(!\think\Cache::get('mysql_init_log_table_cache')){
\think\Db::execute($ddl);
\think\Cache::set('mysql_init_log_table_cache',1);//1存在
}
}
/**
* desc设置日志表名
*
* authorwh
* @param $tab
*/
function setTable($tab){
$this->table = $tab;
}
/**
*
* desc获取日志表名
*
* authorwh
* @return string
*/
function getTable(){
return $this->table;
}
/**
* desc写日志
*
* 写进内存
*
* authorwh
* @param $content
*/
function write($content){
parent::write($content);
$log_len = count($this->log_data);
$time = $this->getMicrosecond();
$use = $log_len>0?$time-$this->log_data[$log_len-1]['time']:0;
//data
$this->log_data[] = [
'url'=>request()->domain().request()->baseUrl(),
'time'=>$this->getMicrosecond(),
'ip'=>request()->ip(),
'reqid'=>$this->getReqID(),
'use'=>$use,//单次消耗
'all'=>array_sum(array_column($this->log_data, 'use'))+$use,
'user_agent'=>json_encode(request()->header(),JSON_UNESCAPED_UNICODE),
'input'=>json_encode(input(), JSON_UNESCAPED_UNICODE),
'log_info'=>json_encode($content, JSON_UNESCAPED_UNICODE),
];
}
/**
* desc持久化
*
* authorwh
*/
function flush()
{
parent::flush();
return \think\Db::table($this->getTable())->insertAll($this->log_data);
}
/**
* desc获取日志数据
* authorwh
* @return array|void
*/
function getLogData()
{
parent::getLogData();
return $this->log_data;
}
}

View File

@@ -0,0 +1,168 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/12/07} {13:44}
*/
namespace app\common\tools;
use app\common\model\SundryConfigModel;
use wanghua\general_utility_tools_php\log\Logger as LoggerObj;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 预计2025年01月01日移除
*
* 通用日志工具类
*
* 请将此类放入项目目录app\common\tools中再调用
* 请将SundryConfigModel类放入项目目录app\common\model中再调用
*
* 使用案例:
*
$rspArray = ['asfdasd'=>['1111'=>['222'=>['测试'=>'测试内容']]]];
$res = Logger::log_email(['aaaaaaaa'=>['1111'=>['222']]],'test_log');
dump($res);
$res = Logger::log_email(['bbbbbbbbbbbbbbb',$rspArray],'test_log');
dump($res);
*
* Class Logger
* @package app\common\tools
*/
class Logger
{
/**
* desc开启调试模式
* authorwh
*/
static function open_debug(){
LoggerObj::open_debug();
}
/**
* desc关闭调试模式
* authorwh
*/
static function close_debug(){
LoggerObj::close_debug();
}
/**
* desc系统未来通用统一日志记录
*
* egLogger::log(['测试测试测试测试.',$rspArray],$this->wechat_pay_log);//普通日志写入
*
* authorwh
* @param array $data
* @param string $file_log_name
*/
static function log(array $data=[], string $file_log_name){
try {
//服务器配置 start
$send_server_nickname = SundryConfigModel::getConfigVal('send_server_username');
$send_server_username = SundryConfigModel::getConfigVal('send_server_username');
$send_server_pwd = SundryConfigModel::getConfigVal('send_server_pwd');
$send_server_host = SundryConfigModel::getConfigVal('send_server_host');
//服务器配置 end
//收件人信息 start
$receiver = SundryConfigModel::getConfigVal('admin_error_log_email');
$receiver_nickname = '日志管理员';//默认收件人昵称
//收件人信息 end
//写入日志
LoggerObj::set_email_config($send_server_nickname,$send_server_username,$send_server_pwd,$send_server_host);
LoggerObj::set_receiver_config($receiver,$receiver_nickname);
return LoggerObj::write_text($data,$file_log_name);
}catch (\Exception $e){
Tools::log_to_write_txt(['error'=>'系统未来通用统一日志记录,出错.'.$e->getMessage(),'日志入参:'=>$data,'error_info'=>$e->getTraceAsString()],$file_log_name);
return ['code'=>500,'msg'=>'日志写入异常.'.$e->getMessage()];
}
}
/**
* desc记录错误信息并发送邮件
*
* egLogger::log_email(['收银宝统一支付-支付失败.',$rspArray],$this->wechat_pay_log);
*
* 小贴士即将写入的数据将被添加一个error键名再调用底层日志写入
*
* 使用案例:
*
$rspArray = ['asfdasd'=>['1111'=>['222'=>['测试'=>'测试内容']]]];
$res = Logger::log_email(['aaaaaaaa'=>['1111'=>['222']]],'test_log');
dump($res);
$res = Logger::log_email(['bbbbbbbbbbbbbbb',$rspArray],'test_log');
dump($res);
*
*
* 【注】
* 必须的邮件配置包含:
*
send_server_nickname 邮件发送服务器昵称
send_server_username 邮件发送服务器账号
send_server_pwd 邮件发送服务器密码
send_server_host 邮件发送服务器
admin_error_log_email 接收异常日志的管理员邮箱
*
* sql复制可用
INSERT INTO `fa_zc_sundry_config` ( `name`, `key`, `val`, `msg`, `group`, `ext_one`, `ext_two`, `ext_three`, `create_time`, `update_time`) VALUES ( '邮件发送服务器昵称', 'send_server_nickname', '掌电竞技', '', 'email', '', '', '', '2021-12-07 11:37:11', NULL);
INSERT INTO `fa_zc_sundry_config` ( `name`, `key`, `val`, `msg`, `group`, `ext_one`, `ext_two`, `ext_three`, `create_time`, `update_time`) VALUES ( '邮件发送服务器账号', 'send_server_username', '1003076950@qq.com', '', 'email', '', '', '', '2021-12-07 11:37:11', NULL);
INSERT INTO `fa_zc_sundry_config` ( `name`, `key`, `val`, `msg`, `group`, `ext_one`, `ext_two`, `ext_three`, `create_time`, `update_time`) VALUES ( '邮件发送服务器密码', 'send_server_pwd', 'pdosdyjuurowbcfc', '', 'email', '', '', '', '2021-12-07 11:37:11', NULL);
INSERT INTO `fa_zc_sundry_config` ( `name`, `key`, `val`, `msg`, `group`, `ext_one`, `ext_two`, `ext_three`, `create_time`, `update_time`) VALUES ( '邮件发送服务器', 'send_server_host', 'smtp.qq.com', '', 'email', '', '', '', '2021-12-07 11:37:11', NULL);
INSERT INTO `fa_zc_sundry_config` ( `name`, `key`, `val`, `msg`, `group`, `ext_one`, `ext_two`, `ext_three`, `create_time`, `update_time`) VALUES ( '接收异常日志的管理员邮箱', 'admin_error_log_email', '1003076950@qq.com', '', 'email', '', '', '', '2021-12-07 13:48:18', '2021-12-07 13:48:27');
*
*
* authorwh
* @param array $data
* @param string $file_log_name
*/
static function log_email(array $data=[], string $file_log_name){
try {
//服务器配置 start
$send_server_nickname = SundryConfigModel::getConfigVal('send_server_username');
$send_server_username = SundryConfigModel::getConfigVal('send_server_username');
$send_server_pwd = SundryConfigModel::getConfigVal('send_server_pwd');
$send_server_host = SundryConfigModel::getConfigVal('send_server_host');
//服务器配置 end
if(empty($send_server_nickname)){
Tools::log_to_write_txt(['服务器配置错误.发件服务器昵称为空.'],$file_log_name);
return Tools::set_res(500,'服务器配置错误.发件服务器昵称为空');
}
if(empty($send_server_username)){
Tools::log_to_write_txt(['服务器配置错误.发件服务器邮箱为空.'],$file_log_name);
return Tools::set_res(500,'服务器配置错误.发件服务器邮箱为空');
}
if(empty($send_server_pwd)){
Tools::log_to_write_txt(['服务器配置错误.发件服务器密码为空.'],$file_log_name);
return Tools::set_res(500,'服务器配置错误.发件服务器密码为空.');
}
if(empty($send_server_host)){
Tools::log_to_write_txt(['服务器配置错误.发件服务器HOST为空.'],$file_log_name);
return Tools::set_res(500,'服务器配置错误.发件服务器HOST为空..');
}
//收件人信息 start
$receiver = SundryConfigModel::getConfigVal('admin_error_log_email');
$receiver_nickname = '日志管理员';//默认收件人昵称
//收件人信息 end
if(empty($send_server_host)){
Tools::log_to_write_txt(['服务器配置错误.发件服务器HOST为空.'],$file_log_name);
return Tools::set_res(500,'服务器配置错误.发件服务器HOST为空..');
}
LoggerObj::set_email_config($send_server_nickname,$send_server_username,$send_server_pwd,$send_server_host);
LoggerObj::set_receiver_config($receiver,$receiver_nickname);
//写入日志
return LoggerObj::write_text_email($data,$file_log_name);
}catch (\Exception $e){
Tools::log_to_write_txt(['error'=>'系统未来通用统一日志记录,出错.'.$e->getMessage(),'日志入参:'=>$data,'error_info'=>$e->getTraceAsString()],$file_log_name);
return ['code'=>500,'msg'=>'日志写入异常.'.$e->getMessage()];
}
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/14} {16:50}
*/
namespace wanghua\general_utility_tools_php\log\example;
use wanghua\general_utility_tools_php\log\Driver;
/**
* 行为定义
*
* 使用方法请阅读example/README.MD第“使用驱动记录日志”项。
*
*
* Class LoggerBehavior
* @package app\behavior
*/
class LoggerBehavior
{
//function run(){
//
//}
function appInit(){
if(!request()->LogObj) {
request()->LogObj = new Driver();
}
request()->LogObj->write(['app start']);
//业务日志对象
if(!request()->ServeLogObj) {
request()->ServeLogObj = new Driver();
request()->ServeLogObj->setDriver('file');
}
request()->ServeLogObj->write(['app serve start']);
}
function appEnd(){
//框架生命周期日志对象
request()->LogObj->write(['app end']);
request()->LogObj->flush();
//业务日志对象
request()->ServeLogObj->write(['app serve end']);
request()->ServeLogObj->flush();
}
}

View File

@@ -0,0 +1,70 @@
## 可直接使用的案例
### 使用驱动记录日志
如需记录系统生命周期日志、临时调试日志(不推荐记录巨量日志数据),参考如下方案(简单修改即可):
* 在tags.php文件中初始化日志对象并监听应用行为, 复制代码即可使用
````
//框架生命周期日志bind的变量名可以自定义不一定是LogObj
\think\Request::instance()->bind('LogObj',new \wanghua\general_utility_tools_php\log\Driver('mysql'));
//业务日志对象
\think\Request::instance()->bind('ServeLogObj',new \wanghua\general_utility_tools_php\log\Driver('file'));
````
* 完成上一步即可使用需要注意的是LogObj对象的日志记录在变量中(内存中)
一旦程序异常终止,则日志也将丢失。有一个好办法,那就是捕获系统异常并刷新已经
记录的日志到介质磁盘、数据库。在框架config.php配置文件中配置异常接管类
参考以下案例:
````
// 异常处理handle类 留空使用 \think\exception\Handle
// 如果想扩展异常类的功能,可以创建一个类来继承此类,然后把新建的类配置在这里
'exception_handle' => '\\wanghua\\general_utility_tools_php\\src\\exception\\SystemException',
````
* 在系统中,你可以随时记录日志,参考代码:
``````
request()->LogObj->write(['你的文本,这里是数据哦!']);//记录一条简单的日志
``````
当然,如果你觉得此代码不好记忆和使用的话,你可以自由封装此代码,方便在应用中调用。
### 灵活的日志记录方式(可以在系统任意地方使用)
eg1:
```
//一维数组
//记录一条简单的日志
Tools::log_to_write_txt(['测试日志标题'=>'测试日志内容'],'test_log_dir');
//记录错误日志
Tools::log_to_write_txt([
'error'=>'系统错误.'.$e->getMessage(),
'input'=>input(),
'error_info'=>$e->getTraceAsString()
],'test_log_dir');
```
eg2:
```
//多维数组
Tools::log_to_write_txt([
'数组类型的日志'=>'随便记录点什么',
[
'可以是多维数组'=>
[
'a'=>'test'
]
]
],'test_log_dir');//test_log_dir是你的日志文件路径
```

View File

@@ -0,0 +1,30 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/08/25} {17:37}
*/
namespace app\common\model;
use think\Db;
/**
* @deprecated 预计2025年01月01日移除
*
* Class SundryConfigModel
* @package app\common\model
*/
class SundryConfigModel
{
protected static $self_table = 'fa_zc_sundry_config';
static function getConfigVal(string $key){
return Db::table(self::$self_table)->where('key',$key)->value('val');
}
}

View File

@@ -0,0 +1,56 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 应用行为扩展定义文件
// 动态绑定Request对象属性
//框架生命周期日志用mysql驱动
\think\Request::instance()->bind('LogObj',new \wanghua\general_utility_tools_php\log\Driver('mysql'));
//业务日志对象(用文件驱动)
\think\Request::instance()->bind('ServeLogObj',new \wanghua\general_utility_tools_php\log\Driver('file'));
// 注册 app\index\behavior\CheckLang行为类到app_init标签位
\think\Hook::add('app_init', '\\wanghua\\general_utility_tools_php\\log\\example\\LoggerBehavior');
//注册 app\admin\behavior\CronRun行为类到app_init标签位
\think\Hook::add('app_end', '\\wanghua\\general_utility_tools_php\\log\\example\\LoggerBehavior');
return [
// 应用初始化
'app_init' => [],
// 应用开始
'app_begin' => [],
// 应用调度
'app_dispatch' => [
'app\\common\\behavior\\Common',
],
// 模块初始化
'module_init' => [
'app\\common\\behavior\\Common',
],
// 插件开始
'addon_begin' => [
'app\\common\\behavior\\Common',
],
// 操作开始执行
'action_begin' => [],
// 视图内容过滤
'view_filter' => [],
// 日志写入
'log_write' => [],
// 应用结束
'app_end' => [],
];

View File

@@ -0,0 +1,119 @@
<?php
/*
* description
* authorwh
* email
* createTime{2021/11/02} {20:17}
*/
namespace wanghua\general_utility_tools_php\md5rsa;
/**
* md5rsa对称加密
*
* Class Md5RSA
* @package app\index\pay
*/
class Md5RSA{
/**
* 利用约定数据和私钥生成数字签名
* @param string $data 待签数据
* @return String 返回签名
*
* 使用示例:
*
* $crypted = (new Md5RSA())->sign($signMsg='待加密字符串',$mo_bao_cust_privite_key='私钥');
if(empty($crypted)){
return Tools::set_res(500, '加密失败');
}
$eb64_cry = base64_encode($crypted);
*
* 私钥格式参考:
* -----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKaQA2YLzj0g6e1h
BB1KAu8wwabxX+7zfwBMdqhnC/B6hjJ37/tbH5vHlt2S0lvjphupLW/PdVF1ezIk
haGk1ya0qFiKfByXzdou3Ml4qzOemmozIAix5ooh4Cbq1Py6202SvbM7KYv89syk
HJNB4QWXLLWmgsHCuiXQXhRJz6jDAgMBAAECgYAIF5cSriAm+CJlVgFNKvtZg5Tk
93UhttLEwPJC3D7IQCuk6A7Qt2yhtOCvgyKVNEotrdp3RCz++CY0GXIkmE2bj7i0
fv5vT3kWvO9nImGhTBH6QlFDxc9+p3ukwsonnCshkSV9gmH5NB/yFoH1m8tck2Gm
BXDj+bBGUoKGWtQ7gQJBANR/jd5ZKf6unLsgpFUS/kNBgUa+EhVg2tfr9OMioWDv
MSqzG/sARQ2AbO00ytpkbAKxxKkObPYsn47MWsf5970CQQDIqRiGmCY5QDAaejW4
HbOcsSovoxTqu1scGc3Qd6GYvLHujKDoubZdXCVOYQUMEnCD5j7kdNxPbVzdzXll
9+p/AkEAu/34iXwCbgEWQWp4V5dNAD0kXGxs3SLpmNpztLn/YR1bNvZry5wKew5h
z1zEFX+AGsYgQJu1g/goVJGvwnj/VQJAOe6f9xPsTTEb8jkAU2S323BG1rQFsPNg
jY9hnWM8k2U/FbkiJ66eWPvmhWd7Vo3oUBxkYf7fMEtJuXu+JdNarwJAAwJK0YmO
LxP4U+gTrj7y/j/feArDqBukSngcDFnAKu1hsc68FJ/vT5iOC6S7YpRJkp8egj5o
pCcWaTO3GgC5Kg==
-----END PRIVATE KEY-----
*/
public function sign(string $data, string $private_key)
{
if (empty($data) || empty($private_key))
{
return false;
}
//$private_key = file_get_contents(dirname(__FILE__).'/rsa_private_key.pem');
//if (empty($private_key))
//{
// echo "Private Key error!";
// return False;
//}
$pkeyid = openssl_get_privatekey($private_key);
if (empty($pkeyid))
{
// "private key resource identifier False!";
return false;
}
$verify = openssl_sign($data, $signature, $pkeyid, OPENSSL_ALGO_MD5);
openssl_free_key($pkeyid);
return $signature;
}
/**
* 利用公钥和数字签名以及约定数据验证合法性
* @param string $data 待验证数据
* @param string $signature 数字签名
* @return -1:error验证错误 1:correct验证成功 0:incorrect验证失败
*/
public function isValid(string $data, string $signature, string $public_key)
{
if (empty($data) || empty($signature) || empty($public_key))
{
return false;
}
//$public_key = file_get_contents(dirname(__FILE__).'/rsa_public_key.pem');
//if (empty($public_key))
//{
// echo "Public Key error!";
// return False;
//}
$pkeyid = openssl_get_publickey($public_key);
if (empty($pkeyid))
{
// "public key resource identifier False!";
return false;
}
$ret = openssl_verify($data, $signature, $pkeyid, OPENSSL_ALGO_MD5);
//switch ($ret)
//{
// case -1:
// echo "error";
// break;
// default:
// echo $ret==1 ? "correct" : "incorrect";//0:incorrect
// break;
//}
return $ret==1;
}
}

View File

@@ -0,0 +1,6 @@
## 仅适用于tp5+, php7+
## 表管理
## 字段管理

View File

@@ -0,0 +1,314 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {10:57}
*/
namespace wanghua\general_utility_tools_php\mysql\lib;
use think\Db;
/**
* @deprecated 废弃,未来将会删除
*
* desc
*
* authorwh
* Class Field
* @package wanghua\general_utility_tools_php\mysql\lib
*/
class Field
{
/**
* desc创建字段存在则修改字段
* authorwh
* @param array $table
*/
function createfield(array $table){
$table_name = $table['tablename'];
//新增字段
$dec_num = '';
if(in_array(strtoupper($table['type']), ['TINYINT','SMALLINT','MEDIUMINT','INT','BIGINT','FLOAT','DOUBLE','DECIMAL'])){
$table['default'] = '0';//整型默认值 浮点类型自动转为0.00
if(in_array(strtoupper($table['type']), ['FLOAT','DOUBLE','DECIMAL'])){
$dec_num = ','.$table['decimals_size'];//浮点类型小数位位数
}
}elseif ($table['type'] == 'enum'){
//枚举值不转换
}else{
$table['default'] = '""';
}
if(in_array(strtolower($table['type']), ['timestamp','datetime','date','time','text','mediumtext','longtext'])){
$table['size'] = 0;//重写 默认0
$table['default'] = 'null';//重写
}
//是否可以负数
if(in_array(strtolower($table['type']), ['int','tinyint','bigint','smallint', 'double', 'float', 'decimal']) && !empty($post['is_unsigned'])){//不是
$is_unsigned = 'unsigned';
}else{//是 表单可选值
$is_unsigned = '';
}
if (in_array($table['fields_name'], ['create_time', 'update_time'])){
if($table['fields_name'] == 'create_time'){
$table['default'] = 'CURRENT_TIMESTAMP';
}
if($table['fields_name'] == 'update_time'){
$table['default'] = 'NULL ON UPDATE CURRENT_TIMESTAMP';
}
}
//验证字段是否存在
$f = DB::table($table_name)->getTableFields();
if(in_array($table['fields_name'], $f)){
//枚举类型
if ($table['type'] == 'enum'){
$enum_str = '';
$enum_default = '';
//组合枚举值
if($table['default']){
$enum_str.='ENUM('.$table['default'].')';
$enum_default = explode(',', $table['default'])[0];
}
$sql2 = "ALTER TABLE {$table_name} MODIFY {$table['fields_name']} {$enum_str} NOT NULL DEFAULT {$enum_default} COMMENT '{$table['title']}';";
}else{
$sql2 = "ALTER TABLE {$table_name} MODIFY COLUMN {$table['fields_name']} {$table['type']}(".$table['size'].$dec_num.") {$is_unsigned} DEFAULT {$table['default']} COMMENT '{$table['title']}';";
}
}else{
if(in_array($table['type'], ['text','longtext'])){
$sql2 = "ALTER TABLE {$table_name} ADD COLUMN {$table['fields_name']} {$table['type']} COMMENT '{$table['title']}' AFTER id;";
}elseif ($table['type'] == 'enum'){//枚举类型
$enum_str = '';
$enum_default = '';
//组合枚举值
if($table['default']){
$enum_str.='ENUM('.$table['default'].')';
$enum_default = explode(',', $table['default'])[0];
}
$sql2 = "ALTER TABLE {$table_name} ADD {$table['fields_name']} {$enum_str} NOT NULL DEFAULT {$enum_default} COMMENT '{$table['title']}' AFTER id;";
}else{
$sql2 = "ALTER TABLE {$table_name} ADD COLUMN {$table['fields_name']} {$table['type']}(".$table['size'].$dec_num.") {$is_unsigned} DEFAULT {$table['default']} COMMENT '{$table['title']}' AFTER id;";
}
}
DB::execute($sql2);
}
/**
* desc删除字段
* authorwh
* @param $tablename
* @param $fieldname
*/
function dropFieldName($tablename, $fieldname){
$sql = "ALTER TABLE {$tablename} DROP COLUMN {$fieldname};";
DB::execute($sql);
}
/**
* desc改注释
* authorwh
* @param $tablename
* @param $comment
* @return bool
*/
function updateComment($tablename,$field, $comment){
$sql = "ALTER TABLE {$tablename} MODIFY COLUMN {$field} INT COMMENT '{$comment}';";
Db::execute($sql);
}
/**
* desc获取数据表中所有字段的数据类型含其它属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldsDataType(string $dbname,string $tablename){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'";
return Db::query($sql);
}
/**
* desc获取数据表中某个字段的数据类型含这个字段的其它属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldDataType(string $dbname,string $tablename,string $fieldname){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0];
}
/**
* desc获取数据表中某个字段的数据类型
*
* 不含这个字段的其它属性
*
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldDataTypeVal(string $dbname,string $tablename,string $fieldname){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0]['F_DATATYPE'];
}
/**
* desc获取数据表中某个字段的属性
*
* 查询结果字段名说明
*
* F_FIELD字段名称
* F_DATATYPE数据类型
* F_DATALENGTH数据长度int类型默认为null,业务处理时默认为10即可
* F_PRECISION精度
* F_DECIMAL_DIGITS小数位数
* F_ALLOWNULL是否允许为null值(1是0否)
* F_FIELDNAME字段名称
* F_PRIMARYKEY是否主键(1是0否)
* F_DEFAULTS字段默认值
*
* authorwh
* @param string $dbname
* @param string $tablename
* @param string $fieldname
* @return mixed
*/
function getFieldAttrVal(string $dbname,string $tablename,string $fieldname,string $attr){
$sql = "SELECT
COLUMN_NAME F_FIELD,
data_type F_DATATYPE,
CHARACTER_MAXIMUM_LENGTH F_DATALENGTH,
NUMERIC_PRECISION F_PRECISION,
NUMERIC_SCALE F_DECIMAL_DIGITS,
IF
( IS_NULLABLE = 'YES', '1', '0' ) F_ALLOWNULL,
COLUMN_COMMENT F_FIELDNAME,
IF
( COLUMN_KEY = 'PRI', '1', '0' ) F_PRIMARYKEY,
column_default F_DEFAULTS,
CONCAT( upper( COLUMN_NAME ), '(', COLUMN_COMMENT, ')' ) AS 'F_DESCRIPTION'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = '$tablename'
AND TABLE_SCHEMA = '$dbname'
AND COLUMN_NAME='$fieldname'
";
$ar = Db::query($sql);
return $ar[0][$attr];
}
}

View File

@@ -0,0 +1,181 @@
<?php
/*
* description
* authorwh
* email
* createTime{2022/02/24} {10:48}
*/
namespace wanghua\general_utility_tools_php\mysql\lib;
use think\Db;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* @deprecated 废弃,未来将会删除
*
* desc
*
* authorwh
* Class Table
* @package wanghua\general_utility_tools_php\mysql\lib
*/
class Table
{
/**
* desc创建表初始化默认字段
* authorwh
* @param $data
* @return bool
*/
function createTable($data){
$tablename = $data['tablename'];
$title = $data['title'];
//创建表
$sql = "CREATE TABLE IF NOT EXISTS {$tablename}(
id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='{$title}' ;";
Db::execute($sql);
}
/**
* desc改注释
* authorwh
* @param $tablename
* @param $comment
* @return bool
*/
function updateComment($tablename, $comment){
$sql = "ALTER TABLE {$tablename} COMMENT '{$comment}';";
Db::execute($sql);
}
/**
* desc改表名
* authorwh
* @param $tablename
* @param $new_table_name
* @return bool
*/
function updateTableName($tablename, $new_table_name){
$sql = "ALTER TABLE {$tablename} RENAME TO {$new_table_name}";
Db::execute($sql);
}
/**
* desc查询当前数据库所有的表名
* authorwh
* @return mixed
*/
function getTables(){
$dbname = $this->getNowDbName();
return array_column(Db::query('SHOW TABLES;'), 'Tables_in_'.$dbname);
}
/**
* desc获取数据表字所有段名
* authorwh
* @param $tablename
* @return array
*/
function getTableFields($tablename){
$dbname = $this->getNowDbName();
$sql = "SELECT COLUMN_NAME column_name,COLUMN_COMMENT column_comment,DATA_TYPE data_type
FROM information_schema.columns WHERE TABLE_NAME='{$tablename}' AND table_schema='{$dbname}'";
return array_column(Db::query($sql), 'column_name');
}
/**
* desc查询当前数据库名
* authorwh
* @return mixed
*/
function getNowDbName(){
$sql = "SELECT DATABASE() AS dbname;";
return Db::query($sql)['dbname'];
}
/**
* desc获取表的属性
* Name:
表名称
Engine:
表的存储引擎
Version:
版本
Row_format:
行格式。对于MyISAM引擎这可能是DynamicFixed或Compressed。动态行的行长度可变例如Varchar或Blob类型字段。固定行是指行长度不变例如Char和Integer类型字段
Rows:
表中的行数。对于MyISAM和其他存储引擎这个值是精确的对于innoDB存储引擎这个值通常是估算的
Avg_row_length:
平均每行包括的字节数
Data_length:
整个表的数据量(以字节为单位)
Max_data_length:
表可以容纳的最大数据量,该值和存储引擎相关
Index_length:
索引占用磁盘的空间大小(以字节为单位)
Data_free:
对于MyISAM引擎表示已经分配但目前没有使用的空间。这部分空间包含之前被删除的行以及后续可以被insert利用到的空间
Auto_increment:
下一个Auto_increment的值
Create_time:
表的创建时间
Update_time:
表的最近更新时间
Check_time:
使用 check table 或myisamchk工具最后一次检查表的时间
Collation:
表的默认字符集和字符排序规则
Checksum:
如果启用,保存的是整个表的实时校验和
Create_options:
创建表时指定的其他选项
Comment:
包含了其他额外信息对于MyISAM引擎保存的是表在创建时带的注释。如果表使用的是innodb引擎 保存的是InnoDB表空间的剩余空间。如果是一个视图注释里面包含了VIEW字样。
*
* @param $tablename 表名(无表名则查询所有表)
*
* authorwh
* @return mixed
*/
function getTableInfo(string $tablename=''){
if($tablename){
$sql = "SHOW TABLE STATUS WHERE Name = 'fa_agent';";
return Db::query($sql);
}
$sql = 'show table status;';
return Db::query($sql);
}
/**
* desc检查某个表是否存在
*
* authorwh
* @param string $dbname
* @param string $tablename
* @return bool
*/
function isExistTable(string $dbname,string $tablename){
$sql = "SELECT TABLE_SCHEMA,TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA ='$dbname'
AND TABLE_NAME = '$tablename';";
return empty(Db::query($sql));
}
/**
* desc获取表注释
*
* authorwh
*/
function getTableComment($table){
$prefix = config('database.prefix');
$table = $prefix.$table;
$sql = 'show table status;';
$arr = Db::query($sql);
$tmp = Tools::key_val_arr($arr,'Name','Comment');
return empty($tmp[$table])?'':$tmp[$table];
}
}

View File

@@ -0,0 +1,140 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/5/16} {14:22}
*/
namespace wanghua\general_utility_tools_php\oss\alicloud;
use AlibabaCloud\SDK\Sts\V20150401\Models\AssumeRoleRequest;
use AlibabaCloud\SDK\Sts\V20150401\Sts;
use AlibabaCloud\Tea\Utils\Utils\RuntimeOptions;
use Darabonba\OpenApi\Models\Config;
use OSS\Core\OssException;
use OSS\Credentials\StaticCredentialsProvider;
use OSS\OssClient;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 阿里云OSS存储服务工具库
*
* 封装了快捷方法,支持快速使用
*
* 功能:
* 创建桶、查询桶bucket删除必须先删除桶下面的所有文件
* sts临时凭证获取用于前端Browser.js直接操作oss上传、下载等操作
* 上传文件、下载文件、查询文件列表
*
* 依赖:
* composer require aliyuncs/oss-sdk-php
*
* 如果要使用临时凭证您需要使用composer require alibabacloud/sts-20150401命令安装STS依赖使用composer require alibabacloud/sdk命令安装PHP SDK依赖。
*
* Class AliyunOSS
* @package wanghua\general_utility_tools_php\oss\alicloud
*/
class AliyunOSS
{
public $bucket = '';//必须且后续操作都存放在这个bucket中
protected $provider = null;
protected $aliyun_config = [];
protected $ossClient = [];
protected $file_save_path = '';//文件存储路径
public function __construct($aliyun_config)
{
$this->aliyun_config = $aliyun_config;
if(empty($aliyun_config['AccessKeyId'])||empty($aliyun_config['AccessKeySecret'])||empty($aliyun_config['bucket'])){
throw new \Exception('OSS 配置错误');
}
if(empty($this->bucket)){
$this->bucket = $aliyun_config['bucket'];
}
//默认存储路径
$this->setSavePath();
// 从环境变量中获取RAM用户的访问密钥AccessKey ID和AccessKey Secret
$accessKeyId = $aliyun_config['AccessKeyId'];//config('aliyun_config.AccessKeyId');
$accessKeySecret = $aliyun_config['AccessKeySecret'];//config('aliyun_config.AccessKeySecret');
// 使用代码嵌入的RAM用户的访问密钥配置访问凭证。
$this->provider = new StaticCredentialsProvider($accessKeyId, $accessKeySecret);
// 填写Bucket所在地域对应的Endpoint。以华东1杭州为例Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
$endpoint = $aliyun_config['endpoint'];
$config = array(
"provider" => $this->provider,
"signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
// 填写Endpoint对应的Region信息例如cn-hangzhou。
"region" => $this->aliyun_config['region_id'],
"endpoint" => $endpoint,
);
$this->ossClient = new OssClient($config);
}
/**
* desc生成临时访问凭证。
*
* doc:https://help.aliyun.com/zh/oss/developer-reference/authorize-access-2?spm=a2c4g.11186623.0.i6#section-8rj-9sk-q7r
* authorwh
*/
function getAccessToken(){
return Mmodel::catch(function (){
// 运行本代码示例之前请确保已使用步骤1创建的RAM用户的访问密钥设置环境变量YOUR_ACCESS_KEY_ID和YOUR_ACCESS_KEY_SECRET。
$config = new Config([
'accessKeyId' => $this->aliyun_config['AccessKeyId'],//getenv('YOUR_ACCESS_KEY_ID'),
'accessKeySecret' => $this->aliyun_config['AccessKeySecret'],//getenv('YOUR_ACCESS_KEY_SECRET'),
]);
//
$config->endpoint = $this->aliyun_config['sts_endpoint'];
$client = new Sts($config);
$assumeRoleRequest = new AssumeRoleRequest([
// roleArn填写步骤2获取的角色ARN例如acs:ram::175708322470****:role/ramtest。
"roleArn" => "acs:ram::1113242774600735:role/ramoss",
// roleSessionName用于自定义角色会话名称用来区分不同的令牌例如填写为sessiontest。
"roleSessionName" => "sessiontest",
// durationSeconds用于设置临时访问凭证有效时间单位为秒最小值为900最大值以当前角色设定的最大会话时间为准。本示例指定有效时间为3000秒。
"durationSeconds" => 3000,
// policy填写自定义权限策略用于进一步限制STS临时访问凭证的权限。如果不指定Policy则返回的STS临时访问凭证默认拥有指定角色的所有权限。
// 临时访问凭证最后获得的权限是步骤4设置的角色权限和该Policy设置权限的交集。
//"policy" => ""
]);
$runtime = new RuntimeOptions([
//'http' => [
// 'verify' => false, // 禁用SSL验证仅限于调试
//],
]);
$result = $client->assumeRoleWithOptions($assumeRoleRequest, $runtime);
//printf("AccessKeyId:" . $result->body->credentials->accessKeyId. PHP_EOL);
//printf("AccessKeySecret:".$result->body->credentials->accessKeySecret.PHP_EOL);
//printf("Expiration:".$result->body->credentials->expiration.PHP_EOL);
//printf("SecurityToken:".$result->body->credentials->securityToken.PHP_EOL);
return Tools::set_ok('ok',[
'accessKeyId'=>$result->body->credentials->accessKeyId,
'accessKeySecret'=>$result->body->credentials->accessKeySecret,
'expiration'=>$result->body->credentials->expiration,
'securityToken'=>$result->body->credentials->securityToken,
]);
});
}
/**
* desc设置文件保存目录
* authorwh
* @param string $unique_id 唯一id可以是用户id可以是其它用于区分的标识
* @return string
*/
function setSavePath($unique_id=''){
if($unique_id){
$unique_id = $unique_id.'/';
}
//项目名称
$project_name = Tools::get_project_name();
//控制器方法路径
$ctl = strtolower(request()->controller().'/'.request()->action());
$this->file_save_path = $project_name.'/'.$ctl.'/'.$unique_id.date('Ymd').'/';//日期
return $this->file_save_path;
}
}

View File

@@ -0,0 +1,101 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/5/16} {17:42}
*/
namespace wanghua\general_utility_tools_php\oss\alicloud;
use OSS\Core\OssException;
use OSS\OssClient;
use wanghua\general_utility_tools_php\Mmodel;
class Bucket extends AliyunOSS
{
/**
* desc创建Bucket
* authorwh
* @param string $bucket_name bucket名称 为空则使用bucket否则以bucket+$bucket_name组成
*/
function createBucket($bucket_name='',$opts=[],$oss_acl_type = OssClient::OSS_ACL_TYPE_PRIVATE){
if(empty($this->aliyun_config['bucket'])){
throw new \Exception('OSS bucket必须');
}
$bucket_name = !$bucket_name?$this->aliyun_config['bucket']:$this->aliyun_config['bucket'].'-'.$bucket_name;
return Mmodel::catch(function () use ($bucket_name,$opts,$oss_acl_type){
// 填写Bucket名称例如examplebucket。
//$bucket_name = "examplebucket";
// 设置Bucket的存储类型为标准类型。
$options = $opts?:array(
OssClient::OSS_STORAGE => OssClient::OSS_STORAGE_STANDARD
);
return $this->ossClient->createBucket($bucket_name, $oss_acl_type, $options);
});
}
/**
* desc列出所有bucket
* authorwh
* @return array
*/
function getAllBucket(){
return Mmodel::catch(function (){
// 列举当前账号所有地域下的存储空间。
$bucketListInfo = $this->ossClient->listBuckets();
$bucketList = $bucketListInfo->getBucketList();
$arr = [];
foreach($bucketList as $bucket) {
$arr['name'] = $bucket->getName();
$arr['location'] = $bucket->getLocation();
$arr['create_time'] = $bucket->getCreatedate();
}
return $arr;
});
}
/**
* desc查询指定前缀bucket
* authorwh
* @param $prefix 指定前缀
* @throws OssException
* @throws \OSS\Http\RequestCore_Exception
*/
function getBucketPrefix($prefix){
return Mmodel::catch(function () use ($prefix){
$options = array(OssClient::OSS_QUERY_STRING => array(OssClient::OSS_PREFIX => $prefix));
// 列举当前账号所有地域下指定前缀的存储空间。
$bucketListInfo = $this->ossClient->listBuckets($options);
$bucketList = $bucketListInfo->getBucketList();
$arr = [];
foreach($bucketList as $bucket) {
$arr['name'] = $bucket->getName();
$arr['location'] = $bucket->getLocation();
$arr['create_time'] = $bucket->getCreatedate();
}
return $arr;
});
}
/**
* desc删除bucket
* authorwh
* @param $bucket_name
* doc:https://help.aliyun.com/zh/oss/developer-reference/delete-buckets-3?spm=a2c4g.11186623.0.0.61556dd5SHNYgV
* @return array
*/
function deleteBucket($bucket_name){
return Mmodel::catch(function () use ($bucket_name){
// 填写Bucket名称例如examplebucket。
return $this->ossClient->deleteBucket($bucket_name);
});
}
}

View File

@@ -0,0 +1,318 @@
<?php
/*
* description
* authorwh
* email
* createTime{2024/5/16} {18:02}
*/
namespace wanghua\general_utility_tools_php\oss\alicloud;
use OSS\Core\OssException;
use OSS\Core\OssUtil;
use OSS\OssClient;
use wanghua\general_utility_tools_php\Mmodel;
use wanghua\general_utility_tools_php\tool\Tools;
/**
* 文件对象操作类
*
* 包含上传、下载(可以上传字符串文本和文件)
*
* Class Objects
* @package app\api\logic
*/
class Objects extends AliyunOSS
{
/**
* desc文本上传
* authorwh
* @param string $object 保存文件名包含完整路径eg:exampledir/exampleobject.txtObject完整路径中不能包含Bucket名称
* @param string $content 上传文本内容
* @param string $opts 上传时可以设置相关的headers例如设置访问权限为private、自定义元数据等。
*
* 访问权限:
* 文件的访问权限ACL有以下四种defaultprivatepublic-readpublic-read-write
* 权限doc: https://help.aliyun.com/zh/oss/developer-reference/manage-object-acls-6?spm=a2c4g.11186623.0.0.63286dd53fYcTB
* @return array
*/
function txtUpload($object,$content='',$opts=[]){
return Mmodel::catch(function () use ($object,$content,$opts){
$options = [
OssClient::OSS_HEADERS => $opts?:[
'x-oss-object-acl' => 'public-read',//$permissions='public-read-write'
'x-oss-meta-info' => $content
],
];
return $this->ossClient->putObject($this->bucket, $this->file_save_path.$object, $content, $options);
});
}
/**
* desc文件上传
*
* authorwh
* @param string $object 上传后的文件名,完整路径中不能包含Bucket名称 如:"exampledir/exampleobject.txt";
* @param string $filePath 本地文件的完整路径例如D:\\localpath\\examplefile.txt, $file->getInfo('tmp_name')可获取临时文件路径
* @param $opts 权限等其它header参数配置
* 访问权限:
* 文件的访问权限ACL有以下四种defaultprivatepublic-readpublic-read-write
* 权限doc: https://help.aliyun.com/zh/oss/developer-reference/manage-object-acls-6?spm=a2c4g.11186623.0.0.63286dd53fYcTB
* @return array
*/
function fileUpload($object, $filePath, $opts=[]){
return Mmodel::catch(function () use ($object, $filePath,$opts){
$options = [
OssClient::OSS_HEADERS => $opts?:[
'x-oss-object-acl' => 'public-read',//'public-read-write'
],
];
return $this->ossClient->uploadFile($this->bucket, $this->file_save_path.$object, $filePath,$options);
});
}
/**
* desc文件流上传
*
* authorwh
* @param $object
* @param $stream
* @param array $opts
* @return array
*/
function uploadFileStream($object, $stream, $opts=[]){
return Mmodel::catch(function () use ($object, $stream,$opts){
$options = [
OssClient::OSS_HEADERS => $opts?:[
'x-oss-object-acl' => 'public-read',//'public-read-write'
],
];
// 从文件流上传
$res = $this->ossClient->putObject($this->bucket, $this->file_save_path.$object, $stream, $options);
return $res;
});
}
/**
* desc分片上传文件
* authorwh
* @param $object 上传后的文件名,完整路径中不能包含Bucket名称 如:"exampledir/exampleobject.txt";
* @param $uploadFile 填写本地文件的完整路径 如:'D:\\localpath\\examplefile.txt'
*/
function multipartUpload($object,$uploadFile){
$object = $this->file_save_path.$object;
//填写不包含Bucket名称在内的Object完整路径例如exampledir/exampleobject.txt。
//$object = 'exampledir/exampleobject.txt';
// 填写本地文件的完整路径。
//$uploadFile = 'D:\\localpath\\examplefile.txt';
$uploadId = '';
$initOptions = array(
OssClient::OSS_HEADERS => array(
// 指定该Object被下载时的网页缓存行为。
// 'Cache-Control' => 'no-cache',
// 指定该Object被下载时的名称。
// 'Content-Disposition' => 'attachment;filename=oss_download.jpg',
// 指定该Object被下载时的内容编码格式。
// 'Content-Encoding' => 'utf-8',
// 指定过期时间,单位为毫秒。
'Expires' => 150,
// 指定初始化分片上传时是否覆盖同名Object。此处设置为true表示禁止覆盖同名Object。
'x-oss-forbid-overwrite' => 'false',
// 指定上传该Object的每个part时使用的服务器端加密方式。
// 'x-oss-server-side-encryption'=> 'KMS',
// 指定Object的加密算法。
// 'x-oss-server-side-data-encryption'=>'SM4',
// 指定KMS托管的用户主密钥。
//'x-oss-server-side-encryption-key-id' => '9468da86-3509-4f8d-a61e-6eab1eac****',
// 指定Object的存储类型。
'x-oss-storage-class' => 'Standard',
// 指定Object的对象标签可同时设置多个标签。
// 'x-oss-tagging' => 'TagA=A&TagB=B',
),
OssClient::OSS_ACL => OssClient::OSS_ACL_TYPE_PUBLIC_READ,
);
/**
* 步骤1初始化一个分片上传事件并获取uploadId。
*/
try{
//返回uploadId。uploadId是分片上传事件的唯一标识您可以根据uploadId发起相关的操作如取消分片上传、查询分片上传等。
$uploadId = $this->ossClient->initiateMultipartUpload($this->bucket, $object, $initOptions);
//print("initiateMultipartUpload OK" . "\n");
// 根据uploadId执行取消分片上传事件或者列举已上传分片的操作。
// 如果您需要根据您需要uploadId执行取消分片上传事件的操作您需要在调用InitiateMultipartUpload完成初始化分片之后获取uploadId。
// 如果您需要根据您需要uploadId执行列举已上传分片的操作您需要在调用InitiateMultipartUpload完成初始化分片之后且在调用CompleteMultipartUpload完成分片上传之前获取uploadId。
//print("UploadId: " . $uploadId . "\n");
} catch(OssException $e) {
Tools::error_txt_log($e);
return Tools::set_fail('初始化一个分片上传事件并获取uploadId ERROR.'.$e->getMessage());
}
//dump('$uploadId:'.$uploadId);
/*
* 步骤2上传分片。
*/
$partSize = 10 * 1024 * 1024;
$uploadFileSize = sprintf('%u',filesize($uploadFile));
$pieces = $this->ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
$responseUploadPart = array();
$uploadPosition = 0;
$isCheckMd5 = true;
foreach ($pieces as $i => $piece) {
$fromPos = $uploadPosition + (integer)$piece[$this->ossClient::OSS_SEEK_TO];
$toPos = (integer)$piece[$this->ossClient::OSS_LENGTH] + $fromPos - 1;
$upOptions = array(
// 上传文件。
$this->ossClient::OSS_FILE_UPLOAD => $uploadFile,
// 设置分片号。
$this->ossClient::OSS_PART_NUM => ($i + 1),
// 指定分片上传起始位置。
$this->ossClient::OSS_SEEK_TO => $fromPos,
// 指定文件长度。
$this->ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
// 是否开启MD5校验true为开启。
$this->ossClient::OSS_CHECK_MD5 => $isCheckMd5,
);
// 开启MD5校验。
if ($isCheckMd5) {
$contentMd5 = OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos);
$upOptions[$this->ossClient::OSS_CONTENT_MD5] = $contentMd5;
}
try {
// 上传分片。
$responseUploadPart[] = $this->ossClient->uploadPart($this->bucket, $object, $uploadId, $upOptions);
//printf("initiateMultipartUpload, uploadPart - part#{$i} OK\n");
} catch(OssException $e) {
Tools::error_txt_log($e);
//printf("initiateMultipartUpload, uploadPart - part#{$i} FAILED\n");
//printf($e->getMessage() . "\n");
return Tools::set_fail('上传分片ERROR.'.$e->getMessage());
}
}
// $uploadParts是由每个分片的ETag和分片号PartNumber组成的数组。
$uploadParts = array();
foreach ($responseUploadPart as $i => $eTag) {
$uploadParts[] = array(
'PartNumber' => ($i + 1),
'ETag' => $eTag,
);
}
//dump($uploadParts);
//die;
/**
* 步骤3完成上传。
*/
$comOptions['headers'] = array(
// 指定完成分片上传时是否覆盖同名Object。此处设置为true表示禁止覆盖同名Object。
'x-oss-forbid-overwrite' => 'false',
// 如果指定了x-oss-complete-all:yes则OSS会列举当前uploadId已上传的所有Part然后按照PartNumber的序号排序并执行CompleteMultipartUpload操作。
// 'x-oss-complete-all'=> 'yes'
);
try {
// 执行completeMultipartUpload操作时需要提供所有有效的$uploadParts。OSS收到提交的$uploadParts后会逐一验证每个分片的有效性。当所有的数据分片验证通过后OSS将把这些分片组合成一个完整的文件。
$res = $this->ossClient->completeMultipartUpload($this->bucket, $object, $uploadId, $uploadParts,$comOptions);
//dump($res);
//printf( "Complete Multipart Upload OK\n");
$this->ossClient->putObjectAcl($this->bucket, $object, OssClient::OSS_ACL_TYPE_PUBLIC_READ);
return Tools::xml_to_array(($res['body']));
} catch(OssException $e) {
Tools::error_txt_log($e);
//printf("Complete Multipart Upload FAILED\n");
//printf($e->getMessage() . "\n");
return Tools::set_fail('ERROR:Complete Multipart Upload FAILED.'.$e->getMessage());
}
}
/**
* desc查询文件列表
* authorwh
* @param array $options
* @return array
*/
function getLists($options=[]){
return Mmodel::catch(function () use ($options){
$options = $options?:[
'max-keys'=>200,//设置最大个数为200。
'delimiter' => '',//分隔符表示将Object名称按该分隔符进行截断并返回结果。
];
$listObjectInfo = $this->ossClient->listObjects($this->bucket,$options);
//文件列表
$objectList = $listObjectInfo->getObjectList();
//前缀列表
//$prefixList = $listObjectInfo->getPrefixList();
$arr = [];
foreach ($objectList as $objectInfo) {
$arr[] = $objectInfo->getKey();
}
return $arr;
});
}
/**
* desc文件下载到本地
*
* authorwh
* @param string $object 存储空间内的文件名 eg: testfolder/exampleobject.txt
*
* @param string $save_local_path 本地保存路径 eg: D:\\localpath\\examplefile.txt
* 如果指定的本地文件存在会覆盖,不存在则新建。如果未指定本地路径,则下载后的文件默认保存到示例程序所属项目对应本地路径中。
*
* @param array $options 可选参数
*/
function downloadLocal($object,$save_local_path='',$options=[]){
// 填写不包含Bucket名称在内的Object完整路径例如testfolder/exampleobject.txt。
//$object = "testfolder/exampleobject.txt";
// 下载Object到本地文件examplefile.txt并保存到指定的本地路径中D:\\localpath。如果指定的本地文件存在会覆盖不存在则新建。
// 如果未指定本地路径,则下载后的文件默认保存到示例程序所属项目对应本地路径中。
//$save_local_path = "D:\\localpath\\examplefile.txt";
$opts = [];
if($save_local_path){
$opts = array(
OssClient::OSS_FILE_DOWNLOAD => $save_local_path
);
}
if($options){
$opts = array_merge($opts, $options);
}
// 使用try catch捕获异常。如果捕获到异常则说明下载失败如果没有捕获到异常则说明下载成功。
try{
$res = $this->ossClient->getObject($this->bucket, $object, $opts);
if($res){
return $res;
}
//下载成功
//print(__FUNCTION__ . ": OK, please check localfile: 'examplefile.txt'" . "\n");
return Tools::set_ok('ok',['save_path'=>$save_local_path]);
} catch(OssException $e) {
Tools::error_txt_log($e);
//printf(__FUNCTION__ . ": FAILED\n");
//printf($e->getMessage() . "\n");
return Tools::set_fail('ERROR:'.$e->getMessage());
}
}
/**
* desc下载文件到内存
*
* authorwh
* @param $object
* @throws \OSS\Http\RequestCore_Exception
*/
function downloadMemory($object){
try{
$this->ossClient->getObject($this->bucket, $object);
//dump($content);
return Tools::set_ok('下载文件到内存ok');
} catch(OssException $e) {
Tools::error_txt_log($e);
return Tools::set_fail('ERROR:'.$e->getMessage());
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/6/17} {18:22}
*/
namespace wanghua\general_utility_tools_php\oss\huawei\obs;
/**
* obs配置
*
* Class Config
* @package wanghua\general_utility_tools_php\huawei\obs
*/
class Config
{
//'*** Provide your Access Key ***'
static $key = '6XA5YIMA3MTDCHTGNXBQ';
//*** Provide your Secret Key ***'
static $secret = 'y1hptl5f820aayVfHAqkEFYnZGZ8xC8UTIAzygJm';
//桶名称
static $bucket_name = 'white-hand-bucket';
//'https://your-endpoint'
static $endpoint = 'obs.cn-east-3.myhuaweicloud.com';
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/6/17} {18:22}
*/
namespace wanghua\general_utility_tools_php\huawei\obs;
use Obs\ObsClient;
/**
* 初始化OBS
*
* Composer依赖obs/esdk-obs-php
*
* Class InitObs
* @package wanghua\general_utility_tools_php\huawei\obs
*/
class InitObs
{
protected static $obsClient = null;
/**
* desc
* authorwh
* @return ObsClient|null
*/
static function getObsClient(int $socket_timeout=30, int $connect_timeout=10){
if(null === self::$obsClient){
self::$obsClient = ObsClient::factory ( [
'key' => Config::$key,
'secret' => Config::$secret,
'endpoint' => Config::$endpoint,
'socket_timeout' => $socket_timeout,
'connect_timeout' => $connect_timeout
] );
}
return self::$obsClient;
}
/**
* desc关闭obsClient
* authorwh
*/
static function close(){
// 关闭obsClient
return self::$obsClient->close();
}
}

View File

@@ -0,0 +1,4 @@
## 华为OBS-对象存储服务
###支持常规功能,例如文件上传下载。
####温馨提示:你可能会遇到权限拒绝问题,请确认在华为云控制台已给用户授权
资料参考地址https://support.huaweicloud.com/productdesc-obs/zh-cn_topic_0045829060.html

View File

@@ -0,0 +1,40 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/6/19} {18:09}
*/
namespace wanghua\general_utility_tools_php\huawei\obs\service;
use wanghua\general_utility_tools_php\huawei\obs\InitObs;
/**
* obs基类负责初始化客户端
* 注意:所有操作必须具有权限,否则会报权限错误
* Class BaseObs
* @package libraries\huawei\obs\service
*/
class BaseObs
{
//桶名
protected $bucketName = 'bs-obs1';
//文件夹
public $dir = '';
//protected $origin = '';
//客户端
protected $obsClient = null;
public function __construct($dir='bs')
{
$this->obsClient = InitObs::getObsClient();
if($dir) {
$this->dir = (false !== strpos($dir, '/'))?$dir:$dir.'/';
}
}
}

View File

@@ -0,0 +1,288 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/6/19} {17:25}
*/
namespace wanghua\general_utility_tools_php\huawei\obs\service;
use Obs\ObsClient;
/**
* 华为云obs桶管理
* 包括创建桶、删除、修改属性、修改权限等
* 资料参考地址https://support.huaweicloud.com/sdk-php-devg-obs/obs_28_0301.html
* Class BucketManage
* @package libraries\huawei\obs\service
*/
class BucketManage extends BaseObs
{
public function __construct()
{
parent::__construct();
}
/**
* desc列举桶
* 包含名称、创建时间、区域位置
* authorwh
* @return array
*/
function getBuckList(){
$resp = $this->obsClient->listBuckets([
'QueryLocation' => true
]);
$data = [];
$data['RequestId'] = $resp['RequestId'];
$data['OwnerID'] = $resp['Owner']['ID'];
$data['OwnerName'] = $resp ['Owner']['DisplayName'];
$tmp = [];
foreach ($resp['Buckets'] as $index => $bucket){
array_push($tmp, [
'Name'=> $bucket['Name'],
'CreationDate'=> $bucket['CreationDate'],
'Location'=> $bucket['Location'],
]);
}
$data['Buckets'] = $tmp;
return $data;
}
/**
* desc创建桶
* 桶的名字是全局唯一的,所以您需要确保不与已有的桶名称重复。
* 同一用户在同一区域多次创建同名桶不会报错,创建的桶属性以第一次请求为准。
* 本示例创建的桶的访问权限默认是私有读写,存储类型默认是标准类型,区域位置是默认区域华北-北京一cn-north-1
* 须知:
* 创建桶时,如果使用的终端节点归属于默认区域华北-北京一cn-north-1则可以不指定区域如果使用的终端节点归属于其他区域
* 则必须指定区域,且指定的区域必须与终端节点归属的区域一致。当前有效的区域名称可从这里查询。
* 您可以使用带参数创建方式,在创建桶时,指定桶的区域位置。
* 资料参考地址https://support.huaweicloud.com/sdk-php-devg-obs/obs_28_0301.html
* authorwh
* @return array
*/
function createBucket(){
// 创建桶
$resp = $this->obsClient->createBucket([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
];
}
/**
* desc带参数创建桶
* 说明:
* 使用ACL参数指定桶的访问权限使用StorageClass参数指定桶的存储类型使用LocationConstraint参数指定桶的区域位置。
* authorwh
* @return array
*/
function createBucketByParam(){
// 创建桶
$resp = $this->obsClient->createBucket([
'Bucket' => $this->bucketName,
// 设置桶访问权限为公共读写,默认是私有读写
'ACL' => ObsClient::AclPublicReadWrite,
// 设置桶的存储类型为归档存储类型
'StorageClass' => ObsClient::StorageClassCold,
// 设置桶区域位置
'LocationConstraint' => 'bucketlocation'
]);
return [
'RequestId'=>$resp['RequestId'],
];
}
/**
* desc删除桶
* authorwh
* @return array
*/
function deleteBucket(){
// 删除桶
$resp = $this->obsClient->deleteBucket([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
];
}
/**
* desc获取桶存量信息
* 桶存量信息包括桶已使用的空间大小以及桶包含的对象个数。
* 您可以通过ObsClient->getBucketStorageInfo获取桶的存量信息。
* authorwh
* @return array
*/
function getBucketStorageInfo(){
$resp = $this->obsClient->getBucketStorageInfo([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
'Size'=>$resp['Size'],
'ObjectNumber'=>$resp['ObjectNumber'],
];
}
/**
* desc获取桶配额
* authorwh
* @return array
*/
function getBucketQuota(){
$resp = $this->obsClient->getBucketQuota([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
'StorageQuota'=>$resp['StorageQuota'],
];
}
/**
* desc设置桶配额
* 使用StorageQuota参数指定桶的配额大小。
* 桶配额值必须为非负整数单位为字节支持的最大值为2^63 - 1。
* authorwh
* @param float|int $StorageQuota
* @return array
*/
function setBucketQuota($StorageQuota = 1024 * 1024 * 100){
$resp = $this->obsClient->setBucketQuota([
'Bucket' => $this->bucketName,
'StorageQuota' => $StorageQuota
]);
return [
'RequestId'=>$resp['RequestId'],
];
}
/**
* desc获取桶存储类型
* authorwh
* @return array
*/
function getBucketStoragePolicy(){
$resp = $this->obsClient->getBucketStoragePolicy([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
'StorageClass'=>$resp['StorageClass'],
];
}
/**
* desc设置桶存储类型
* OBS允许您对桶配置不同的存储类型桶中对象的存储类型默认将与桶的存储类型保持一致。
* 不同的存储类型可以满足客户业务对存储性能、成本的不同诉求。桶的存储类型分为三类
* 标准存储
标准存储拥有低访问时延和较高的吞吐量,适用于有大量热点对象(平均一个月多次)或小对象(<1MB且需要频繁访问数据的业务场景。
ObsClient::StorageClassStandard
低频访问存储
低频访问存储适用于不频繁访问平均一年少于12次但在需要时也要求能够快速访问数据的业务场景。
ObsClient::StorageClassWarm
归档存储
归档存储适用于很少访问(平均一年访问一次)数据的业务场景。
ObsClient::StorageClassCold
* 资料参考地址https://support.huaweicloud.com/sdk-php-devg-obs/obs_28_0311.html
* authorwh
* @param $StorageClass
* @return array
*/
function setBucketStoragePolicy($StorageClass){
$resp = $this->obsClient->setBucketStoragePolicy([
'Bucket' => $this->bucketName,
'StorageClass' => $StorageClass
]);
return [
'RequestId'=>$resp['RequestId'],
];
}
/**
* desc获取桶区域位置
* 说明:
* 创建桶时可以指定桶的区域位置,请参见创建桶。
* authorwh
* @return array
*/
function getBucketLocation(){
$resp = $this->obsClient->getBucketLocation([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
'Location'=>$resp['Location'],
];
}
/**
* desc获取桶策略
* authorwh
* @return array
*/
function getBucketPolicy(){
$resp = $this->obsClient->getBucketPolicy([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
'Policy'=>$resp['Policy'],
];
}
/**
* @deprecated 未完成
* desc设置桶策略[此方法待扩展]
* 除了桶访问权限外,桶的拥有者还可以通过桶策略,提供对桶和桶内对象的集中访问控制。
* 更多关于桶策略的内容请参考桶策略 https://support.huaweicloud.com/usermanual-obs/zh-cn_topic_0045829071.html
* 说明:
* 桶策略内容的具体格式JSON格式字符串请参考《对象存储服务API参考》。
* authorwh
* @return array
*/
function setBucketPolicy(){
$resp = $this->obsClient->setBucketPolicy([
'Bucket' => $this->bucketName,
'Policy' => 'your policy'
]);
return [
'RequestId'=>$resp['RequestId'],
];
}
/**
* desc删除桶策略
* authorwh
* @return array
*/
function deleteBucketPolicy(){
$resp = $this->obsClient->deleteBucketPolicy([
'Bucket' => $this->bucketName
]);
return [
'RequestId'=>$resp['RequestId'],
];
}
}

View File

@@ -0,0 +1,87 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/6/22} {14:09}
*/
namespace wanghua\general_utility_tools_php\huawei\obs\service;
/**
* 跨域资源共享
* 跨域资源共享CORS允许Web端的应用程序访问不属于本域的资源。OBS提供接口方便开发者控制跨域访问的权限。
* Class Cors
* @package libraries\huawei\obs\service
*/
class Cors extends BaseObs
{
public $AllowedMethod = [];// 指定允许的跨域请求方法(GET/PUT/DELETE/POST/HEAD)
public $domains = [];//// 指定允许跨域请求的来源 ['http://www.a.com', 'http://www.b.com']
/**
* desc设置跨域规则
* authorwh
* @return array
*/
function setBucketCors(){
$resp = $this->obsClient->setBucketCors ( [
'Bucket' => $this->bucketName,
'CorsRules' => [
[
'ID' => 'rule1',
// 指定允许的跨域请求方法(GET/PUT/DELETE/POST/HEAD)
'AllowedMethod' => $this->AllowedMethod,
// 指定允许跨域请求的来源
'AllowedOrigin' => $this->domains,
// 控制在OPTIONS预取指令中Access-Control-Request-Headers头中指定的header是否被允许使用
'AllowedHeader' => [ 'x-obs-header'],
// 指定允许用户从应用程序中访问的header
'ExposeHeader' => ['x-obs-expose-header'],
// 指定浏览器对特定资源的预取(OPTIONS)请求返回结果的缓存时间,单位为秒
'MaxAgeSeconds' => 60
]
]
] );
return [
'RequestId'=>$resp['RequestId'],
];
}
/**
* desc查看跨域规则
* authorwh
* @return array
*/
function getBucketCors(){
$resp = $this->obsClient->getBucketCors ( [
'Bucket' => $this->bucketName,
] );
$data = [];
foreach ( $resp ['CorsRules'] as $index => $rule ) {
$data[]['ID'] = $rule ['ID'];
$data[]['MaxAgeSeconds'] = $rule ['MaxAgeSeconds'];
$data[]['AllowedMethod'] = $rule ['AllowedMethod'];
$data[]['AllowedOrigin'] = $rule ['AllowedOrigin'];
$data[]['AllowedHeader'] = $rule ['AllowedHeader'];
$data[]['ExposeHeader'] = $rule ['ExposeHeader'];
}
return $data;
}
/**
* desc删除跨域规则
* authorwh
* @return array
*/
function deleteBucketCors(){
$resp = $this->obsClient->deleteBucketCors ( [
'Bucket' => $this->bucketName,
] );
return [
'RequestId'=>$resp['RequestId'],
];
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* description
* authorwh
* email
* createTime{2020/6/22} {15:26}
*/
namespace wanghua\general_utility_tools_php\huawei\obs\service;
class Download extends BaseObs
{
/**
* desc文本下载
* 文本下载方式下返回结果中的Body是包含文本内容的GuzzleHttp\Psr7\StreamInterface对象。
* authorwh
* @param string $objectname
* @return array
*/
function text(string $objectname){
$resp = $this->obsClient -> getObject([
'Bucket' => $this->bucketName,
'Key' => $objectname
]);
$str = '';
// 获取对象内容
while(!$resp['Body'] -> eof()){
$str .= $resp['Body'] -> read(65536);
}
return [
'RequestId'=>$resp ['RequestId'],
'text'=>$str
];
}
/**
* desc流式下载向浏览器输出流直接下载文件
*
* 使用SaveAsStream参数指定使用流式下载。
* 返回结果中的Body是一个可读的GuzzleHttp\Psr7\StreamInterface对象可将对象的内容读取到本地文件或者内存中。
* authorwh
* @param string $objectname
* @param string $filename
*/
function stream(string $objectname, string $filename){
$resp = $this->obsClient -> getObject([
'Bucket' => $this->bucketName,
'Key' => $objectname,
'SaveAsStream' => true
]);
header("Content-Disposition:attachment; filename={$filename}");
while(!$resp['Body'] -> eof()){
echo $resp['Body'] -> read(65536);//开始下载
}
}
/**
* desc文件下载将文件从obs下载到当前应用所在服务器
* 说明:
* 使用SaveAsFile参数指定文件下载的路径。
* authorwh
* @param string $objectname
* @param $localfile "将文件从obs下载到当前应用所在服务器"
* @return array
*/
function file(string $objectname,string $localfile){
$resp = $this->obsClient -> getObject([
'Bucket' => $this->bucketName,
'Key' => $objectname,
'SaveAsFile' => $localfile,
]);
return [
'RequestId'=>$resp ['RequestId'],
];
}
}

Some files were not shown because too many files have changed in this diff Show More