PHP各版本json_encode/json_decode存在兼容性问题:7.4前不处理非法UTF-8字节,8.0+拒绝含\0的JSON,浮点精度因serialize_precision配置而异,AI返回JSON易超默认512层嵌套限制。

PHP 7.4 之前 json_encode() 默认不处理 UTF-8 非法字节
如果你从 API、数据库或用户上传拿到一段含混合编码(比如 GBK 转换不彻底、Windows 记事本保存的 UTF-8 BOM 残留)的字符串,PHP 7.3 及更早版本的 json_encode() 会静默失败——返回 false,但不报错,json_last_error_msg() 显示 "Malformed UTF-8 characters, possibly incorrectly encoded"。
常见于旧系统导出 CSV 再转 JSON、微信/钉钉回调带乱码 nickname、MySQL latin1 表存中文后直读再 encode 的场景。
- 解决办法不是硬加
JSON_UNESCAPED_UNICODE(它只影响输出格式,不修复输入) - 先用
mb_convert_encoding($str, 'UTF-8', 'UTF-8')做一次“自转换”,可触发非法字节清理(PHP 内部会丢弃无法识别的 byte sequence) - 或更稳妥:用
iconv('UTF-8', 'UTF-8//IGNORE', $str),//IGNORE会跳过损坏字节 - 别依赖
mb_detect_encoding()—— 它在多字节混杂时误判率高,直接转比检测更可靠
PHP 8.0+ json_decode() 对 null 字符更敏感
PHP 8.0 起,json_decode() 默认拒绝含 C 语言风格空字符(\u0000)的 JSON 字符串,哪怕它在字符串值内部。此前版本会忽略或截断,现在直接返回 null,json_last_error() 是 JSON_ERROR_CTRL_CHAR。
典型来源:前端 JS 用 JSON.stringify() 序列化含 \0 的 ArrayBuffer 视图、某些 Python json.dumps() 输出未过滤的二进制数据、日志系统注入调试用的 null 字节。
立即学习“PHP免费学习笔记(深入)”;
- 检查原始 JSON 是否含
\u0000:用preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', $json) - 预处理建议:
$json = str_replace("\x00", '', $json)(简单粗暴),或用preg_replace()清理控制字符 - 注意:PHP 8.1 新增
JSON_THROW_ON_ERROR标志,开启后会抛JsonException,便于捕获而非靠false判断
不同 PHP 版本对浮点数精度处理不一致
PHP 7.1 之前,json_encode(0.1 + 0.2) 输出 0.30000000000000004;7.2+ 默认启用 serialize_precision = -1 后,结果变为 0.3。但若 ini 配置被显式设为正数(如 14),又会退回到老行为。
这会导致前后端数值比对失败,尤其在金融类接口中,0.3 == 0.30000000000000004 在 JS 里是 false。
- 不要依赖 PHP 默认序列化精度 —— 在
json_encode()前手动四舍五入:round($value, 2) - 避免用
float存金额,改用整数分、或字符串传"19.99",后端再转 - 检查运行时配置:
var_dump(ini_get('serialize_precision')),线上环境务必统一为-1
AI 数据解析时 JSON 嵌套层级与递归限制易被忽略
大模型输出 JSON 常嵌套过深(如树形 schema、多层 reasoning steps),PHP 默认 json_decode() 递归深度是 512(PHP 7.4+),但部分 AI 返回结构可达 600+ 层。超限后返回 null,错误码是 JSON_ERROR_DEPTH。
这不是数据格式错,而是解析器中途放弃 —— 日志里看不到明显异常,容易误判为 AI 输出为空。
- 调用时显式加大深度:
json_decode($json, true, 1024, JSON_THROW_ON_ERROR) - 若仍失败,先用
json_decode($json, false, 10, JSON_THROW_ON_ERROR)测试前 10 层是否合法,定位是深度问题还是某层语法错 - 生产环境建议加兜底:对 AI 返回做
strlen()和括号配对检查(substr_count($json, '{') === substr_count($json, '}')),避免无效 JSON 进入 decode
最麻烦的不是版本差异本身,而是同一套代码在本地 PHP 8.2、测试机 7.4、客户服务器 7.2 上表现不一 —— 所有 JSON 处理逻辑都得把版本兼容和输入清洗写死,不能假设“数据本来就是干净的”。



















