removeEvilAttributes(['style']);
// 检查到 xss 代码之后使用 cleanXss 替换它
$antiXss->setReplacement('cleanXss');
return $antiXss->xss_clean($string);
}
}
if (!function_exists('htmlspecialchars_decode_improve')) {
/**
* html解码增强
* 被 filter函数 内的 htmlspecialchars 编码的字符串,需要用此函数才能完全解码
* @param string $string
* @param int $flags
* @return string
*/
function htmlspecialchars_decode_improve(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string
{
return htmlspecialchars_decode($string, $flags);
}
}
if (!function_exists('get_sys_config')) {
/**
* 获取站点的系统配置,不传递参数则获取所有配置项
* @param string $name 变量名
* @param string $group 变量分组,传递此参数来获取某个分组的所有配置项
* @param bool $concise 是否开启简洁模式,简洁模式下,获取多项配置时只返回配置的键值对
* @return mixed
* @throws Throwable
*/
function get_sys_config(string $name = '', string $group = '', bool $concise = true): mixed
{
if ($name) {
// 直接使用->value('value')不能使用到模型的类型格式化
$config = configModel::cache($name, null, configModel::$cacheTag)->where('name', $name)->find();
if ($config) $config = $config['value'];
} else {
if ($group) {
$temp = configModel::cache('group' . $group, null, configModel::$cacheTag)->where('group', $group)->select()->toArray();
} else {
$temp = configModel::cache('sys_config_all', null, configModel::$cacheTag)->order('weigh desc')->select()->toArray();
}
if ($concise) {
$config = [];
foreach ($temp as $item) {
$config[$item['name']] = $item['value'];
}
} else {
$config = $temp;
}
}
return $config;
}
}
if (!function_exists('get_route_remark')) {
/**
* 获取当前路由后台菜单规则的备注信息
* @return string
*/
function get_route_remark(): string
{
$controllerName = request()->controller;
$actionName = request()->action;
$path = str_replace('.', '/', $controllerName);
$remark = Db::name('admin_rule')
->where('name', $path)
->whereOr('name', $path . '/' . $actionName)
->value('remark');
return __((string)$remark);
}
}
if (!function_exists('full_url')) {
/**
* 获取资源完整url地址;若安装了云存储或 config/buildadmin.php 配置了CdnUrl,则自动使用对应的CdnUrl
* @param string $relativeUrl 资源相对地址 不传入则获取域名
* @param string|bool $domain 是否携带域名 或者直接传入域名
* @param string $default 默认值
* @return string
*/
function full_url(string $relativeUrl = '', string|bool $domain = true, string $default = ''): string
{
// 存储/上传资料配置
Event::trigger('uploadConfigInit', App::getInstance());
$cdnUrl = Config::get('buildadmin.cdn_url');
if (!$cdnUrl) {
$cdnUrl = request()->upload['cdn'] ?? '//' . request()->host();
}
if ($domain === true) {
$domain = $cdnUrl;
} elseif ($domain === false) {
$domain = '';
}
$relativeUrl = $relativeUrl ?: $default;
if (!$relativeUrl) return $domain;
$regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
if (preg_match('/^http(s)?:\/\//', $relativeUrl) || preg_match($regex, $relativeUrl) || $domain === false) {
return $relativeUrl;
}
$url = $domain . $relativeUrl;
$cdnUrlParams = Config::get('buildadmin.cdn_url_params');
if ($domain === $cdnUrl && $cdnUrlParams) {
$separator = str_contains($url, '?') ? '&' : '?';
$url .= $separator . $cdnUrlParams;
}
return $url;
}
}
if (!function_exists('encrypt_password')) {
/**
* 加密密码
* @deprecated 使用 hash_password 代替
*/
function encrypt_password($password, $salt = '', $encrypt = 'md5')
{
return $encrypt($encrypt($password) . $salt);
}
}
if (!function_exists('hash_password')) {
/**
* 创建密码散列(hash)
*/
function hash_password(string $password): string
{
return password_hash($password, PASSWORD_DEFAULT);
}
}
if (!function_exists('verify_password')) {
/**
* 验证密码是否和散列值匹配
* @param string $password 密码
* @param string $hash 散列值
* @param array $extend 扩展数据
*/
function verify_password(string $password, string $hash, array $extend = []): bool
{
// 第一个表达式直接检查是否为 password_hash 函数创建的 hash 的典型格式,即:$algo$cost$salt.hash
if (str_starts_with($hash, '$') || password_get_info($hash)['algoName'] != 'unknown') {
return password_verify($password, $hash);
} else {
// 兼容旧版 md5 加密的密码
return encrypt_password($password, $extend['salt'] ?? '') === $hash;
}
}
}
if (!function_exists('str_attr_to_array')) {
/**
* 将字符串属性列表转为数组
* @param string $attr 属性,一行一个,无需引号,比如:class=input-class
* @return array
*/
function str_attr_to_array(string $attr): array
{
if (!$attr) return [];
$attr = explode("\n", trim(str_replace("\r\n", "\n", $attr)));
$attrTemp = [];
foreach ($attr as $item) {
$item = explode('=', $item);
if (isset($item[0]) && isset($item[1])) {
$attrVal = $item[1];
if ($item[1] === 'false' || $item[1] === 'true') {
$attrVal = !($item[1] === 'false');
} elseif (is_numeric($item[1])) {
$attrVal = (float)$item[1];
}
if (strpos($item[0], '.')) {
$attrKey = explode('.', $item[0]);
if (isset($attrKey[0]) && isset($attrKey[1])) {
$attrTemp[$attrKey[0]][$attrKey[1]] = $attrVal;
continue;
}
}
$attrTemp[$item[0]] = $attrVal;
}
}
return $attrTemp;
}
}
if (!function_exists('action_in_arr')) {
/**
* 检测一个方法是否在传递的数组内
* @param array $arr
* @return bool
*/
function action_in_arr(array $arr = []): bool
{
$arr = is_array($arr) ? $arr : explode(',', $arr);
if (!$arr) {
return false;
}
$arr = array_map('strtolower', $arr);
if (in_array(strtolower(request()->action), $arr) || in_array('*', $arr)) {
return true;
}
return false;
}
}
if (!function_exists('build_suffix_svg')) {
/**
* 构建文件后缀的svg图片
* @param string $suffix 文件后缀
* @param ?string $background 背景颜色,如:rgb(255,255,255)
* @return string
*/
function build_suffix_svg(string $suffix = 'file', string $background = null): string
{
$suffix = mb_substr(strtoupper($suffix), 0, 4);
$total = unpack('L', hash('adler32', $suffix, true))[1];
$hue = $total % 360;
[$r, $g, $b] = hsv2rgb($hue / 360, 0.3, 0.9);
$background = $background ?: "rgb($r,$g,$b)";
return '';
}
}
if (!function_exists('get_area')) {
/**
* 获取省份地区数据
* @throws Throwable
*/
function get_area(): array
{
$province = request()->get('province', '');
$city = request()->get('city', '');
$where = ['pid' => 0, 'level' => 1];
if ($province !== '') {
$where['pid'] = $province;
$where['level'] = 2;
if ($city !== '') {
$where['pid'] = $city;
$where['level'] = 3;
}
}
return Db::name('area')
->where($where)
->field('id as value,name as label')
->select()
->toArray();
}
}
if (!function_exists('hsv2rgb')) {
function hsv2rgb($h, $s, $v): array
{
$r = $g = $b = 0;
$i = floor($h * 6);
$f = $h * 6 - $i;
$p = $v * (1 - $s);
$q = $v * (1 - $f * $s);
$t = $v * (1 - (1 - $f) * $s);
switch ($i % 6) {
case 0:
$r = $v;
$g = $t;
$b = $p;
break;
case 1:
$r = $q;
$g = $v;
$b = $p;
break;
case 2:
$r = $p;
$g = $v;
$b = $t;
break;
case 3:
$r = $p;
$g = $q;
$b = $v;
break;
case 4:
$r = $t;
$g = $p;
$b = $v;
break;
case 5:
$r = $v;
$g = $p;
$b = $q;
break;
}
return [
floor($r * 255),
floor($g * 255),
floor($b * 255)
];
}
}
if (!function_exists('ip_check')) {
/**
* IP检查
* @throws Throwable
*/
function ip_check($ip = null): void
{
$ip = is_null($ip) ? request()->ip : $ip;
$noAccess = get_sys_config('no_access_ip');
$noAccess = !$noAccess ? [] : array_filter(explode("\n", str_replace("\r\n", "\n", $noAccess)));
if ($noAccess && IpUtils::checkIp($ip, $noAccess)) {
$response = Response::create(['msg' => 'No permission request'], 'json', 403);
throw new HttpResponseException($response);
}
}
}
if (!function_exists('set_timezone')) {
/**
* 设置时区
* @throws Throwable
*/
function set_timezone($timezone = null): void
{
// $defaultTimezone = Config::get('app.default_timezone');
// $timezone = is_null($timezone) ? get_sys_config('time_zone') : $timezone;
// if ($timezone && $defaultTimezone != $timezone) {
// Config::set([
// 'app.default_timezone' => $timezone
// ]);
// date_default_timezone_set($timezone);
// }
date_default_timezone_set('Asia/Shanghai');
}
}
if (!function_exists('env')) {
/**
* 获取环境变量值
* @access public
* @param string $name 环境变量名(支持二级 .号分割)
* @param string $default 默认值
* @return mixed
*/
function env(?string $name = null, $default = null)
{
return getenv($name, $default);
}
}
if (!function_exists('get_upload_config')) {
/**
* 获取上传配置
* @return array
*/
function get_upload_config(): array
{
// 存储/上传资料配置
Event::trigger('uploadConfigInit', App::getInstance());
$uploadConfig = Config::get('upload');
$uploadConfig['max_size'] = Filesystem::fileUnitToByte($uploadConfig['max_size']);
$upload = request()->upload;
if (!$upload) {
$uploadConfig['mode'] = 'local';
return $uploadConfig;
}
unset($upload['cdn']);
return array_merge($upload, $uploadConfig);
}
}
if (!function_exists('get_auth_token')) {
/**
* 获取鉴权 token
* @param array $names
* @return string
*/
function get_auth_token(array $names = ['ba', 'token']): string
{
$separators = [
'header' => ['', '-'], // batoken、ba-token【ba_token 不在 header 的接受列表内因为兼容性不高,改用 http_ba_token】
'param' => ['', '-', '_'], // batoken、ba-token、ba_token
'server' => ['_'], // http_ba_token
];
$tokens = [];
$request = request();
foreach ($separators as $fun => $sps) {
foreach ($sps as $sp) {
$tokens[] = $request->$fun(($fun == 'server' ? 'http_' : '') . implode($sp, $names));
}
}
$tokens = array_filter($tokens);
return array_values($tokens)[0] ?? '';
}
}
if (!function_exists('keys_to_camel_case')) {
/**
* 将数组 key 的命名方式转换为小写驼峰
* @param array $array 被转换的数组
* @param array $keys 要转换的 key,默认所有
* @return array
*/
function keys_to_camel_case(array $array, array $keys = []): array
{
$result = [];
foreach ($array as $key => $value) {
// 将键名转换为驼峰命名
$camelCaseKey = $keys && in_array($key, $keys) ? parse_name($key, 1, false) : $key;
if (is_array($value)) {
// 如果值是数组,递归转换
$result[$camelCaseKey] = keys_to_camel_case($value);
} else {
$result[$camelCaseKey] = $value;
}
}
return $result;
}
}
if (!function_exists('p')) {
/**
* 将数组 key 的命名方式转换为小写驼峰
* @param array $array 被转换的数组
* @param array $keys 要转换的 key,默认所有
* @return array
*/
function p(...$p)
{
if(count($p) > 1) {
foreach ($p as $k => $v) {
print_r($v);
print_r('---');
}
}else {
print_r($p[0]);
}
die;
}
}
if (!function_exists('returnErrorData')) {
function returnErrorData($msg)
{
return ['code' => -1, 'message' => $msg, 'data' => []];
}
}
function getYesterdayFiles($dirPath)
{
// 1. 获取昨天的日期前缀(格式:两位数日期,如 08)
$prefix = date('d', strtotime('-1 day'));
// 2. 定义要读取的文件夹路径
// 示例:读取 public 目录下的 files 文件夹
// 若读取其他目录,可使用 app_path()、root_path() 等助手函数
// $dirPath = app_path() . 'data/'; // app/data/ 目录
// 3. 验证文件夹是否存在
if (!is_dir($dirPath)) {
return "文件夹不存在:{$dirPath}";
}
// 4. 读取文件夹中的所有文件,并筛选以指定前缀开头的文件
$fileNames = [];
// 打开目录
$dirHandle = opendir($dirPath);
if ($dirHandle) {
// 循环读取目录中的文件
while (($fileName = readdir($dirHandle)) !== false) {
// 排除 . 和 .. 目录
if ($fileName != '.' && $fileName != '..') {
// 检查文件名是否以昨天日期前缀开头
if (strpos($fileName, $prefix) === 0) {
$fileNames[] = $fileName;
}
}
}
closedir($dirHandle); // 关闭目录句柄
}
// 5. 输出结果
return [
'prefix' => $prefix,
'dir' => $dirPath,
'files' => $fileNames,
'count' => count($fileNames)
];
}
if (!function_exists('returnSuccessData')) {
function returnSuccessData($data = [], $arr_data = [])
{
if(!empty($arr_data)) {
$return = $arr_data;
$return['code'] = 0;
$return['message'] = 'ok';
return $return;
}else {
return ['code' => 0, 'message' => 'ok', 'data' => $data];
}
}
}
if (!function_exists('maskPhoneNumber')) {
/**
* 隐藏手机号中间部分
* @param string $phone 手机号
* @param int $start 开始位置
* @param int $length 替换长度
* @return string 处理后的手机号
*/
function maskPhoneNumber(string $phone, int $start = 3, int $length = 4): string
{
if (strlen($phone) < $start + $length) {
return $phone; // 长度不足,直接返回原号码
}
return substr_replace($phone, str_repeat('*', $length), $start, $length);
}
}
if (!function_exists('sha256Hex')) {
function sha256Hex($data) {
return hash('sha256', $data);
}
}
if (!function_exists('toSerialCode')) {
function toSerialCode($id) {
$r = str_split("0123456789ABCDEFGHIJKLMNOPQRSTUV");
$binLen = count($r); // 进制长度,通常为32
$s = 8; // 目标字符串长度
$e = "00000000"; // 补全字符,通常为8个'0'
$buf = array_fill(0, 32, '');
$charPos = 32;
$id = (string) $id;
while (intdiv($id, $binLen) > 0) {
$ind = (int) ($id % $binLen);
$buf[--$charPos] = $r[$ind];
$id = intdiv($id, $binLen);
}
$buf[--$charPos] = $r[(int) ($id % $binLen)];
$str = implode(array_slice($buf, $charPos));
// 长度不足时补全
if (strlen($str) < $s) {
$str = substr($e, 0, $s - strlen($str)) . $str;
}
return strtoupper($str);
}
}
function toSnakeCase($string) {
// 将驼峰或帕斯卡命名法转为下划线命名
$string = preg_replace('/([a-z])([A-Z])/', '$1_$2', $string);
$string = preg_replace('/([A-Z])([A-Z][a-z])/', '$1_$2', $string);
return strtolower($string);
}
function arrayKeysToSnakeCase($array) {
$result = [];
foreach ($array as $key => $value) {
$newKey = is_string($key) ? toSnakeCase($key) : $key;
if (is_array($value)) {
$value = arrayKeysToSnakeCase($value); // 递归处理
}
$result[$newKey] = $value;
}
return $result;
}
function getNormalDate(string $time = '')
{
return $time ? date($time) : date('Y-m-d H:i:s');
}
/**
* ThinkPHP 防抖函数,基于 cache() 助手函数实现
*
* @param string $key 唯一操作标识符(例如 user_id_action)
* @param int $waitMs 等待毫秒数(支持毫秒)
* @param callable $callback 要执行的操作
* @return mixed|null 等待时间内重复调用将返回 null
*/
function debounceAndRun(string $key, callable $callback,int $waitMs = 2000)
{
// cache() 默认单位是秒,我们转成浮点秒数
$ttl = $waitMs / 1000;
// 尝试设置缓存,存在时不执行
if (!cache($key)) {
cache($key, 1, $ttl); // 设置一个短暂缓存用于防抖
return $callback();
}
throw new Exception("操作过于频繁,请稍后再试");
}
function debounce(string $key,int $waitMs = 20)
{
// cache() 默认单位是秒,我们转成浮点秒数
$ttl = $waitMs;
// 尝试设置缓存,存在时不执行
if (!cache($key)) {
cache($key, 1, $ttl); // 设置一个短暂缓存用于防抖
}else{
throw new SysException("操作过于频繁,请稍后再试");
}
}
/**
* 类似 Java 的 StrUtil.format("{}, {}") 占位符替换函数
*
* @param string $template 模板字符串
* @param mixed ...$args 依次替换的值
* @return string
*/
function format(string $template, ...$args): string
{
foreach ($args as $value) {
if (!is_string($value)) {
$value = json_encode($value);
}
$template = preg_replace('/\{}/', (string)$value, $template, 1);
}
return $template;
}
if (!function_exists('uuid')) {
function uuid():string
{
$data = random_bytes(16);
$data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
$data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
return vsprintf('%s%s%s%s%s%s%s%s', str_split(bin2hex($data), 4));
}
}
if (!function_exists('formatTo4Decimal')) {
/**
* 使用sprintf保留4位小数
* @param float $number 输入的浮点数
* @return string 格式化后的字符串
*/
function formatTo4Decimal(float $number): string
{
// 先乘以10000取整,再除以10000,最后格式化为4位小数
$truncated = (int)($number * 10000) / 10000;
return sprintf('%.4f', $truncated);
}
}
function hasEmpty(...$args): bool
{
foreach ($args as $arg) {
if (empty($arg)) {
return true;
}
}
return false;
}
if(!function_exists('shuffleMultiArray')) {
/**
* 打乱多维数组的顶层元素
* @param array $array 输入的多维数组
* @return array 打乱顺序后的数组
*/
function shuffleMultiArray(array $array): array {
$keys = array_keys($array);
shuffle($keys); // 随机打乱键名顺序
$shuffled = [];
foreach ($keys as $key) {
$shuffled[$key] = $array[$key];
}
return $shuffled;
}
}
if(!function_exists('convertToCamelCase')) {
/**
* 下划线转驼峰
*/
function convertToCamelCase(array | null $data): array | null {
if (!$data) {
return $data;
}
$result = [];
foreach ($data as $k => $item) {
$camelItem = [];
if(is_array($item)) {
foreach ($item as $key => $value) {
$camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) {
return strtoupper($match[1]);
}, $key));
$camelItem[$camelKey] = $value;
}
$result[] = $camelItem;
}else {
$camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) {
return strtoupper($match[1]);
}, $k));
$result[$camelKey] = $item;
}
}
return $result;
}
}
if(!function_exists('apiconvertToCamelCase')) {
/**
* 下划线转驼峰
*/
function apiconvertToCamelCase(array $data): array {
$result = [];
foreach ($data as $k => $item) {
$camelItem = [];
if(is_array($item)) {
foreach ($item as $key => $value) {
$camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) {
return strtoupper($match[1]);
}, $key));
$camelItem[$camelKey] = $value;
}
$result[] = $camelItem;
}else {
$camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) {
return strtoupper($match[1]);
}, $k));
$result[$camelKey] = $item;
}
}
$result = convertUserIdToString($result);
return $result;
}
}
if(!function_exists('page')) {
/**
* 分页计算
*/
function page($page = 1, $limit = 10) {
return ($page - 1) * $limit;
}
}
/**
* 自动加锁执行回调逻辑,结束后自动释放锁
*
* @param string $key 锁的键名
* @param int $expire 锁过期时间(秒)
* @param \Closure $callback 要执行的回调函数
* @return mixed 回调函数返回值,失败返回 false
*/
function runWithLock(string $key, int $expire, \Closure $callback)
{
$lockValue = uniqid();
$val = cache($key);
if ($val) {
return false;
}
// 加锁(NX:不存在时设置,EX:过期时间)
$acquired = cache($key, $lockValue, $expire);
if (!$acquired) {
return false; // 获取锁失败
}
try {
return $callback(); // 执行传入的方法
} finally {
release($key, $lockValue);
}
}
if (!function_exists('cache')) {
/**
* 缓存管理
* @param string $name 缓存名称
* @param mixed $value 缓存值
* @param mixed $options 缓存参数
* @param string $tag 缓存标签
* @return mixed
*/
function cache(?string $name = null, $value = '', $options = null, $tag = null)
{
if (is_null($name)) {
return app('cache');
}
if ('' === $value) {
// 获取缓存
return str_starts_with($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
} elseif (is_null($value)) {
// 删除缓存
return Cache::delete($name);
}
// 缓存数据
if (is_array($options)) {
$expire = $options['expire'] ?? null; //修复查询缓存无法设置过期时间
} else {
$expire = $options;
}
if (is_null($tag)) {
return Cache::set($name, $value, $expire);
} else {
return Cache::tag($tag)->set($name, $value, $expire);
}
}
}
/**
* 解锁(用Lua防止误删)
*/
function release(string $key, string $value)
{
$lua = << uuid(),
];
Log::info("消息队列发送消息,对列名: $class, 携带数据: ".json_encode($data).', 延时时间: '.$seconds);
if ($seconds > 0) {
Queue::later($seconds, $class, $data);
}else{
Queue::push($class, $data);
}
}
if(!function_exists('daysBetween')) {
/**
* 计算两个日期之间的天数差(纯原生函数实现)
* @param string $startDate 开始日期(格式:YYYY-MM-DD)
* @param string $endDate 结束日期(格式:YYYY-MM-DD,默认当前日期)
* @return int 天数差(绝对值)
*/
function daysBetween(string $startDate, string $endDate = null): int {
// 解析开始日期
$startTimestamp = strtotime($startDate);
if ($startTimestamp === false) {
throw new InvalidArgumentException("无效的开始日期格式: $startDate");
}
// 处理结束日期(默认为当前日期)
if ($endDate === null) {
$endTimestamp = time();
} else {
$endTimestamp = strtotime($endDate);
if ($endTimestamp === false) {
throw new InvalidArgumentException("无效的结束日期格式: $endDate");
}
}
// 计算天数差(忽略时区影响,仅计算日期差)
$startDay = strtotime(date('Y-m-d', $startTimestamp));
$endDay = strtotime(date('Y-m-d', $endTimestamp));
$daysDiff = ($endDay - $startDay) / (24 * 60 * 60); // 86400秒/天
return (int)$daysDiff;
}
}
if(!function_exists('buildFlowDays')) {
/**
* 生成从开始日期起的连续日期列表
* @param string $beginDay 开始日期(格式:YYYY-MM-DD)
* @param int $activeDays 连续天数
* @return array 日期字符串列表(格式:YYYY-MM-DD)
*/
function buildFlowDays(string $beginDay, int $activeDays)
{
$flowDays = [];
$timestamp = strtotime($beginDay); // 转换为时间戳
for ($i = 0; $i < $activeDays; $i++) {
// 计算偏移后的时间戳(每天86400秒)
$currentTimestamp = $timestamp + ($i * 86400);
// 格式化为YYYY-MM-DD并添加到列表
$flowDays[] = date('Y-m-d', $currentTimestamp);
}
return $flowDays;
}
}
if(!function_exists('buildFlowDaysTwo')) {
/**
* 生成从开始日期到结束日期的连续日期列表(纯原生函数实现)
* @param string $beginDay 开始日期(格式:YYYY-MM-DD)
* @param string $endDay 结束日期(格式:YYYY-MM-DD)
* @return array 日期字符串列表(格式:YYYY-MM-DD)
*/
function buildFlowDaysTwo(string $beginDay, string $endDay): array
{
$flowDays = [];
$currentTimestamp = strtotime($beginDay);
$endTimestamp = strtotime($endDay);
while (true) {
// 将当前日期添加到列表(每次循环开始时添加)
$flowDays[] = date('Y-m-d', $currentTimestamp);
// 判断是否达到结束日期
if ($currentTimestamp === $endTimestamp) {
break;
}
// 未达到结束日期,增加一天
$currentTimestamp += 86400; // 86400秒 = 1天
}
return $flowDays;
}
}
if(!function_exists('todayAfterSecond')) {
/**
* 获取当日剩余秒数
* @return int 剩余秒数
*/
function todayAfterSecond()
{
// 获取当前时间戳
$now = time();
// 获取当天结束时间(23:59:59)的时间戳
$endOfDay = strtotime('tomorrow -1 second');
// 计算剩余秒数
$diffSeconds = $endOfDay - $now;
return $diffSeconds;
}
}
if(!function_exists('generateRedisKey')) {
function generateRedisKey($key, $id)
{
return 'sys:limit:' . $key . ':' . $id;
}
}
if(!function_exists('bankCard')) {
function bankCard($bankCardNo)
{
if (empty($bankCardNo)) {
return $bankCardNo;
}
$bankCardNo = trim($bankCardNo);
$length = strlen($bankCardNo);
if ($length < 9) {
return $bankCardNo;
}
$midLength = $length - 8; // 中间需要脱敏的长度
$buf = '';
// 保留前4位
$buf .= substr($bankCardNo, 0, 4);
// 中间部分用*替换,每4位加一个空格
for ($i = 0; $i < $midLength; $i++) {
if ($i % 4 === 0) {
$buf .= ' ';
}
$buf .= '*';
}
// 保留后4位,前面加一个空格
$buf .= ' ' . substr($bankCardNo, -4);
return $buf;
}
}
if(!function_exists('email')) {
function email($email)
{
if (empty($email)) {
return '';
}
$index = strpos($email, '@');
if ($index <= 1) {
return $email;
}
// 保留第一个字符,中间部分用*替换,保留@及后面的域名
$prefix = substr($email, 0, 1);
$suffix = substr($email, $index);
$maskLength = $index - 1;
$maskedPart = str_repeat('*', $maskLength);
return $prefix . $maskedPart . $suffix;
}
}
if(!function_exists('idCardNum')) {
/**
* 对身份证号进行脱敏处理
* @param string $idCardNum 身份证号码
* @param int $front 保留前几位
* @param int $end 保留后几位
* @return string 脱敏后的身份证号
*/
function idCardNum($idCardNum, $front, $end)
{
// 身份证不能为空
if (empty($idCardNum)) {
return '';
}
// 需要截取的长度不能大于身份证号长度
if (($front + $end) > strlen($idCardNum)) {
return '';
}
// 需要截取的位数不能小于0
if ($front < 0 || $end < 0) {
return '';
}
// 保留前$front位和后$end位,中间用*替换
$maskLength = strlen($idCardNum) - $front - $end;
$maskedPart = str_repeat('*', $maskLength);
return substr($idCardNum, 0, $front) . $maskedPart . substr($idCardNum, -$end);
}
}
if(!function_exists('http_post')) {
function http_post($url, $data, $headers = [], $timeout = 30, $contentType = 'application/json')
{
// 初始化cURL
$ch = curl_init();
// 处理请求数据
if ($contentType === 'application/json') {
$data = json_encode($data);
} elseif (is_array($data)) {
$data = http_build_query($data);
}
// 设置请求头
$defaultHeaders = [
"Content-Type: $contentType",
"Content-Length: " . strlen($data)
];
$headers = array_merge($defaultHeaders, $headers);
// 设置cURL选项
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// 执行请求
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
// 关闭cURL
curl_close($ch);
// 处理错误
if ($response === false) {
return $error;
}
return $response;
}
}
function shiro_simple_hash_hex_salt(string $algorithm, string $source, ?string $salt = null, int $iterations = 1): string
{
if ($iterations < 1) {
$iterations = 1;
}
$salt = $salt ?? ''; // 将 null 转为空字符串
$hexSalt = bin2hex($salt);
// 转为字节
$sourceBytes = is_string($source) ? $source : strval($source);
$saltBytes = $hexSalt !== '' ? hex2bin($hexSalt) : '';
if ($saltBytes === false && $hexSalt !== '') {
throw new InvalidArgumentException("Invalid hex salt: $hexSalt");
}
// 初始 hash(含 salt)
$digest = hash_init($algorithm);
if ($saltBytes !== '') {
hash_update($digest, $saltBytes);
}
hash_update($digest, $sourceBytes);
$result = hash_final($digest, true);
// 后续迭代(不加盐)
for ($i = 1; $i < $iterations; $i++) {
$result = hash($algorithm, $result, true); // binary
}
return bin2hex($result);
}
if(!function_exists('get_master_connect_name')) {
function get_master_connect_name()
{
return config('database.z_library');
}
}
if(!function_exists('get_slave_connect_name')) {
function get_slave_connect_name()
{
return config('database.search_library');
}
}
if(!function_exists('get_file_info')) {
function get_file_info($file)
{
return file_get_contents($file);
}
}
function buildPageInfo(array $info, bool $isRecord=false)
{
$size = count($info);
return [
'totalCount' => $size,
'pageSize' => $size,
'totalPage' => 1,
'currPage' => 1,
$isRecord ? 'records' : 'list' => $info
];
}
/**
* 将日期补全为当天的开始时间(00:00:00)
* @param string $date 输入日期(支持格式:'2023-10-05'、'2023/10/05'、'2023-10-05 12:30:45' 等)
* @return string 补全后的时间字符串(格式:'Y-m-d 00:00:00')
* @throws \Exception 若日期格式无效则抛出异常
*/
function completeStartTime($date) {
// 将输入日期转换为时间戳(支持多种格式)
$timestamp = strtotime($date);
if ($timestamp === false) {
throw new \Exception("无效的日期格式:{$date},请使用类似 'YYYY-MM-DD' 的格式");
}
// 格式化时间戳为 "YYYY-MM-DD 00:00:00"
return date('Y-m-d 00:00:00', $timestamp);
}
/**
* 模糊删除 Redis 中匹配指定模式的所有 key(使用 SCAN)
*
* @param string $pattern 匹配模式,如 "user_*"
* @param int $count 每次扫描数量,默认 100
* @return int 删除的 key 数量
*/
function deleteRedisKeysByPattern(string $pattern, int $count = 100): int
{
$redis = Cache::store('redis')->handler();
$iterator = null;
$deleted = 0;
do {
$keys = $redis->scan($iterator, $pattern, $count);
if (!empty($keys)) {
$redis->del(...$keys);
$deleted += count($keys);
}
} while ($iterator > 0);
return $deleted;
}
/**
* 将多维数组中的user_id或userId键的值转换为字符串类型
* @param array $data 待处理的多维数组
* @return array 处理后的数组
*/
function convertUserIdToString(array $data): array {
$result = [];
foreach ($data as $key => $value) {
// 处理键为user_id或userId的情况
$id_list = [
'user_id',
'userId',
'id',
'roleId',
'courseDetailsId',
'courseId',
'ordersId',
];
if (in_array($key, $id_list)) {
$result[$key] = (string)$value;
continue;
}
// 递归处理子数组
if (is_array($value)) {
$result[$key] = convertUserIdToString($value);
} else {
// 其他类型的值保持不变
$result[$key] = $value;
}
}
return $result;
}