Files
p_ysk/app/functions.php
2025-11-19 15:34:37 +08:00

412 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
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
/**
* Here is your custom functions.
*/
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;
}
}
/**
* Here is your custom functions.
*/
if (!function_exists('d')) {
/**
* 将数组 key 的命名方式转换为小写驼峰
* @param array $array 被转换的数组
* @param array $keys 要转换的 key默认所有
* @return array
*/
function d()
{
return date('Y-m-d H:i:s');
}
}
/**
* 加密函数带随机字符AES-256-CBC
* @param string $plaintext 待加密明文(如字符串、数字)
* @param string $key 加密密钥(建议至少 8 位,越复杂越安全)
* @return string|false 加密后的密文(失败返回 false
*/
function simple_encrypt(string $plaintext, string $key): string|false
{
try {
// 1. 生成随机字符Salt + IV
$salt = random_bytes(8); // 8 位随机盐值(增加密钥多样性)
$iv = random_bytes(16); // 16 位随机 IVAES-256-CBC 块大小要求)
// 2. 密钥处理Salt + 原始密钥 → SHA256 哈希 → 32 位密钥AES-256 要求)
$encryptedKey = hash('sha256', $salt . $key, true); // true 表示返回二进制数据
// 3. 明文填充PKCS7 填充AES 要求明文长度是块大小的整数倍)
$blockSize = openssl_cipher_iv_length('aes-256-cbc');
$padding = $blockSize - (strlen($plaintext) % $blockSize);
$paddedPlaintext = $plaintext . str_repeat(chr($padding), $padding);
// 4. AES 加密(返回 Base64 编码的密文)
$ciphertext = openssl_encrypt(
$paddedPlaintext,
'aes-256-cbc',
$encryptedKey,
OPENSSL_RAW_DATA, // 输出原始二进制数据(后续手动 Base64
$iv
);
if ($ciphertext === false) {
throw new Exception('加密失败:' . openssl_error_string());
}
// 5. 拼接 Salt + IV + 密文Base64 编码后用冒号分隔,避免字符冲突)
return base64_encode($salt) . ':' . base64_encode($iv) . ':' . base64_encode($ciphertext);
} catch (Exception $e) {
error_log('加密异常:' . $e->getMessage());
return false;
}
}
/**
* 解密函数(对应 simple_encrypt
* @param string $ciphertext 加密后的密文
* @param string $key 解密密钥(必须与加密密钥一致)
* @return string|false 解密后的明文(失败返回 false
*/
function simple_decrypt(string $ciphertext, string $key): string|false
{
try {
// 1. 拆分密文Salt:IV:密文(按冒号分割)
$parts = explode(':', $ciphertext);
if (count($parts) !== 3) {
throw new Exception('密文格式错误');
}
// 2. Base64 解码,获取原始 Salt、IV、密文
$salt = base64_decode($parts[0]);
$iv = base64_decode($parts[1]);
$ciphertextRaw = base64_decode($parts[2]);
if ($salt === false || $iv === false || $ciphertextRaw === false) {
throw new Exception('Base64 解码失败');
}
// 3. 密钥处理(与加密时一致)
$encryptedKey = hash('sha256', $salt . $key, true);
// 4. AES 解密
$paddedPlaintext = openssl_decrypt(
$ciphertextRaw,
'aes-256-cbc',
$encryptedKey,
OPENSSL_RAW_DATA,
$iv
);
if ($paddedPlaintext === false) {
throw new Exception('解密失败:' . openssl_error_string());
}
// 5. 去除 PKCS7 填充
$padding = ord(substr($paddedPlaintext, -1));
$plaintext = substr($paddedPlaintext, 0, -$padding);
return $plaintext;
} catch (Exception $e) {
error_log('解密异常:' . $e->getMessage());
return false;
}
}
/**
* 微信风格时间格式化(仅用原生 date/strtotime 函数,周一为一周起点)
* @param string $inputTime 输入时间字符串支持Y-m-d H:i:s、timestamp、Y-m-d 等)
* @param string $timezone 时区(默认北京时间,原生函数依赖服务器时区,建议提前配置)
* @return string 格式化结果09:11、昨天 15:30、周一 10:20、11-16 08:05、2024-05-20 14:30
*/
function formatWeChatTime(string $inputTime, string $timezone = 'Asia/Shanghai'): string
{
// 1. 配置时区(原生函数需手动设置,避免服务器时区差异)
date_default_timezone_set($timezone);
// 2. 转换目标时间为时间戳(兼容多种输入格式)
$targetTimestamp = strtotime($inputTime);
if ($targetTimestamp === false) {
error_log('无效时间格式:' . $inputTime);
return $inputTime; // 无效时间返回原始值
}
// 3. 计算关键时间边界的时间戳(原生函数核心)
$nowTimestamp = time(); // 当前时间戳
$todayStart = strtotime('today'); // 今天 00:00:00等价于 mktime(0,0,0,date('m'),date('d'),date('Y'))
$yesterdayStart = strtotime('yesterday'); // 昨天 00:00:00today - 86400
$currentWeekDay = date('N', $todayStart); // 今天是本周第几天1=周一7=周日)
$mondayStart = strtotime("-$currentWeekDay days +1 day", $todayStart); // 本周一 00:00:00
$yearStart = strtotime(date('Y') . '-01-01 00:00:00'); // 今年 1月1日 00:00:00
// 4. 周几映射date('N') 返回 1-7 → 对应周一-周日)
$weekMap = [1 => '周一', 2 => '周二', 3 => '周三', 4 => '周四', 5 => '周五', 6 => '周六', 7 => '周日'];
// 5. 按优先级判断场景(从近到远)
// 场景1今天>= 今天0点
if ($targetTimestamp >= $todayStart) {
return date('H:i', $targetTimestamp);
}
// 场景2昨天>= 昨天0点 且 < 今天0点
if ($targetTimestamp >= $yesterdayStart && $targetTimestamp < $todayStart) {
return '昨天 ' . date('H:i', $targetTimestamp);
}
// 场景3本周内>= 本周一0点 且 < 今天0点→ 显示周几+时间
if ($targetTimestamp >= $mondayStart && $targetTimestamp < $todayStart) {
$targetWeekDay = date('N', $targetTimestamp); // 目标时间的周几
return $weekMap[$targetWeekDay] . ' ' . date('H:i', $targetTimestamp);
}
// 场景4今年内>= 今年1月1日 且 < 本周一0点→ 显示月-日+时间
if ($targetTimestamp >= $yearStart && $targetTimestamp < $mondayStart) {
return date('m-d H:i', $targetTimestamp);
}
// 场景5去年及以前< 今年1月1日→ 显示完整日期+时间
return date('Y-m-d H:i', $targetTimestamp);
}
/**
* 多维数组去重并重新索引
* @param array $array 待处理的多维数组
* @return array 去重后并重新索引的数组
*/
function uniqueMultidimensionalArray($array, $k = 'user_id') {
$unique = [];
$seenIds = []; // 用于记录已出现的user_id
foreach ($array as $item) {
// 确保子数组包含user_id字段
if (!isset($item[$k])) {
continue; // 跳过不包含user_id的子数组可选也可抛出异常
}
$userId = $item[$k];
// 如果user_id未出现过则保留该记录
if (!in_array($userId, $seenIds)) {
$seenIds[] = $userId;
$unique[] = $item;
}
// 已出现的user_id会被自动跳过去重
}
// 重新索引数组从0开始的连续数字键
return array_values($unique);
}
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;
}
}
if (!function_exists('curl_post')) {
function curl_post($url, $params, $op)
{
$postData = json_encode($params, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if(!empty($params['shopId'])) {
\support\Log::info('curl_post--->' . $postData);
}
$opts = array(
CURLOPT_TIMEOUT => 30,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_ENCODING => 'gzip',
);
$opts[CURLOPT_URL] = $url;
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = $postData;
$opts[CURLOPT_HTTPHEADER] = $op;
$ch = curl_init();
curl_setopt_array($ch, $opts);
$responsedata = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
\support\Log::info('curl_post_结果--->' . $responsedata);
return $responsedata;
}
}
// 处理${}字符串
if (!function_exists('handldollerstr')) {
function handldollerstr($str)
{
$pattern = '/\$\{(.*?)\}/';
preg_match_all($pattern, $str, $matches);
$result = $matches[1];
return $result;
}
}
// 处理${}字符串
if (!function_exists('saveJson_arr')) {
function saveJson_arr($th_json, $cont_json, $user)
{
$th_json_arr = json_decode($th_json, true);
$cont_json_arr = json_decode($cont_json, true);
$new_arr = [];
foreach ($th_json_arr as $k => $v) {
foreach ($cont_json_arr as $k1 => $v1) {
if($k == $v1) {
if($k1 == 'username') {
$new_arr[$k1] = removeEmojiAndSpecialChars($user['nick_name'], false);
}else {
$new_arr[$k1] = removeEmojiAndSpecialChars($v, false);
// $new_arr[$k1] = $v;
}
}
}
}
return json_encode($new_arr);
}
}
if (!function_exists('replace_placeholder_keys')) {
function replace_placeholder_keys($str, $replaceArray)
{
preg_match_all('/\$\{.*?\}/', $str, $matches);
$originalPlaceholders = $matches[0]; // 结果: ['${用户昵称}', '${店铺名称}', ...]
$arrayKeys = array_keys($replaceArray); // 结果: ['username', 'shopname', 'time', 'item1', 'item2']
$replacePairs = [];
foreach ($originalPlaceholders as $index => $placeholder) {
if (isset($arrayKeys[$index])) {
// 关键:明确拼接 ${键} 结构
$replacePairs[$placeholder] = '${' . $arrayKeys[$index] . '}';
}
}
$newStr = strtr($str, $replacePairs);
return $newStr;
}
}
if (!function_exists('replace_json_keys')) {
function replace_json_keys($originalJson, $mapJson, $user)
{
$originalData = json_decode($originalJson, true);
$mapData = json_decode($mapJson, true);
$reverseMap = array_flip($mapData);
$newData = [];
foreach ($originalData as $oldKey => $value) {
// 如果原始键在映射中存在,则替换为对应的新键;否则保留原键
$newKey = $reverseMap[$oldKey] ?? $oldKey;
$newData[$newKey] = $value;
if($newKey == 'username') {
$newData[$newKey] = $user['nick_name'];
}
}
$newJson = json_encode($newData, JSON_UNESCAPED_UNICODE);
return $newJson;
}
}
/**
* 移除字符串中的表情和特殊字符
* @param string $str 原始字符串
* @param bool $keepBasicPunct 是否保留基本标点(默认保留)
* @return string 处理后的字符串
*/
if (!function_exists('removeEmojiAndSpecialChars')) {
function removeEmojiAndSpecialChars($str, $keepBasicPunct = true)
{
// 1. 移除 Emoji 表情(匹配常见 Emoji 的 Unicode 范围)
$emojiPattern = '/[\x{1F600}-\x{1F64F}]|[\x{1F300}-\x{1F5FF}]|[\x{1F680}-\x{1F6FF}]|[\x{1F1E0}-\x{1F1FF}]|[\x{2600}-\x{26FF}]/u';
$str = preg_replace($emojiPattern, '', $str);
// 2. 移除特殊字符(控制字符、非预期字符)
if ($keepBasicPunct) {
// 保留:字母、数字、中文、空格,以及基本标点(!@#$%^&*()_+-= etc.
// 正则含义:匹配所有不在以下范围内的字符并移除
// \p{L}:所有语言的字母(包括中文、英文、日文等)
// \p{N}:所有数字
// \s空白字符空格、换行等
// !-~ASCII 可见标点33-126 范围内的字符)
$specialPattern = '/[^\p{L}\p{N}\s!-~]/u';
} else {
// 不保留标点:只保留字母、数字、中文、空格
$specialPattern = '/[^\p{L}\p{N}\s]/u';
}
$str = preg_replace($specialPattern, '', $str);
// 3. 移除连续的空白字符(可选,根据需求)
$str = preg_replace('/\s+/', ' ', $str);
return trim($str);
}
}