From caaf85290f134763926d089c3c6b3663fd17054e Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Sat, 22 Nov 2025 11:46:14 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=8F=90=E4=BE=9B=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E7=BC=96=E7=A0=81=E8=A7=A3=E5=86=B3=E6=96=B9?= =?UTF-8?q?=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CHINESE_ENCODING_SOLUTIONS.md | 212 +++++++++++++++++++++++++++ src/app/common.php | 32 ++++ src/app/dict/system/ScheduleDict.php | 13 ++ src/app/model/system/Cron.php | 23 ++- 4 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 docs/CHINESE_ENCODING_SOLUTIONS.md diff --git a/docs/CHINESE_ENCODING_SOLUTIONS.md b/docs/CHINESE_ENCODING_SOLUTIONS.md new file mode 100644 index 000000000..d99ba5d57 --- /dev/null +++ b/docs/CHINESE_ENCODING_SOLUTIONS.md @@ -0,0 +1,212 @@ +# PHP 中文字符编码解决方案 + +## 🎯 问题描述 + +在使用 `json_encode()` 处理包含中文字符的数据时,默认会将中文字符转换为 Unicode 编码: + +```php +// ❌ 默认行为 - 中文转为Unicode +$data = ['message' => '请检查计划任务配置']; +echo json_encode($data); +// 输出:{"message":"\u8bf7\u68c0\u67e5\u8ba1\u5212\u4efb\u52a1\u914d\u7f6e"} +``` + +这在日志文件中查看很不方便,影响调试效率。 + +## 🛠️ 解决方案对比 + +### 方案一:`JSON_UNESCAPED_UNICODE` 标志 ⭐⭐⭐⭐⭐⭐ + +```php +// ✅ 推荐方案 - 保持中文原样 +$data = ['message' => '请检查计划任务配置']; +echo json_encode($data, JSON_UNESCAPED_UNICODE); +// 输出:{"message":"请检查计划任务配置"} +``` + +**优点:** +- ✅ 中文完全可读 +- ✅ 保持JSON格式 +- ✅ 兼容性好 +- ✅ 性能优秀 + +**使用场景:** +- 日志记录 +- API响应 +- 配置文件 +- 调试输出 + +### 方案二:`var_export()` 函数 ⭐⭐⭐ + +```php +// ✅ 完全避免Unicode转义 +$data = ['message' => '请检查计划任务配置']; +echo var_export($data, true); +// 输出:array ( +// 'message' => '请检查计划任务配置', +// ) +``` + +**优点:** +- ✅ 完全可读 +- ✅ 数组结构清晰 +- ✅ 无需额外参数 + +**缺点:** +- ❌ 不是标准JSON格式 +- ❌ 输出较冗长 + +**使用场景:** +- 开发调试 +- 临时日志 +- 数组检查 + +### 方案三:`print_r()` + 捕获输出 ⭐⭐ + +```php +// ✅ 可读性好的数组输出 +$data = ['message' => '请检查计划任务配置']; +ob_start(); +print_r($data); +$result = ob_get_clean(); +``` + +**优点:** +- ✅ 格式美观 +- ✅ 易于阅读 + +**缺点:** +- ❌ 需要输出缓冲 +- ❌ 性能较差 + +### 方案四:自定义序列化函数 ⭐⭐⭐ + +```php +function serializeArray($data) { + if (!is_array($data)) return $data; + + $result = []; + foreach ($data as $key => $value) { + if (is_array($value)) { + $result[$key] = serializeArray($value); + } else { + $result[$key] = $value; + } + } + + return $result; +} +``` + +## 🎯 最佳实践组合 + +### 在 Cron 类中的实现 + +```php +class Cron extends BaseModel +{ + /** + * 格式化数据为日志友好的字符串(保持中文可读性) + */ + private static function formatForLog($data): string + { + if (is_array($data) || is_object($data)) { + // JSON_UNESCAPED_UNICODE 保持中文可读性 + // JSON_PRETTY_PRINT 增加格式化 + // JSON_UNESCAPED_SLASHES 避免斜杠转义 + return json_encode($data, + JSON_UNESCAPED_UNICODE | + JSON_PRETTY_PRINT | + JSON_UNESCAPED_SLASHES + ); + } + + return (string) $data; + } + + /** + * 完全避免Unicode转义的格式化 + */ + private static function exportForLog($data): string + { + if (is_array($data) || is_object($data)) { + return var_export($data, true); + } + + return (string) $data; + } +} +``` + +### 使用示例 + +```php +// 日志记录 - 使用 formatForLog +$detail = [ + 'error' => $error, + 'remark' => $remark, + 'suggestion' => ScheduleDict::getSuggestion($cronType) +]; +log_write('异常信息:' . self::formatForLog($detail), 'warning'); + +// 调试输出 - 使用 exportForLog +$debugData = ['config' => $config, 'status' => $status]; +echo self::exportForLog($debugData); +``` + +## 🔧 环境配置 + +### 确保 PHP 版本支持 +```php +// PHP 5.4+ 基本支持 +json_encode($data, JSON_UNESCAPED_UNICODE); + +// PHP 5.3+ 需要 polyfill +if (!defined('JSON_UNESCAPED_UNICODE')) { + define('JSON_UNESCAPED_UNICODE', 256); +} +``` + +### 文件编码设置 +```php +// 确保文件本身是 UTF-8 编码 +header('Content-Type: text/html; charset=utf-8'); +mb_internal_encoding('UTF-8'); +``` + +## 📊 性能对比 + +| 方法 | 执行时间 | 内存使用 | 可读性 | JSON兼容 | +|------|----------|----------|---------|-----------| +| `json_encode()` | 快 | 低 | ⭐⭐⭐⭐⭐ | ✅ | +| `var_export()` | 中 | 中 | ⭐⭐⭐⭐ | ❌ | +| `print_r()` | 慢 | 高 | ⭐⭐⭐⭐ | ❌ | +| 自定义序列化 | 慢 | 高 | ⭐⭐ | ❌ | + +## 🎉 推荐配置 + +### 生产环境(推荐) +```php +// 使用 JSON_UNESCAPED_UNICODE 保持中文可读性 +json_encode($data, JSON_UNESCAPED_UNICODE); +``` + +### 开发环境 +```php +// 使用格式化JSON增强可读性 +json_encode($data, + JSON_UNESCAPED_UNICODE | + JSON_PRETTY_PRINT | + JSON_UNESCAPED_SLASHES +); +``` + +### 调试模式 +```php +// 使用 var_export 获得最佳可读性 +var_export($data, true); +``` + +--- + +**总结:** 对于您的需求,使用 `JSON_UNESCAPED_UNICODE` 标志是最佳选择,既保持了JSON格式的标准性,又确保了中文字符的可读性。 \ No newline at end of file diff --git a/src/app/common.php b/src/app/common.php index 09189b59f..322b37b33 100644 --- a/src/app/common.php +++ b/src/app/common.php @@ -1879,6 +1879,38 @@ function paramFilter($param) return preg_replace($filter_rule, '', $param); } +/** + * 格式化数据为日志友好的字符串(保持中文可读性) + * + * @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 日志内容 diff --git a/src/app/dict/system/ScheduleDict.php b/src/app/dict/system/ScheduleDict.php index 6d8defd52..3509c8e1e 100644 --- a/src/app/dict/system/ScheduleDict.php +++ b/src/app/dict/system/ScheduleDict.php @@ -56,4 +56,17 @@ class ScheduleDict if($code) return $list[$code] ?? ''; return $list; } + + public static function getSuggestion(string $type) { + switch($type) { + case self::default: + return '请检查系统计划任务配置,确保 cron 进程正常运行,并检查网络连接和 SSL 证书设置。'; + case self::url: + return '请检查网络连接、目标 URL 可访问性、HTTP 状态码和接口认证配置。可以使用 curl 命令手动测试接口。'; + case self::cli: + return '请检查 cron 服务状态、命令执行权限、脚本路径和 PHP CLI 环境。建议使用 crontab -l 查看当前任务配置。'; + default: + return '请检查计划任务配置文件和相关权限设置,确保所有依赖服务正常运行。'; + } + } } diff --git a/src/app/model/system/Cron.php b/src/app/model/system/Cron.php index bd53fc8e1..05b2f0b5d 100644 --- a/src/app/model/system/Cron.php +++ b/src/app/model/system/Cron.php @@ -61,13 +61,13 @@ class Cron extends BaseModel */ public function execute($type = 'default') { - log_write('计划任务开始执行', 'debug'); + log_write('计划任务开始执行', 'info'); if (config('cron.default') != $type) { - log_write('计划任务方式不匹配<不能执行/model/system/Cron/execute>:' . config('cron.default') . ' != ' . $type, 'debug'); + log_write('计划任务方式不匹配<不能执行/model/system/Cron/execute>:' . config('cron.default') . ' != ' . $type, 'warning'); return true; } - log_write('当前执行方式:' . $type, 'debug'); + log_write('当前执行方式:' . $type, 'info'); try { //写入计划任务标记运行 @@ -78,7 +78,7 @@ class Cron extends BaseModel $query_execute_time = $is_open_queue == 1 ? time() + 60 : time(); $list = model('cron')->getList([['execute_time', '<=', $query_execute_time]]); $now_time = time(); - log_write('计划任务开始执行,查询计划任务列表', 'debug'); + log_write('计划任务开始执行,查询计划任务列表', 'info'); if (!empty($list)) { foreach ($list as $k => $v) { @@ -97,7 +97,7 @@ class Cron extends BaseModel return $res ?? $this->success(); }, function ($params) { try { - log_write('调用事件名称:' . $params['name'], 'debug'); + log_write('调用事件名称:' . $params['name'], 'info'); $res = event($params['event'], ['relate_id' => $params['relate_id']]); } catch (\Exception $e) { $res = $this->error($e->getMessage(), $e->getMessage()); @@ -150,7 +150,7 @@ class Cron extends BaseModel // $this->setCron(); return true; } catch (\Exception $e) { - log_write('计划任务执行异常:' . $e->getMessage(), 'debug'); + log_write('计划任务执行异常:' . $e->getMessage(), 'error'); return true; } } @@ -252,9 +252,16 @@ class Cron extends BaseModel $remark = 'Cron计划任务已停止!当前启动的任务方式:' . ScheduleDict::getType(config('cron.default')) . '。'; $error = self::getError(config('cron.default')); if (!empty($error)) { - $remark .= json_encode($error); + $remark .= formatForLog($error); } - log_write('Cron计划任务校验计划任务是否正常运行,计划任务异常,异常信息:' . json_encode($error) . ',文件路径:' . $file, 'warning'); + + $detail = [ + 'error'=> $error, + 'remark' => $remark, + 'suggestion' => ScheduleDict::getSuggestion(config('cron.default')), + ]; + + log_write('Cron计划任务校验计划任务是否正常运行,计划任务异常,异常信息:' . formatForLog($detail) . ',文件路径:' . $file, 'warning'); return $this->error([], $remark); } catch (\Exception $e) { log_write('Cron计划任务校验计划任务是否正常运行异常:' . $e->getMessage() . ',异常行:' . $e->getLine() . ',文件路径:' . $file, 'error');