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 位随机 IV(AES-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; } } /** * 多维数组排序 */ function param_sort($chatList, $param) { usort($chatList, function ($a, $b) use($param) { // 将时间字符串转为时间戳,便于比较 $timeA = strtotime($a[$param]); $timeB = strtotime($b[$param]); // 倒序:$timeB - $timeA(正序则$timeA - $timeB) return $timeB - $timeA; }); return $chatList; } /** * 1. 判断字符串是否包含Emoji表情(精准正则) * @param string $str 待检测字符串 * @return bool */ function hasEmoji($str) { if (empty($str)) return false; $emojiRegex = '/[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F1E0}-\x{1F1FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{1F900}-\x{1F9FF}]/u'; return preg_match($emojiRegex, $str) > 0; } /** * 2. 存储前处理:含Emoji则Base64编码,否则原样返回 * @param string $str 待入库字符串 * @return string 处理后的值(Base64编码/原字符串) */ function encodeEmojiForDb($str) { // 空值直接返回 if (empty($str)) return $str; // 含表情则Base64编码,否则返回原字符串 return hasEmoji($str) ? base64_encode($str) : $str; } /** * 3. 提取后处理:识别Base64编码的Emoji内容,解码还原 * @param string $str 从数据库取出的字符串 * @return string 还原后的值(解码后/原字符串) */ function decodeEmojiFromDb($str) { if (empty($str)) return $str; // 步骤1:判断是否为有效Base64(strict模式验证) $decoded = base64_decode($str, true); if ($decoded === false) { // 不是有效Base64,返回原字符串 return $str; } // 步骤2:解码后验证是否含Emoji(避免普通Base64字符串误解码) if (hasEmoji($decoded)) { // 是含表情的Base64,返回解码结果 return $decoded; } // 是有效Base64但不含表情,返回原字符串(避免误解码普通Base64内容) return $str; } /** * 微信风格时间格式化(仅用原生 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:00(today - 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); } }