Files
shop-platform/src/app/common.php
2025-12-05 15:05:54 +08:00

2238 lines
59 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// 应用公共文件
// 除了 E_NOTICE报告其他所有错误
//error_reporting(E_ALL ^ E_NOTICE);
//error_reporting(E_ERROR | E_WARNING | E_PARSE);
error_reporting(E_NOTICE);
use extend\QRcode as QRcode;
use think\facade\Session;
use think\facade\Event;
use think\facade\App;
use app\model\system\Addon;
use extend\Barcode;
use app\common\library\CallerInfo;
/*****************************************************基础函数*********************************************************/
/**
* 生成编号
* @param string $prefix
* @param string $tag 业务标识 例如member_id ...
* @return string
* @throws Exception
*/
function create_no($prefix = '', $tag = '')
{
$data_center_id = 1;
$machine_id = 2;
$snowflake = new \extend\util\Snowflake($data_center_id, $machine_id);
$id = $snowflake->generateId();
return $prefix . date('Ymd') . $tag . $id;
}
/**
* 把返回的数据集转换成Tree
*
* @param array $list
* 要转换的数据集
* @param string $pid
* parent标记字段
* @param string $level
* level标记字段
* @return array
*/
function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root = 0)
{
// 创建Tree
$tree = [];
if (!is_array($list)) :
return false;
endif;
// 创建基于主键的数组引用
$refer = [];
foreach ($list as $key => $data) {
$refer[ $data[ $pk ] ] = &$list[ $key ];
$refer[ $data[ $pk ] ][ $child ] = [];
$refer[ $data[ $pk ] ][ 'child_num' ] = 0;
}
foreach ($refer as $key => $data) {
// 判断是否存在parent
$parentId = $data[ $pid ];
if ($root == $parentId) {
$tree[ $key ] = &$refer[ $key ];
} else if (isset($refer[ $parentId ])) {
is_object($refer[ $parentId ]) && $refer[ $parentId ] = $refer[ $parentId ]->toArray();
$parent = &$refer[ $parentId ];
$parent[ $child ][ $key ] = &$refer[ $key ];
$parent[ 'child_num' ]++;
}
}
return $tree;
}
/**
* 读取csv的内容到数组
* @param string $uploadfile
* @return array|mixed
*/
function readCsv($uploadfile)
{
$file = fopen($uploadfile, "r");
while (!feof($file)) {
$data[] = fgetcsv($file);
}
$data = eval('return ' . iconv('gbk', 'utf-8', var_export($data, true)) . ';');
foreach ($data as $key => $value) {
if (!$value) {
unset($data[ $key ]);
}
}
fclose($file);
return $data;
}
/**
* 将list_to_tree的树还原成列表
*
* @param array $tree
* 原来的树
* @param string $child
* 孩子节点的键
* @param string $order
* 排序显示的键,一般是主键 升序排列
* @param array $list
* 过渡用的中间数组,
* @return array 返回排过序的列表数组
*/
function tree_to_list($tree, $child = '_child', $order = 'id', &$list = array ())
{
if (is_array($tree)) {
foreach ($tree as $key => $value) {
$reffer = $value;
if (isset($reffer[ $child ])) {
unset($reffer[ $child ]);
tree_to_list($value[ $child ], $child, $order, $list);
}
$list[] = $reffer;
}
$list = list_sort_by($list, $order, $sortby = 'asc');
}
return $list;
}
/**
* 对查询结果集进行排序
*
* @access public
* @param array $list
* 查询结果
* @param string $field
* 排序的字段名
* @param array $sortby
* 排序类型
* asc正向排序 desc逆向排序 nat自然排序
* @return array
*/
function list_sort_by($list, $field, $sortby = 'asc')
{
if (is_array($list)) {
$refer = $resultSet = array ();
foreach ($list as $i => $data)
$refer[ $i ] = &$data[ $field ];
switch ( $sortby ) {
case 'asc': // 正向排序
asort($refer);
break;
case 'desc': // 逆向排序
arsort($refer);
break;
case 'nat': // 自然排序
natcasesort($refer);
break;
}
foreach ($refer as $key => $val)
$resultSet[] = &$list[ $key ];
return $resultSet;
}
return false;
}
/**
* 对象转化为数组
* @param object $obj
*/
function object_to_array($obj)
{
if (is_object($obj)) {
$obj = (array) $obj;
}
if (is_array($obj)) {
foreach ($obj as $key => $value) {
$obj[ $key ] = object_to_array($value);
}
}
return $obj;
}
/**
* 系统加密方法
*
* @param string $data
* 要加密的字符串
* @param string $key
* 加密密钥
* @param int $expire
* 过期时间 单位 秒
* @return string
*/
function encrypt($data, $key = '', $expire = 0)
{
$key = md5(empty ($key) ? 'luckysaas098)(*' : $key);
$data = base64_encode($data);
$x = 0;
$len = strlen($data);
$l = strlen($key);
$char = '';
for ($i = 0; $i < $len; $i++) {
if ($x == $l)
$x = 0;
$char .= substr($key, $x, 1);
$x++;
}
$str = sprintf('%010d', $expire ? $expire + time() : 0);
for ($i = 0; $i < $len; $i++) {
$str .= chr(ord(substr($data, $i, 1)) + ( ord(substr($char, $i, 1)) ) % 256);
}
return str_replace(array (
'+',
'/',
'='
), array (
'-',
'_',
''
), base64_encode($str));
}
/**
* 系统解密方法
*
* @param string $data
* 要解密的字符串 必须是encrypt方法加密的字符串
* @param string $key
* 加密密钥
* @return string
*/
function decrypt($data, $key = '')
{
$key = md5(empty ($key) ? 'luckysaas098)(*' : $key);
$data = str_replace(array (
'-',
'_'
), array (
'+',
'/'
), $data);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
$data = base64_decode($data);
$expire = substr($data, 0, 10);
$data = substr($data, 10);
if ($expire > 0 && $expire < time()) {
return '';
}
$x = 0;
$len = strlen($data);
$l = strlen($key);
$char = $str = '';
for ($i = 0; $i < $len; $i++) {
if ($x == $l)
$x = 0;
$char .= substr($key, $x, 1);
$x++;
}
for ($i = 0; $i < $len; $i++) {
if (ord(substr($data, $i, 1)) < ord(substr($char, $i, 1))) {
$str .= chr(( ord(substr($data, $i, 1)) + 256 ) - ord(substr($char, $i, 1)));
} else {
$str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1)));
}
}
return base64_decode($str);
}
/**
* 数据签名认证
*/
function data_auth_sign($data)
{
// 数据类型检测
if (!is_array($data)) {
$data = (array) $data;
}
ksort($data); // 排序
$code = http_build_query($data); // url编码并生成query字符串
$sign = sha1($code); // 生成签名
return $sign;
}
/**
* 重写md5加密方式
*
* @param string $str
* @return string
*/
function data_md5($str)
{
return '' === $str ? '' : md5(md5($str) . 'LuckySaas');
}
/**
* 时间戳转时间
*/
function time_to_date($time_stamp, $format = 'Y-m-d H:i:s')
{
if ($time_stamp > 0) {
$time = date($format, $time_stamp);
} else {
$time = "";
}
return $time;
}
/**
* 时间转时间戳
*/
function date_to_time($date)
{
$time_stamp = strtotime($date);
return $time_stamp;
}
/**
* 获取唯一随机字符串
*/
function unique_random($len = 10)
{
$str = 'qwertyuiopasdfghjklzxcvbnm';
str_shuffle($str);
$res = 'nc_' . substr(str_shuffle($str), 0, $len) . date('is');
return $res;
}
/**
* 生成随机数
* @param int $length
* @return string
*/
function random_keys($length)
{
// $pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ';
$pattern = array (
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
);
$keys = array_rand($pattern, $length);
$key = '';
for ($i = 0; $i < $length; $i++) {
$key .= $pattern[ $keys[ $i ] ]; //生成php随机数
}
return $key;
}
/**
* 发送HTTP请求方法目前只支持CURL发送请求
*
* @param string $url
* 请求URL
* @param array $params
* 请求参数
* @param string $method
* 请求方法GET/POST
* @return array $data 响应数据
*/
function http($url, $timeout = 30, $header = array ())
{
if (!function_exists('curl_init')) {
throw new Exception('server not install curl');
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727;)');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 关闭 SSL 证书校验
if (!empty($header)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
$data = curl_exec($ch);
if ($data && is_array(explode("\r\n\r\n", $data))) {
list ($header, $data) = explode("\r\n\r\n", $data);
} else {
$header = explode("\r\n\r\n", $data)[ 0 ];
$data = [];
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code == 301 || $http_code == 302) {
$matches = array ();
preg_match('/Location:(.*?)\n/', $header, $matches);
$url = trim(array_pop($matches));
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
$data = curl_exec($ch);
}
if ($data == false) {
curl_close($ch);
}
@curl_close($ch);
return $data;
}
/**
* 替换数组元素
* @param array $array 数组
* @param array $replace 替换元素['key' => 'value', 'key' => 'value']
*/
function replace_array_element($array, $replace)
{
foreach ($replace as $k => $v) {
if ($v == "unset" || $v == "") {
unset($array[ $k ]);
} else {
$array[ $k ] = $v;
}
}
return $array;
}
/**
* 过滤特殊符号
* @param $string
* @return array|string|string[]|null
*/
function ihtmlspecialchars($string)
{
if (is_array($string)) {
foreach ($string as $key => $val) {
$string[ $key ] = ihtmlspecialchars($val);
}
} else {
$string = preg_replace('/&amp;((#(d{3,5}|x[a-fa-f0-9]{4})|[a-za-z][a-z0-9]{2,5});)/', '&\1',
str_replace(array ( '&', '"', '<', '>' ), array ( '&amp;', '&quot;', '&lt;', '&gt;' ), $string));
}
return $string;
}
/********************************************* 插件,站点相关函数 ************************************************************************************
*
* /**
* 插件显示内容里生成访问插件的url
*
* @param string $url
* @param array $param
* 参数格式addon_url('HelloWorld://sitehome/Game/index', [])
*/
function addon_url($url, $param = array ())
{
if (strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0) {
return $url;
}
$parse_url = parse_url($url);
$addon = $parse_url['scheme'] ?? '';
$controller = $parse_url['host'] ?? '';
$action = trim($parse_url[ 'path' ], '/');
/* 解析URL带的参数 */
if (isset($parse_url[ 'query' ])) {
parse_str($parse_url[ 'query' ], $query);
$param = array_merge($query, $param);
}
$url = $addon . '/' . $controller . '/' . $action;
if (empty($addon)) {
$url = $controller . '/' . $action;
if (empty($controller)) {
$url = $action;
}
}
return url($url, $param);
}
/**
* Url生成(重写url函数)
* @param string $url 路由地址
*/
function url(string $url = '', $vars = [])
{
if (!empty($vars)) {
if (is_array($vars)) {
$vars = http_build_query($vars);
}
$tag = REWRITE_MODULE ? '?' : '&';
$var_url = $tag . $vars;
} else {
$var_url = '';
}
$url = $url . '.html';
$url_arr = explode("/", $url);
foreach ($url_arr as $key => $val) {
if ($val == "shop") {
$url_arr[ $key ] = SHOP_MODULE;
break;
}
}
$url = implode("/", $url_arr);
// $url = str_replace("shop", SHOP_MODULE, $url); //针对输入
return ROOT_URL . '/' . $url . $var_url;
}
/**
* Url生成(重写url函数) 获取不含html的url
* @param string $url 路由地址
*/
function getUrl(string $url = '', $vars = [])
{
if (!empty($vars)) {
if (is_array($vars)) {
$vars = http_build_query($vars);
}
$tag = REWRITE_MODULE ? '?' : '&';
$var_url = $tag . $vars;
} else {
$var_url = '';
}
// $url = $url . '.html';
$url_arr = explode("/", $url);
foreach ($url_arr as $key => $val) {
if ($val == "shop") {
$url_arr[ $key ] = SHOP_MODULE;
break;
}
}
$url = implode("/", $url_arr);
// $url = str_replace("shop", SHOP_MODULE, $url); //针对输入
return ROOT_URL . '/' . $url . $var_url;
}
/**
* 解析url的插件模块控制器方法
* @param unknown $url
*/
function url_action($url)
{
if (empty($url)) {
return [
'addon' => '',
'model' => 'index',
'controller' => 'index',
'action' => 'index'
];
}
if (!strstr($url, '://')) {
$url_array = explode('/', $url);
return [
'addon' => '',
'model' => $url_array[ 0 ],
'controller' => $url_array[ 1 ],
'action' => $url_array[ 2 ]
];
} else {
$url_addon_array = explode('://', $url);
$addon = $url_addon_array[ 0 ];
$url_array = explode('/', $url_addon_array[ 1 ]);
return [
'addon' => $addon,
'model' => $url_array[ 0 ],
'controller' => $url_array[ 1 ],
'action' => $url_array[ 2 ]
];
}
}
/**
* @param $url
* @param array $param
* @return string
*/
function hash_url($url, $param = array ())
{
if (strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0) {
return $url;
}
$parse_url = parse_url($url);
$url = explode('?', $url)[ 0 ];
/* 解析URL带的参数 */
if (isset($parse_url[ 'query' ])) {
parse_str($parse_url[ 'query' ], $query);
$param = array_merge($query, $param);
}
$hash = "url={$url}";
if (!empty($param)) {
foreach ($param as $k => $v) {
$hash .= "&{$k}={$v}";
}
}
return $hash;
}
/**
* 链接跳转
* @param $url
* @param array $param
* @return string
*/
function href_url($url, $param = array ())
{
return ROOT_URL . '/shop.html#' . hash_url($url, $param);
}
/**
* 检测插件是否存在
* @param string $name
* @return number
*/
function addon_is_exit($name, $site_id = 0)
{
$addon_model = new Addon();
$addon_data = $addon_model->getAddonList([], 'name');
$addons = array_column($addon_data[ 'data' ], 'name');
if (in_array($name, $addons)) {
return 1;
} else {
return 0;
}
}
/***************************************************LuckySaaS系统函数***************************************************/
/**
* 处理事件
*
* @param string $event
* 钩子名称
* @param mixed $args
* 传入参数
* @param bool $once
* 只获取一个有效返回值
* @return void
*/
function event($event, $args = [], $once = false)
{
$res = Event::trigger($event, $args);
if (is_array($res)) {
$res = array_filter($res);
sort($res);
}
//只返回一个结果集
if ($once) {
return $res[0] ?? '';
}
return $res;
}
/**
* 错误返回值函数
* @param int $code
* @param string $message
* @param array|string $data
* @return array
*/
function error($code = -1, $message = '', $data = '')
{
return [
'code' => $code,
'message' => $message,
'data' => $data
];
}
/**
* 返回值函数
* @param int $code
* @param string $message
* @param array|string $data
* @return array
*/
function success($code = 0, $message = '', $data = '')
{
return [
'code' => $code,
'message' => $message,
'data' => $data
];
}
/**
* 实例化Model
* @param string $table
* @param array $option
* @return \app\model\Model
*/
function model($table = '', $option = [])
{
return new \app\model\Model($table, $option);
}
/**
* 获取带有表前缀的表名
* @param string $table
*/
function table($table = '')
{
return config('database.connections.prefix') . $table;
}
/**
* 获取图片的真实路径
*
* @param string $path 图片初始路径
* @param string $type 类型 big、mid、small
* @return string 图片的真实路径
*/
function img($path, $type = '')
{
$start = strripos($path, '.');
$type = $type ? '_' . strtoupper($type) : '';
$first = explode("/", $path);
$path = substr_replace($path, $type, $start, 0);
// 处理商品助手的图片路径
$path = str_replace('addons/NsGoodsAssist/', '', $path);
$path = str_replace('shop/goods/', '', $path);
if (stristr($path, "http://") === false && stristr($path, "https://") === false) {
if (is_numeric($first[ 0 ])) {
$true_path = __ROOT__ . '/upload/' . $path;
} else {
$true_path = __ROOT__ . '/' . $path;
}
} else {
$true_path = $path;
}
return $true_path;
}
/**
* 获取标准二维码格式
*
* @param string $url
* @param string $path
* @param $qrcode_name
* @param int $size
* @return string
*/
function qrcode($url, $path, $qrcode_name, $size = 4)
{
if (!is_dir($path)) {
$mode = intval('0777', 8);
mkdir($path, $mode, true);
chmod($path, $mode);
}
$path = $path . '/' . $qrcode_name . '.png';
if (file_exists($path)) {
unlink($path);
}
QRcode::png($url, $path, '', $size, 1);
return $path;
}
/**
* 前端页面api请求(通过api接口实现)
* @param string $method
* @param array $params
* @return mixed
*/
function api($method, $params = [])
{
//本地访问
$data = get_api_data($method, $params);
return $data;
}
/**
* 获取Api类
*
* @param string $method
*/
function get_api_data($method, $params)
{
$method_array = explode('.', $method);
if ($method_array[ 0 ] == 'System') {
$class_name = 'app\\api\\controller\\' . $method_array[ 1 ];
if (!class_exists($class_name)) {
return error();
}
$api_model = new $class_name($params);
} else {
$class_name = "addon\\{$method_array[0]}\\api\\controller\\" . $method_array[ 1 ];
if (!class_exists($class_name)) {
return error();
}
$api_model = new $class_name($params);
}
$function = $method_array[ 2 ];
$data = $api_model->$function($params);
return $data;
}
/**
* 根据年份计算生肖
* @param unknown $year
*/
function get_zodiac($year)
{
$animals = array (
'鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'
);
$key = ( $year - 1900 ) % 12;
return $animals[ $key ];
}
/**
* 计算.星座
* @param int $month 月份
* @param int $day 日期
* @return str
*/
function get_constellation($month, $day)
{
$constellations = array (
'水瓶座', '双鱼座', '白羊座', '金牛座', '双子座', '巨蟹座',
'狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座'
);
if ($day <= 22) {
if (1 != $month) {
$constellation = $constellations[ $month - 2 ];
} else {
$constellation = $constellations[ 11 ];
}
} else {
$constellation = $constellations[ $month - 1 ];
}
return $constellation;
}
/**
* 数组键名转化为数字
* @param $data
* @param $clild_name
* @return array
*/
function arr_key_to_int($data, $clild_name)
{
$temp_data = array_values($data);
foreach ($temp_data as $k => $v) {
if (!empty($v[ $clild_name ])) {
$temp_data[ $k ][ $clild_name ] = arr_key_to_int($v[ $clild_name ], $clild_name);
}
}
return $temp_data;
}
/**
* 以天为单位 计算间隔内的日期数组
* @param $start_time
* @param $end_time
* @param string $format
* @return array
*/
function period_group($start_time, $end_time, $format = 'Ymd')
{
$type_time = 3600 * 24;
$data = [];
for ($i = $start_time; $i <= $end_time; $i += $type_time) {
$data[] = date($format, $i);
}
return $data;
}
/**
* 数组删除另一个数组
* @param $arr
* @param $del_arr
* @return mixed
*/
function arr_del_arr($arr, $del_arr)
{
foreach ($arr as $k => $v) {
if (in_array($v, $del_arr)) {
unset($arr[ $k ]);
}
}
sort($arr);
return $arr;
}
/**
* 检测登录(应用于h5网页检测登录)
* @param unknown $url
*/
function check_auth($url = '')
{
$access_token = Session::get("access_token_" . request()->siteid());
if (empty($access_token)) {
if (!empty($url)) {
Session::set("redirect_login_url", $url);
}
//尚未登录(直接跳转)
return error(url('wap/login/login'));
}
$member_info = cache("member_info_" . request()->siteid() . $access_token);
if (empty($member_info)) {
$member_info = api("System.Member.memberInfo", [ 'access_token' => $access_token ]);
if ($member_info[ 'code' ] == 0) {
$member_info = $member_info[ 'data' ];
cache("member_info_" . request()->siteid() . $access_token, $member_info);
}
}
$member_info[ 'access_token' ] = $access_token;
return success($member_info);
}
/**
* 分割sql语句
* @param string $content sql内容
* @param bool $string 如果为真则只返回一条sql语句默认以数组形式返回
* @param array $replace 替换前缀,如:['my_' => 'me_']表示将表前缀my_替换成me_
* @return array|string 除去注释之后的sql语句数组或一条语句
*/
function parse_sql($content = '', $string = false, $replace = [])
{
// 纯sql内容
$pure_sql = [];
// 被替换的前缀
$from = '';
// 要替换的前缀
$to = '';
// 替换表前缀
if (!empty($replace)) {
$to = current($replace);
$from = current(array_flip($replace));
}
if ($content != '') {
// 多行注释标记
$comment = false;
// 按行分割,兼容多个平台
$content = str_replace([ "\r\n", "\r" ], "\n", $content);
$content = explode("\n", trim($content));
// 循环处理每一行
foreach ($content as $key => $line) {
// 跳过空行
if ($line == '') {
continue;
}
// 跳过以#或者--开头的单行注释
if (preg_match("/^(#|--)/", $line)) {
continue;
}
// 跳过以/**/包裹起来的单行注释
if (preg_match("/^\/\*(.*?)\*\//", $line)) {
continue;
}
// 多行注释开始
if (substr($line, 0, 2) == '/*') {
$comment = true;
continue;
}
// 多行注释结束
if (substr($line, -2) == '*/') {
$comment = false;
continue;
}
// 多行注释没有结束,继续跳过
if ($comment) {
continue;
}
// 替换表前缀
if ($from != '') {
$line = str_replace('`' . $from, '`' . $to, $line);
}
// sql语句
$pure_sql[] = $line;
}
// 只返回一条语句
if ($string) {
return implode("", $pure_sql);
}
// 以数组形式返回sql语句
$pure_sql = implode("\n", $pure_sql);
$pure_sql = explode(";\n", $pure_sql);
}
return $pure_sql;
}
/**
* 执行sql
* @param string $sql_name
*/
function execute_sql($sql_name)
{
$sql_string = file_get_contents($sql_name);
$sql_string = str_replace("{{prefix}}", config("database.connections.mysql.prefix"), $sql_string);
if ($sql_string) {
$sql = explode(";\n", str_replace("\r", "\n", $sql_string));
foreach ($sql as $value) {
$value = trim($value);
if (!empty($value)) {
\think\facade\Db::execute($value);
}
}
}
}
/**
* 检测目录读写权限
*/
function check_dir_iswritable($dir)
{
$testDir = $dir;
sp_dir_create($testDir);
if (sp_testwrite($testDir)) {
return true;
} else {
return false;
}
}
/**
* 检查测试文件是否可写入
*/
function sp_testwrite($d)
{
$tfile = "_test.txt";
$fp = @fopen($d . "/" . $tfile, "w");
if (!$fp) {
return false;
}
fclose($fp);
$rs = @unlink($d . "/" . $tfile);
if ($rs) {
return true;
}
return false;
}
/**
* 检查文件是否创建
*/
function sp_dir_create($path, $mode = 0777)
{
if (is_dir($path))
return true;
$ftp_enable = 0;
$path = sp_dir_path($path);
$temp = explode('/', $path);
$cur_dir = '';
$max = count($temp) - 1;
for ($i = 0; $i < $max; $i++) {
$cur_dir .= $temp[ $i ] . '/';
if (@is_dir($cur_dir))
continue;
@mkdir($cur_dir, 0777, true);
@chmod($cur_dir, 0777);
}
return is_dir($path);
}
/**
* 判断目录是否为空
* @param $dir
* @return bool
*/
function dir_is_empty($dir)
{
$handle = opendir($dir);
while (false !== ( $entry = readdir($handle) )) {
if ($entry != "." && $entry != "..") {
return FALSE;
}
}
return TRUE;
}
/**
* 创建文件夹
*
* @param string $path 文件夹路径
* @param int $mode 访问权限
* @param bool $recursive 是否递归创建
* @return bool
*/
function dir_mkdir($path = '', $mode = 0777, $recursive = true)
{
clearstatcache();
if (!is_dir($path)) {
mkdir($path, $mode, $recursive);
return chmod($path, $mode);
}
return true;
}
/**
* 文件夹文件拷贝
*
* @param string $src 来源文件夹
* @param string $dst 目的地文件夹
* @return bool
*/
function dir_copy($src = '', $dst = '', $ignore_files = [])
{
if (empty($src) || empty($dst)) {
return false;
}
$dir = opendir($src);
dir_mkdir($dst);
while (false !== ( $file = readdir($dir) )) {
if (( $file != '.' ) && ( $file != '..' )) {
if (is_dir($src . '/' . $file)) {
dir_copy($src . '/' . $file, $dst . '/' . $file);
} else {
if (!in_array($file, $ignore_files)) {
copy($src . '/' . $file, $dst . '/' . $file);
}
}
}
}
closedir($dir);
return true;
}
/**查询存在目录
* @param $dir
* @return bool
*/
function sp_exist_dir($dir)
{
$is_exist = false;
$is_write = false;
while (!$is_exist) {
$dir = dirname($dir);
if (is_dir($dir) || $dir == ".") {
$is_exist = true;
if (is_writeable($dir)) {
$is_write = true;
}
}
}
return $is_write;
}
/**
* 拼接字符串
* @param $string
* @param string $delimiter 分割字符
* @param $value
* @return string
*/
function string_split($string, $delimiter, $value)
{
return empty($string) ? $value : $string . $delimiter . $value;
}
/**
* $str为要进行截取的字符串$length为截取长度汉字算一个字字母算半个字
* @param $str
* @param int $length
* @param boolean $is_need_apostrophe 是否需要省略号
* @param string $apostrophe_pos 省略号位置 end 结尾 center 中间
* @return string
*/
function str_sub($str, $length = 10, $is_need_apostrophe = false, $apostrophe_pos = 'end')
{
$encoding = 'UTF-8';
$res_str = $str;
if(mb_strlen($str, $encoding) > $length){
if($is_need_apostrophe){
if($apostrophe_pos == 'end'){
$res_str = mb_substr($str, 0, $length, $encoding) . '...';
}else{
$half_length = floor($length / 2);
$res_str = mb_substr($str, 0, $half_length, $encoding) . '...' . mb_substr($str, mb_strlen($str, $encoding) - $half_length, $half_length, $encoding);
}
}else{
$res_str = mb_substr($str, 0, $length, 'UTF-8');
}
}
return $res_str;
}
/**
* 获取字符串长度
* @param $str
* @return int
*/
function strlen_mb($str)
{
return mb_strlen($str, 'UTF-8');
}
/**
* 删除缓存文件使用
* @param $dir
*/
function rmdirs($dir)
{
$dir = 'runtime/' . $dir;
$dh = opendir($dir);
while ($file = readdir($dh)) {
if ($file != "." && $file != "..") {
$fullpath = $dir . "/" . $file;
if (is_dir($fullpath)) {
rmdirs($fullpath);
} else {
unlink($fullpath);
}
}
}
closedir($dh);
}
/**
* 删除指定目录所有文件和目录
* @param $path
*/
function deleteDir($path)
{
if (is_dir($path)) {
//扫描一个目录内的所有目录和文件并返回数组
$dirs = scandir($path);
foreach ($dirs as $dir) {
//排除目录中的当前目录(.)和上一级目录(..)
if ($dir != '.' && $dir != '..') {
//如果是目录则递归子目录,继续操作
$sonDir = $path . '/' . $dir;
if (is_dir($sonDir)) {
//递归删除
deleteDir($sonDir);
//目录内的子目录和文件删除后删除空目录
@rmdir($sonDir);
} else {
//如果是文件直接删除
@unlink($sonDir);
}
}
}
}
}
/**
* 以天为单位 计算间隔内的日期数组
* @param $srart_time
* @param $end_time
* @param string $format
* @return array
*/
function periodGroup($srart_time, $end_time, $format = 'Ymd')
{
$type_time = 3600 * 24;
$data = [];
for ($i = $srart_time; $i <= $end_time; $i += $type_time) {
$data[] = date($format, $i);
}
return $data;
}
//解决个别中文乱码
function mbStrreplace($content, $to_encoding = "UTF-8", $from_encoding = "GBK")
{
$content = mb_convert_encoding($content, $to_encoding, $from_encoding);
$str = mb_convert_encoding(" ", $to_encoding, $from_encoding);
$content = mb_eregi_replace($str, " ", $content);
$content = mb_convert_encoding($content, $from_encoding, $to_encoding);
$content = trim($content);
return $content;
}
/**
* 将非UTF-8字符集的编码转为UTF-8
*
* @param mixed $mixed 源数据
*
* @return mixed utf-8格式数据
*/
function charset2utf8($mixed)
{
if (is_array($mixed)) {
foreach ($mixed as $k => $v) {
if (is_array($v)) {
$mixed[ $k ] = charsetToUTF8($v);
} else {
$encode = mb_detect_encoding($v, array ( 'ASCII', 'UTF-8', 'GB2312', 'GBK', 'BIG5' ));
if ($encode == 'EUC-CN') {
$mixed[ $k ] = iconv('GBK', 'UTF-8', $v);
}
}
}
} else {
$encode = mb_detect_encoding($mixed, array ( 'ASCII', 'UTF-8', 'GB2312', 'GBK', 'BIG5' ));
if ($encode == 'EUC-CN') {
$mixed = iconv('GBK', 'UTF-8', $mixed);
}
}
return $mixed;
}
/**
* 过滤bom
* @param $filename
* @return false|string
*/
function check_bom($filename)
{
$contents = file_get_contents($filename);
$charset[ 1 ] = substr($contents, 0, 1);
$charset[ 2 ] = substr($contents, 1, 1);
$charset[ 3 ] = substr($contents, 2, 1);
if (ord($charset[ 1 ]) == 239 && ord($charset[ 2 ]) == 187 && ord($charset[ 3 ]) == 191) {
$rest = substr($contents, 3);
return $rest;
} else {
return $contents;
}
}
/**
* 判断 文件/目录 是否可写(取代系统自带的 is_writeable 函数)
*
* @param string $file 文件/目录
* @return boolean
*/
function is_write($file)
{
if (is_dir($file)) {
$dir = $file;
if ($fp = @fopen("$dir/test.txt", 'w')) {
@fclose($fp);
@unlink("$dir/test.txt");
$writeable = true;
} else {
$writeable = false;
}
} else {
if ($fp = @fopen($file, 'a+')) {
@fclose($fp);
$writeable = true;
} else {
$writeable = false;
}
}
return $writeable;
}
/**
* 是否是url链接
* @param unknown $string
* @return boolean
*/
function is_url($string)
{
if (strstr($string, 'http://') === false && strstr($string, 'https://') === false) {
return false;
} else {
return true;
}
}
/**
* 计算两点之间的距离
* @param double $lng1 经度1
* @param double $lat1 纬度1
* @param double $lng2 经度2
* @param double $lat2 纬度2
* @param int $unit mkm
* @param int $decimal 位数
* @return float 米
*/
function getDistance($lng1, $lat1, $lng2, $lat2, $unit = 1, $decimal = 0)
{
$EARTH_RADIUS = 6370.996; // 地球半径系数
$PI = 3.1415926535898;
$radLat1 = $lat1 * $PI / 180.0;
$radLat2 = $lat2 * $PI / 180.0;
$radLng1 = $lng1 * $PI / 180.0;
$radLng2 = $lng2 * $PI / 180.0;
$a = $radLat1 - $radLat2;
$b = $radLng1 - $radLng2;
$distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
$distance = $distance * $EARTH_RADIUS * 1000;
if ($unit === 2) {
$distance /= 1000;
}
return round($distance, $decimal);
}
/**
*
* @param unknown $string
* @return boolean
*/
function is_json($string)
{
json_decode($string);
return ( json_last_error() == JSON_ERROR_NONE );
}
/**
* 获取站点h5域名
* @param $site_id
* @return string
*/
function getH5Domain()
{
// $config = new \app\model\web\Config();
// $info = $config->getH5DomainName();
// $h5_name = $info[ 'data' ][ 'value' ][ 'domain_name_h5' ];
// if ($h5_name) {
// return $h5_name;
// }
return ROOT_URL . '/hwapp';
}
function get_http_type()
{
$http_type = ( ( isset($_SERVER[ 'HTTPS' ]) && $_SERVER[ 'HTTPS' ] == 'on' ) || ( isset($_SERVER[ 'HTTP_X_FORWARDED_PROTO' ]) && $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] == 'https' ) ) ? 'https' : 'http';
return $http_type;
}
/**
* 获取文件地图
* @param $path
* @param array $arr
* @return array
*/
function getFileMap($path, $arr = [])
{
if (is_dir($path)) {
$dir = scandir($path);
foreach ($dir as $file_path) {
if ($file_path != '.' && $file_path != '..') {
$temp_path = $path . '/' . $file_path;
if (is_dir($temp_path)) {
$arr[ $temp_path ] = $file_path;
$arr = getFileMap($temp_path, $arr);
} else {
$arr[ $temp_path ] = $file_path;
}
}
}
return $arr;
}
}
/**
* 判断一个坐标是否在一个多边形内(由多个坐标围成的)
* 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
* 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
* @param array $point 指定点坐标 $point=['longitude'=>121.427417,'latitude'=>31.20357];
* @param array $pts 多边形坐标 顺时针方向 $arr=[['longitude'=>121.23036,'latitude'=>31.218609],['longitude'=>121.233666,'latitude'=>31.210579].............];
*/
function is_point_in_polygon($point, $pts)
{
$N = count($pts);
$boundOrVertex = true; //如果点位于多边形的顶点或边上也算做点在多边形内直接返回true
$intersectCount = 0;//cross points count of x
$precision = 2e-10; //浮点类型计算时候与0比较时候的容差
$p1 = 0;//neighbour bound vertices
$p2 = 0;
$p = $point; //测试点
$p1 = $pts[ 0 ];//left vertex
for ($i = 1; $i <= $N; ++$i) {//check all rays
// dump($p1);
if ($p[ 'longitude' ] == $p1[ 'longitude' ] && $p[ 'latitude' ] == $p1[ 'latitude' ]) {
return $boundOrVertex;//p is an vertex
}
$p2 = $pts[ $i % $N ];//right vertex
if ($p[ 'latitude' ] < min($p1[ 'latitude' ], $p2[ 'latitude' ]) || $p[ 'latitude' ] > max($p1[ 'latitude' ], $p2[ 'latitude' ])) {//ray is outside of our interests
$p1 = $p2;
continue;//next ray left point
}
if ($p[ 'latitude' ] > min($p1[ 'latitude' ], $p2[ 'latitude' ]) && $p[ 'latitude' ] < max($p1[ 'latitude' ], $p2[ 'latitude' ])) {//ray is crossing over by the algorithm (common part of)
if ($p[ 'longitude' ] <= max($p1[ 'longitude' ], $p2[ 'longitude' ])) {//x is before of ray
if ($p1[ 'latitude' ] == $p2[ 'latitude' ] && $p[ 'longitude' ] >= min($p1[ 'longitude' ], $p2[ 'longitude' ])) {//overlies on a horizontal ray
return $boundOrVertex;
}
if ($p1[ 'longitude' ] == $p2[ 'longitude' ]) {//ray is vertical
if ($p1[ 'longitude' ] == $p[ 'longitude' ]) {//overlies on a vertical ray
return $boundOrVertex;
} else {//before ray
++$intersectCount;
}
} else {//cross point on the left side
$xinters = ( $p[ 'latitude' ] - $p1[ 'latitude' ] ) * ( $p2[ 'longitude' ] - $p1[ 'longitude' ] ) / ( $p2[ 'latitude' ] - $p1[ 'latitude' ] ) + $p1[ 'longitude' ];//cross point of lng
if (abs($p[ 'longitude' ] - $xinters) < $precision) {//overlies on a ray
return $boundOrVertex;
}
if ($p[ 'longitude' ] < $xinters) {//before ray
++$intersectCount;
}
}
}
} else {//special case when ray is crossing through the vertex
if ($p[ 'latitude' ] == $p2[ 'latitude' ] && $p[ 'longitude' ] <= $p2[ 'longitude' ]) {//p crossing over p2
$p3 = $pts[ ( $i + 1 ) % $N ]; //next vertex
if ($p[ 'latitude' ] >= min($p1[ 'latitude' ], $p3[ 'latitude' ]) && $p[ 'latitude' ] <= max($p1[ 'latitude' ], $p3[ 'latitude' ])) { //p.latitude lies between p1.latitude & p3.latitude
++$intersectCount;
} else {
$intersectCount += 2;
}
}
}
$p1 = $p2;//next ray left point
}
if ($intersectCount % 2 == 0) {//偶数在多边形外
return false;
} else { //奇数在多边形内
return true;
}
}
/**
* 过滤特殊字符
* @param $strParam
* @return null|string|string[]
*/
function replaceSpecialChar($strParam)
{
$regex = "/\/|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\|\|\_|\+|\{|\}|\:|\<|\>|\?|\[|\]|\,|\.|\/|\;|\'|\`|\-|\=|\\\|\||\s+/";
return preg_replace($regex, "", $strParam);
}
function delFile($path)
{
$res = false;
if (file_exists($path)) {
$res = unlink($path);
}
return $res;
}
/**
* base64转二进制
* @param $base64Str
* @return array|boolean
*/
function base64_to_blob($base64Str)
{
if ($index = strpos($base64Str, 'base64,', 0)) {
$blobStr = substr($base64Str, $index + 7);
$typestr = substr($base64Str, 0, $index);
preg_match("/^data:(.*);$/", $typestr, $arr);
return [ 'blob' => base64_decode($blobStr), 'type' => $arr[ 1 ] ];
}
return false;
}
/**
* 获取近七日的时间
* @param string $time
* @param string $format
* @return array|boo
*/
function getweeks($time = '', $format = 'Y-m-d')
{
$time = $time != '' ? $time : time();
//组合数据
$date = [];
for ($i = 1; $i <= 10; $i++) {
$date[ $i ] = date($format, strtotime('+' . ( $i - 10 ) . ' days', $time));
}
return $date;
}
/**
* 两个数字比
* @param $first | $second
* @return string
*/
function diff_rate($first, $second)
{
if ($second != 0) {
$result = sprintf('%.2f', ( ( $first - $second ) / $second ) * 100) . '%';
} else if ($second == 0 & $first != 0) {
$result = '100%';
} else {
$result = '0%';
}
return $result;
}
/**
* 过滤bom
* @param $contents
* @return false|mixed|string
*/
function removeBom($contents)
{
$charset[ 1 ] = substr($contents, 0, 1);
$charset[ 2 ] = substr($contents, 1, 1);
$charset[ 3 ] = substr($contents, 2, 1);
if (ord($charset[ 1 ]) == 239 && ord($charset[ 2 ]) == 187 && ord($charset[ 3 ]) == 191) {
$rest = substr($contents, 3);
return $rest;
} else {
return $contents;
}
}
/**
* 复制拷贝
* @param string $src 原目录
* @param string $dst 复制到的目录
*/
function recurseCopy($src, $dst)
{
$dir = opendir($src);
@mkdir($dst);
while (false !== ( $file = readdir($dir) )) {
if (( $file != '.' ) && ( $file != '..' )) {
if (is_dir($src . '/' . $file)) {
recurseCopy($src . '/' . $file, $dst . '/' . $file);
} else {
copy($src . '/' . $file, $dst . '/' . $file);
}
}
}
closedir($dir);
}
/**
* 获取毫秒数
* @return false|string
*/
function getMillisecond()
{
list($microsecond, $time) = explode(' ', microtime());
$time = (float) sprintf('%.0f', ( floatval($microsecond) + floatval($time) ) * 1000);
return substr($time, -3);
}
/**
* #号颜色转为rgb
* @return false|string
*/
function hex2rgb($color)
{
if ($color[ 0 ] == '#') {
$color = substr($color, 1);
}
if (strlen($color) == 6) {
list($r, $g, $b) = array ( $color[ 0 ] . $color[ 1 ], $color[ 2 ] . $color[ 3 ], $color[ 4 ] . $color[ 5 ] );
} elseif (strlen($color) == 3) {
list($r, $g, $b) = array ( $color[ 0 ] . $color[ 0 ], $color[ 1 ] . $color[ 1 ], $color[ 2 ] . $color[ 2 ] );
} else {
return false;
}
$r = hexdec($r);
$g = hexdec($g);
$b = hexdec($b);
return array ( $r, $g, $b );
}
/**
* 生成条形码
* @param $content
* @param string $path
* @param int $scale 条形码放大比率
* @param int $size 条形码文本大小
* @return string
*/
function getBarcode($content, $path = '', $scale = 2, $size = 14)
{
$barcode = new Barcode($size, $content);
$path = $barcode->generateBarcode($path, $scale);
return $path;
}
/**
* 生成不重复的随机数
*
* @param int $begin
* @param int $end
* @param int $limit
* @return string
*/
function NoRand($begin = 0, $end = 20, $limit = 5)
{
$rand_array = range($begin, $end);
shuffle($rand_array);//调用现成的数组随机排列函数
$number_arr = array_slice($rand_array, 0, $limit);//截取前$limit个
$number = '';
foreach ($number_arr as $k => $v) {
$number .= $v;
}
$number = trim($number);
return $number;
}
/**
* 获取某个日期的开始时间和结束时间
* @param string $date
* @return array
*/
function getDayStartAndEndTime($date = '')
{
if (empty($date)) {
$time = time();
} else {
$time = strtotime($date);
}
//如果是第一笔订单才能累加下单会员数
$start_time = strtotime(date("Y-m-d", $time));//当日开始时间
$end_time = $start_time + 60 * 60 * 24;//当日结束时间
return [ 'start_time' => $start_time, 'end_time' => $end_time ];
}
/**
* 核验是否开启消息队列
* @param $params
* @param $fun1
* @param $fun2
* @return array
*/
function checkQueue($params, $fun1, $fun2)
{
$system_config_model = new \app\model\system\SystemConfig();
$config = $system_config_model->getSystemConfig()[ 'data' ] ?? [];
$is_open_queue = $config[ 'is_open_queue' ] ?? 0;
if ($is_open_queue) {
$result = $fun1($params);
} else {
$result = $fun2($params);
}
return $result;
}
/**
* 通用处理金额的格式(主要用于业务)
* @param $money
* @return float
*/
function moneyFormat($money)
{
$money = round($money, 2);
return $money;
}
/**
* 长链接转短链接
* @param string $long_url
* @return string
*/
function short_url(string $url) : string
{
$result = sprintf("%u", crc32($url));
$show = '';
while ($result > 0) {
$s = $result % 62;
if ($s > 35) {
$s = chr($s + 61);
} elseif ($s > 9 && $s <= 35) {
$s = chr($s + 55);
}
$show .= $s;
$result = floor($result / 62);
}
return $show;
}
function isMobile()
{
// 如果有HTTP_X_WAP_PROFILE则一定是移动设备
if (isset($_SERVER[ 'HTTP_X_WAP_PROFILE' ])) {
return true;
}
// 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
if (isset($_SERVER[ 'HTTP_VIA' ])) {
// 找不到为flase,否则为true
return (bool)stristr($_SERVER['HTTP_VIA'], "wap");
}
// 判断手机发送的客户端标志,兼容性有待提高。其中'MicroMessenger'是电脑微信
if (isset($_SERVER[ 'HTTP_USER_AGENT' ])) {
$clientkeywords = array ( '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', 'MicroMessenger' );
// 从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;
}
//读取csv数据, 配合生成器使用
function getCsvRow($file)
{
$handle = fopen($file, 'rb');
if ($handle === false) {
throw new \Exception();
}
while (feof($handle) === false) {
yield fgetcsv($handle);
}
fclose($handle);
}
/**
* 数组内部字段求和
* @param $arr
* @param $field1
* @param string $field2
* @return float|int
*/
function getArraySum($arr, $field1, $field2 = '')
{
$sum = 0;
foreach ($arr as $v) {
$num1 = $v[ $field1 ] ?? 0;
$num2 = $v[ $field2 ] ?? 1;
$sum += $num1 * $num2;
}
return $sum;
}
/**
* 过滤Emoji
* @param $str
* @return string|string[]|null
*/
function filterEmoji($str)
{
$str = preg_replace_callback(
'/./u', function(array $match) {
return strlen($match[ 0 ]) >= 4 ? '' : $match[ 0 ];
}, $str);
return $str;
}
/**
* 数字格式化小数点保留3位后三位都为0时取整数
* @param $num
* @return float
*/
function numberFormat($num)
{
$num = round($num, 3);
return $num;
}
/**
* 字符串特殊字符过滤
* @param $param
* @return array|string|string[]|null
*/
function paramFilter($param)
{
// 把数据过滤
$filter_rule = [
"/<(\\/?)(script|i?frame|style|html|body|title|link|meta|object|\\?|\\%)([^>]*?)>/isU",
"/(<[^>]*)on[a-zA-Z]+\s*=([^>]*>)/isU",
"/select|join|where|drop|like|modify|rename|insert|update|table|database|alter|truncate|\'|\/\*|\.\.\/|\.\/|union|into|load_file|outfile/is"
];
return preg_replace($filter_rule, '', $param);
}
//最小公倍数
function getLeastCommonMultiple($a, $b) {
return $a * $b / getGreatestCommonDivisor($a, $b);
}
//最大公约数
function getGreatestCommonDivisor($a, $b) {
if ($b === 0) return $a;
return getGreatestCommonDivisor($b, $a % $b);
}
/**
* 关联数组变为索引数组
* @param array $list
* @return array
*/
function keyArrToIndexArr($list, $child = 'children'){
$list = array_values($list);
foreach($list as $key=>$val){
if(isset($val[$child]) && !empty($val[$child])){
$list[$key][$child] = keyArrToIndexArr($val[$child], $child);
}
}
return $list;
}
/**
* 索引数组变为关联数组
* @param $list
* @param $pk
* @param string $child
* @return array
*/
function indexArrToKeyArr($list, $pk, $child = 'children'){
$new_list = [];
foreach($list as $val){
if(isset($val[$child]) && !empty($val[$child])){
$val[$child] = indexArrToKeyArr($val[$child], $pk, $child);
}
if(isset($val[$pk])){
$new_list[$val[$pk]] = $val;
}
}
return $new_list;
}
/**
* 获取树的末端节点
* @param $list
* @param $pk
* @param $child
* @return array
*/
function getTreeLeaf($list, $pk = 'id', $child = 'child')
{
$leaf_arr = [];
foreach($list as $val){
if(empty($val[$child])){
$leaf_arr[] = $val[$pk];
}else{
$leaf_arr = array_merge($leaf_arr, getTreeLeaf($val[$child], $pk, $child));
}
}
return $leaf_arr;
}
/**
* 覆盖数据
* @param $source_data
* @param $target_data
* @return mixed
*/
function assignData($source_data, $target_data)
{
if(is_array($target_data)){
foreach($target_data as $key=>$val){
if(isset($source_data[$key])){
$target_data[$key] = assignData($source_data[$key], $val);
}
}
}else{
$target_data = $source_data;
}
return $target_data;
}
//使用htmlpurifier防范xss攻击
function removeXss($string)
{
//相对index.php入口文件引入HTMLPurifier.auto.php核心文件
//require_once './plugins/htmlpurifier/HTMLPurifier.auto.php';
// 生成配置对象
$cfg = HTMLPurifier_Config::createDefault();
// 以下就是配置:
$cfg->set('Core.Encoding', 'UTF-8');
// 设置允许使用的HTML标签
$cfg->set('HTML.Allowed', 'div,b,strong,i,em,a[href|title],ul,ol,li,br,p[style],span[style],img[width|height|alt|src],table,tbody,tr[class],th,td[width|valign|style]');
// 设置允许出现的CSS样式属性
//$cfg->set('CSS.AllowedProperties', 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align');
// 设置a标签上是否允许使用target="_blank"
$cfg->set('HTML.TargetBlank', TRUE);
// 使用配置生成过滤用的对象
$obj = new HTMLPurifier($cfg);
// 过滤字符串
$array = json_decode($string, true);
if(is_array($array)){
$array = recursiveDealWithArrayString($array, function ($str) use($obj){
return $obj->purify($str);
});
$string = json_encode($array, JSON_UNESCAPED_UNICODE);
}else{
$string = $obj->purify($string);
}
return $string;
}
/**
* 递归处理数组中的字符串
* @param $array
* @param $callback
* @return mixed
*/
function recursiveDealWithArrayString($array, $callback){
foreach($array as $key=>$val){
if(is_string($val)){
$val = $callback($val);
}else if(is_array($val)){
$val = recursiveDealWithArrayString($val, $callback);
}
$array[$key] = $val;
}
return $array;
}
function exceptionData(\Exception $e){
return [
'file' => $e->getFile(),
'line' => $e->getLine(),
'message' => $e->getMessage(),
];
}
function getCertKey($str)
{
return require "extend/cert/".$str.".php";
}
if(!function_exists('http_url')){
function http_url($url,$data,$headers = [],$type = 'POST') {
$ch = curl_init();
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36';
curl_setopt($ch, CURLOPT_USERAGENT, $user_agent); # 在HTTP请求中包含一个"User-Agent: "头的字符串,声明用什么浏览器来打开目标网页
if(!empty($headers)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
if (1 == strpos("$".$url, "https://"))
{
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_ENCODING, '');
if($type == 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
if(is_array($data)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
}else {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
}
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // ssl 访问核心参数
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // ssl 访问核心参数
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
function secondsToTime($seconds) {
$hours = floor($seconds / 3600);
$minutes = floor(($seconds % 3600) / 60);
$seconds = $seconds % 60;
// 格式化为两位数
return sprintf("%02d:%02d:%02d", $hours, $minutes, $seconds);
}
}
/**
* 格式化数据为日志友好的字符串(保持中文可读性)
*
* @param mixed $data 要格式化的数据
* @return string 格式化后的字符串
*/
function formatForLog($data): string
{
if (is_array($data) || is_object($data)) {
// 使用 JSON_UNESCAPED_UNICODE 保持中文可读性
return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
return (string) $data;
}
/**
* 格式化数据为可导出的字符串完全避免Unicode转义
*
* @param mixed $data 要格式化的数据
* @return string 格式化后的字符串
*/
function exportForLog($data): string
{
if (is_array($data) || is_object($data)) {
// 使用 var_export 完全避免Unicode转义
return var_export($data, true);
}
return (string) $data;
}
/**
* 简单日志写入
* @param string $msg 日志内容
* @param string $level 日志级别 debug、info、warning、error
* @param string $file 日志文件名(不含路径)
*/
function log_write(string $message, string $level = 'info', string $filename = ''): void
{
// 日志文件最大大小(字节)
$maxFileSize = 10485760; // 10MB
$callerInfo = CallerInfo::getCallerInfo(1);
// 格式化日志内容
$content = sprintf(
"[%s] %s: %s (文件:%s%d) " . PHP_EOL,
date('Y-m-d H:i:s'),
strtoupper($level),
is_array($message) ? json_encode($message, JSON_UNESCAPED_UNICODE) : $message,
$callerInfo['file'],
$callerInfo['line']
);
$logPath = app()->getRuntimePath() . 'log/app_run_info/'; // eg. /runtime/shop/log
// 确保日志目录存在
if (!is_dir($logPath)) {
mkdir($logPath, 0755, true);
}
// 生成日志文件名
if (empty($filename)) {
$filename = date('Y-m-d') . '.log';
}
// 日志文件路径
$logFile = $logPath . $filename;
// 检查文件大小,如果超过限制则直接重新写入(不创建新文件)
$flags = FILE_APPEND | LOCK_EX;
if (file_exists($logFile) && is_readable($logFile)) {
try {
// 方法1使用 filesize 函数(基本方法)
$fileSize = filesize($logFile);
// 如果 filesize 失败或返回 false尝试使用 stat 函数
if ($fileSize === false) {
$stat = stat($logFile);
if ($stat && isset($stat['size'])) {
$fileSize = $stat['size'];
}
}
// 如果获取到的文件大小超过限制,使用覆盖模式
if (isset($fileSize) && $fileSize >= $maxFileSize) {
$flags = 0 | LOCK_EX;
}
} catch (Exception $e) {
// 如果发生异常,为了安全起见,继续使用追加模式
// 可以根据需要记录异常信息
}
}
// 写入文件
file_put_contents($logFile, $content, $flags);
}
/**
* 将异常格式化HTML输出方便定位
* @param mixed $th
* @return never
*/
function formatThrowForDebugWithExit($th)
{
// 以友好 HTML 格式输出异常信息并终止执行
http_response_code(500);
header('Content-Type: text/html; charset=utf-8');
$msg = htmlspecialchars($th->getMessage(), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$file = htmlspecialchars($th->getFile(), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$line = (int) $th->getLine();
$trace = htmlspecialchars($th->getTraceAsString(), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
echo <<<HTML
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<title>应用错误</title>
<style>
body{font-family:Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:#f6f8fa;color:#222;margin:0;padding:20px}
.container{max-width:900px;margin:30px auto;background:#fff;border:1px solid #e1e4e8;border-radius:6px;padding:20px;box-shadow:0 2px 6px rgba(0,0,0,.04)}
h1{margin:0 0 10px;font-size:18px;color:#d73a49}
.meta{color:#586069;margin-bottom:12px;font-size:13px}
pre{background:#f6f8fa;border:1px solid #e1e4e8;padding:12px;border-radius:4px;overflow:auto;white-space:pre-wrap;word-break:break-word}
.kv{margin:6px 0}
.key{display:inline-block;width:70px;color:#6a737d;font-weight:600}
.value{color:#24292e}
</style>
</head>
<body>
<div class="container">
<h1>应用发生异常</h1>
<div class="meta">请在开发或测试环境查看具体信息;生产环境建议关闭详细错误输出。</div>
<div class="kv"><span class="key">消息:</span><span class="value">{$msg}</span></div>
<div class="kv"><span class="key">文件:</span><span class="value">{$file}</span></div>
<div class="kv"><span class="key">行号:</span><span class="value">{$line}</span></div>
<h2 style="font-size:14px;margin-top:12px;color:#0366d6">调用栈</h2>
<pre>{$trace}</pre>
</div>
</body>
</html>
HTML;
exit;
}