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('/&((#(d{3,5}|x[a-fa-f0-9]{4})|[a-za-z][a-z0-9]{2,5});)/', '&\1', str_replace(array ( '&', '"', '<', '>' ), array ( '&', '"', '<', '>' ), $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 m,km * @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 <<
{$trace}