php 5.2–8.x 的 json_encode() 存在关键差异:默认行为、错误返回值及对资源/循环引用的处理不同;老项目升级需预处理数据、显式检查返回值并容错降级,且必须设置 utf-8 响应头。

PHP 返回 JSON 数据在 5.2–8.x 各版本间确实存在关键差异,主要集中在 json_encode() 默认行为、编码失败返回值、以及对资源/循环引用的处理上。老项目升级或跨版本部署时,不注意这些点容易导致前端收不到数据、返回空字符串或直接报错。
PHP 5.2–5.4 中 json_encode() 的兼容性陷阱
PHP 5.2 刚引入 json_encode(),但缺乏现代容错机制:它对非 UTF-8 字符串(如 GBK)直接返回 false,且不支持 JSON_UNESCAPED_UNICODE 等标志;5.3 加入了部分错误码(json_last_error()),但无法通过 JSON_PARTIAL_OUTPUT_ON_ERROR 自动替换非法字符。
- 若输入含 GBK 或 Latin-1 字符,必须手动转为 UTF-8:
mb_convert_encoding($data, 'UTF-8', 'GBK') - 不能依赖
JSON_UNESCAPED_UNICODE—— 该常量直到 PHP 5.4 才定义,低版本需用iconv('UTF-8', 'UTF-8//IGNORE', $str)预处理字符串 - 调用后务必检查返回值:
if ($json === false) { error_log('JSON encode failed: ' . json_last_error_msg()); }
PHP 5.5+ 的默认行为变化与隐性风险
从 PHP 5.5 开始,json_encode() 对浮点数精度、NaN/INF 处理更严格,默认不再静默转换;PHP 7.1+ 更是将 json_encode(null) 改为返回 "null"(之前是 false)。这些“修复”反而会暴露旧代码中被掩盖的问题。
- 浮点数若来自数据库 float 字段,可能因精度差异导致前后端解析不一致 —— 建议统一转为字符串或指定小数位:
round($val, 6) - PHP 7.2+ 禁用了对资源类型(如
fopen()返回的句柄)的 JSON 编码,会直接返回false;老代码若未过滤资源字段,升级后 JSON 就会为空 -
JSON_INVALID_UTF8_SUBSTITUTE(PHP 7.2+)可替代非法 UTF-8 字节,但低版本无此能力,不能当作通用兜底方案
跨版本安全返回 JSON 的最小兼容写法
不依赖高版本特性,同时避免低版本崩溃,核心是「预处理 + 显式判断 + 容错降级」。以下逻辑适用于 PHP 5.2 至 8.3:
立即学习“PHP免费学习笔记(深入)”;
// 确保数据是纯数组/对象,不含资源、闭包、循环引用
function safe_json_encode($data, $options = 0) {
// 移除资源、跳过不可序列化项(PHP < 5.5 不支持 JSON_PARTIAL_OUTPUT_ON_ERROR)
$data = json_encode($data, $options | JSON_UNESCAPED_SLASHES);
if ($data === false) {
// 回退:尝试清理常见问题
$data = recursive_utf8_encode($data);
$data = json_encode($data, $options | JSON_UNESCAPED_SLASHES);
}
return $data ?: '[]';
}
// 简单 UTF-8 清理(适用于老版本)
function recursive_utf8_encode($value) {
if (is_string($value)) {
return mb_convert_encoding($value, 'UTF-8', 'UTF-8,GBK,ISO-8859-1');
}
if (is_array($value) || is_object($value)) {
foreach ($value as $k => $v) {
$value[$k] = recursive_utf8_encode($v);
}
}
return $value;
}
实际输出时仍需设置 header:header('Content-Type: application/json; charset=utf-8');,否则 IE8 等老客户端可能解析失败。
调试时最容易忽略的两个点
一是 json_last_error() 在多线程或 FastCGI 环境下可能被其他请求覆盖,务必在 json_encode() 后立即调用;二是 setlocale() 影响数字格式(如小数点被设为逗号),会导致浮点数编码失败 —— 调试时先加 setlocale(LC_ALL, 'C'); 排查。











