php json_decode 默认将数字解析为float,小整数看似int实为double;大整数需用json_bigint_as_string转字符串再手动安全转int,否则精度丢失或溢出。

PHP json_decode 后整型变浮点或字符串?
PHP 解析 JSON 时,json_decode 默认把所有数字都转成 float(哪怕 JSON 里是 123),除非它能被安全地表示为 int —— 但这个“安全”取决于平台(32/64 位)、是否启用了 JSON_BIGINT_AS_STRING,以及数字大小。比如 9223372036854775807 在 64 位系统上可能变成 int,但 9223372036854775808 就会溢出成 float 或字符串(取决于选项)。
常见错误现象:is_int($data['id']) 返回 false,明明 JSON 里写的是 "id": 1001;或者大 ID(如微信 openid、MongoDB ObjectId)被截断或科学计数法显示。
- 默认不加参数时,
json_decode($json)把所有数字当 float 处理(小整数看似是 int,实则是 float,gettype()返回double) - 加
true参数(关联数组模式)不会改变数字类型行为,依然 float 优先 - 想保留整型,必须用
JSON_BIGINT_AS_STRING+ 手动转换,或用filter_var($val, FILTER_VALIDATE_INT)辅助判断
用 JSON_BIGINT_AS_STRING 避免大整数丢失
这是最稳妥的起点:让所有超出 PHP 整型范围的数字(比如 64 位整数)直接变成字符串,避免 float 精度丢失或溢出。但注意——它只对“超限”数字生效,小整数(如 123)仍可能被解析为 float。
使用场景:处理微信支付回调、支付宝通知、数据库导出的 bigint 字段、分布式 ID(如 Snowflake)等。
立即学习“PHP免费学习笔记(深入)”;
-
json_decode($json, false, 512, JSON_BIGINT_AS_STRING)—— 第三个参数是 depth,不能省略才能传第四个标志位 - 即使加了该标志,
123还是 float;但9223372036854775808会是 string 类型 - 后续需统一用
filter_var($val, FILTER_VALIDATE_INT)或正则/^-?\d+$/判断是否可转整型,再用(int)或intval()转换(注意负数和边界)
is_numeric() 和 ctype_digit() 别乱用
很多人用 is_numeric('123') 判整型,但它返回 true 对于 '123.0'、'1e3' 甚至 ' 123 ' —— 完全不符合“JSON 中原始整型”的语义。而 ctype_digit() 又不支持负数。
真正靠谱的判断逻辑是分层的:
- 先看类型:如果是
int,直接接受(极少见,除非手动 cast 过) - 如果是
string:用preg_match('/^-?\d+$/', $val)确保纯数字字符串(含负号) - 如果是
double:用fmod($val, 1) === 0.0 && $val >= PHP_INT_MIN && $val 判断是否为“可无损转 int 的 float” - 转换时统一用
(int)$val,但注意(int)9223372036854775808.0会溢出成PHP_INT_MAX,所以必须先做范围校验
实际处理建议:封装一个 json_decode_intsafe 函数
别每次手写一堆判断。一个轻量封装能收敛所有坑:
function json_decode_intsafe(string $json, bool $assoc = true): mixed {
$decoded = json_decode($json, $assoc, 512, JSON_BIGINT_AS_STRING);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidArgumentException('Invalid JSON: ' . json_last_error_msg());
}
return _convert_numbers_to_int($decoded);
}
function _convert_numbers_to_int(mixed $val): mixed {
if (is_array($val)) {
return array_map('_convert_numbers_to_int', $val);
}
if (is_string($val) && preg_match('/^-?\d+$/', $val)) {
return (int)$val;
}
if (is_float($val) && fmod($val, 1) === 0.0 && $val >= PHP_INT_MIN && $val <= PHP_INT_MAX) {
return (int)$val;
}
return $val;
}
这个函数不改原始 JSON 结构,只对叶子节点数字做安全转换。注意它不处理对象(stdClass),如果用了 $assoc = false,得额外递归处理对象属性。
最容易被忽略的是:PHP 的整型范围依赖编译时配置,PHP_INT_MAX 在不同环境可能不同;而 JSON 标准本身不限制数字大小——这意味着你永远要假设输入可能越界,不能只靠 is_int 或 gettype 做断言。











