
PHP int 类型没有固定字节数,取决于编译环境
PHP 的 int 不是 C 那种“4 字节”或“8 字节”的硬编码类型,它由底层 Zend 引擎在编译时根据平台决定。32 位系统上通常是 4 字节(INT_MAX = 2147483647),64 位系统上通常是 8 字节(INT_MAX = 9223372036854775807)。但这个“通常”不绝对——比如 Windows 下的 64 位 PHP 很多仍是 4 字节 int,因为用了 MSVC 编译且未启用 /D _USE_64_BITS 等宏。
判断当前环境最可靠的方式不是查文档,而是运行:
var_dump(PHP_INT_SIZE); // 输出 4 或 8 var_dump(PHP_INT_MAX); // 输出对应平台最大值
-
PHP_INT_SIZE是字节数,不是位数 - 别用
sizeof(int)或 C 风格假设——PHP 没这语法 - Docker 容器里跑的 PHP 可能和宿主机不同(例如 alpine php:8.2-cli 默认是 64 位 int,但某些精简镜像可能不是)
超出 PHP_INT_MAX 会自动转成 float,精度丢失不可逆
一旦整数运算结果超过 PHP_INT_MAX,PHP 不报错,而是静默转为 float。而 float 在大数值下无法精确表示所有整数——比如 9223372036854775808(即 PHP_INT_MAX + 1)转成 float 后,再用 (int) 强转回去,大概率变回 PHP_INT_MAX 或其他错误值。
- 常见触发场景:时间戳计算(如
time() + 365*24*3600*100)、ID 累加、分页偏移量乘以每页条数 -
bcadd()、bcmul()能绕过,但要求所有操作数是字符串,且不能直接用于数组键或for循环变量 -
gmp_add()更快更稳,但需启用 GMP 扩展,且返回的是GMP对象,不能直接 echo
用 is_int() 判断类型时,注意 float 假整数陷阱
is_int(123.0) 返回 false,但 is_int(123) 才是 true。问题在于:从 JSON 解析、数据库读取(尤其 MySQL 的 BIGINT)、或经过算术运算后的“看起来像整数”的值,很可能已经是 float 类型了。
立即学习“PHP免费学习笔记(深入)”;
- JSON 中
{"id": 9223372036854775807}在 PHP 7.1+ 会自动转为float(如果超限),哪怕你没做任何运算 -
filter_var($x, FILTER_VALIDATE_INT)比is_int()更实用——它接受字符串输入,并能指定范围(options=['min_range' => 0]) - 数据库字段映射时,PDO 默认把
BIGINT当string返回(需设PDO::ATTR_STRINGIFY_FETCHES = false),否则你拿到的是字符串,is_int()也返回false
不要依赖 int 存储 UUID、雪花 ID 或长数字字符串
像 1521234567890123456 这种 19 位 ID,在 64 位 PHP 上刚好卡在 PHP_INT_MAX 边缘;在 32 位或 Windows 上必然溢出。更麻烦的是,一旦被当 float 处理过一次,后面再 strval() 也可能输出科学计数法(如 1.52123456789012E+18)。
- 正确做法:统一用
string类型接收、存储、传递——数据库字段用VARCHAR(20),API 输入用string类型声明(PHP 8.0+ 可加string $id类型提示) -
json_encode()默认把大整数转成float,要用JSON_BIGINT_AS_STRING选项 - Redis 键名、MQ 消息 ID、缓存 key 都应视为不可计算的标识符,而非数值
真正容易被忽略的,是那些“看起来不会超”的计算——比如两个 int 相乘,或时间戳加一个大秒数,它们在测试数据下完全正常,上线后某天突然变成 float,接着在 switch 分支里掉进 default,或者作为数组键被四舍五入……这时候再查 var_dump(gettype($x)) 就晚了。











