php整型溢出时intval()和(int)均静默截断为php_int_max或php_int_min;json解析大数默认转float致精度丢失,应启用json_bigint_as_string;超大数运算须用gmp或bcmath扩展。

PHP整型溢出时 intval() 和强制转换 (int) 都会静默截断
64位系统下,PHP的int是带符号64位整数,理论范围是 -9223372036854775808 到 9223372036854775807。但问题不在“系统支持多少”,而在于你传进去的值是否**已经超出这个范围**——比如从JSON、数据库或HTTP参数里拿到一个超大数字字符串,直接(int)一转,就变成9223372036854775807(正向溢出)或-9223372036854775808(负向溢出),且不报错、不警告。
常见错误现象:json_decode('{"id":"12345678901234567890123"}', true) 后取 $data['id'] + 0 或 (int)$data['id'],结果恒为 9223372036854775807;用filter_var($str, FILTER_VALIDATE_INT)也会返回false,但它不告诉你为什么失败。
- 判断是否溢出:先用
is_string($val)且ctype_digit(ltrim($val, '-'))确认是纯数字字符串,再用gmp_init($val)或bcadd($val, '0')做无损加载 - 若必须转
int,先用filter_var($val, FILTER_VALIDATE_INT, ['options' => ['min_range' => PHP_INT_MIN, 'max_range' => PHP_INT_MAX]])校验范围 -
intval()在64位PHP中仍受PHP_INT_MAX限制,不是“能读多大读多大”
JSON解析大整数时默认转成浮点,精度直接丢
PHP的json_decode()默认把超长数字当float处理,而float在64位系统最多精确表示2^53 - 1(即9007199254740991)以内的整数。超过就四舍五入或变零——这不是溢出,是精度坍塌。
使用场景:API返回订单号、雪花ID、区块链地址等16位以上纯数字字符串,后端用json_decode($json)直接取值,前端看到ID对不上。
立即学习“PHP免费学习笔记(深入)”;
- 解决方案:始终开启
JSON_BIGINT_AS_STRING标志,例如json_decode($json, true, 512, JSON_BIGINT_AS_STRING) - 这样所有大数字都会作为
string保留,后续按需用gmp_init()或bcadd()计算 - 注意:Laravel的
request()->json()、Symfony的Json::decode()默认不带这个标志,得手动传参或改配置
需要算大整数?别硬扛,用 gmp 或 bcmath
PHP原生int撑不住,就别在int上打转。硬写$a + $b之前,先看变量类型——如果其中一个是字符串形式的大数,加法结果大概率错。
性能与兼容性影响:gmp更快更准,但要求扩展启用;bcmath内置率高,函数名更直白(如bcadd()),但所有操作都得显式传小数位数,且不支持位运算。
- 检查是否可用:
extension_loaded('gmp')或function_exists('gmp_add') - 简单加法示例:
gmp_strval(gmp_add('9223372036854775807', '1'))→'9223372036854775808' - 用
bcmath时别漏scale参数:bcadd('12345678901234567890', '98765432109876543210', 0),最后的0表示不要小数位
数据库字段类型和PHP交互时的隐式截断
MySQL的BIGINT UNSIGNED最大到18446744073709551615,远超PHP有符号int上限。PDO默认把BIGINT映射成int,一取出来就溢出变负数或固定最大值。
容易踩的坑:用PDO::FETCH_ASSOC查出记录,直接echo $row['id']看起来正常,但一参与计算或比较就出错;mysqli_fetch_assoc()同理。
- PDO方案:创建时设
PDO::ATTR_STRINGIFY_FETCHES => true,让所有数字字段返回string - mysqli方案:用
mysqli_options($link, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, false)禁用原生类型转换 - ORM如Eloquent,可在模型里加
protected $casts = ['id' => 'string']强制转字符串
真正麻烦的不是“怎么修”,而是那些没做类型校验、又混用了字符串ID和整数ID的旧逻辑——它们在线上跑着,直到某天某个ID刚好跨过PHP_INT_MAX才突然崩掉。











