php从mysql查询的数字字段默认为字符串,根本原因是pdo和mysqli驱动未启用原生类型解析:pdo默认开启pdo::attr_emulate_prepares=true,mysqli默认使用文本协议,均导致数值以字符串形式返回;解决需在连接初期设置pdo::attr_emulate_prepares=false与pdo::attr_stringify_fetches=false(pdo)或mysqli_options($link, mysqli_opt_int_and_float_native, 1)(mysqli)。

PHP 从 MySQL 查询出来的数字字段(比如 INT、BIGINT)默认是字符串,根本原因在于 PDO 或 MySQLi 驱动层的默认行为——它没启用数据库原生类型解析,而是把所有字段都当文本读取了。
为什么 PDO 默认把数字当字符串返回
因为 PDO 默认开启 PDO::ATTR_EMULATE_PREPARES = true,也就是“模拟预处理”。这时 SQL 是 PHP 拼好后发给 MySQL 的,MySQL 返回的是纯文本结果包,PDO 没机会读取字段的原始类型元数据,只能统一转成 string。连 PDO::ATTR_STRINGIFY_FETCHES 都不用设,它本身就生效。
- 即使你用的是
prepare()->execute(),只要没关模拟预处理,结果就是字符串 -
TINYINT(1)常被误认为布尔,但实际返回仍是"1"或"0"字符串 -
BIGINT超过 PHPint范围(如 64 位 ID)时,不关模拟预处理就必然丢精度,因为字符串转整数会溢出
mysqli_fetch_assoc() 为啥也返回字符串
MySQLi 的行为类似:除非显式调用 mysqli_fetch_field_direct() 查元数据再手动转换,否则 mysqli_fetch_assoc() 和 mysqli_fetch_array() 都返回字符串。这是 C 客户端库的默认文本协议行为,不是 bug。
- 即使字段定义为
INT NOT NULL,$row['id']的类型仍是string -
mysqli_options($link, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1)可以开启原生类型,但仅在 MySQLi 使用 mysqlnd 驱动且 PHP ≥ 5.3 时有效 - 该选项对
NULL值无效,NULL字段仍返回NULL(正确),但数值字段不会自动转int,需配合fetch_fields()+ 手动 cast
怎么让数字真变成 int/float 类型
最可靠的方式是关掉模拟预处理,并禁用字符串化 fetch:
- 对 PDO:连接后立刻执行
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); - 对 MySQLi(mysqlnd):连接后调用
mysqli_options($mysqli, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); - 注意:
ATTR_EMULATE_PREPARES = false要求 MySQL 服务端支持原生预处理(MySQL 5.1+ 基本都支持),否则 prepare 会失败
不改驱动配置时的临时补救方法
如果无法修改连接配置(比如用的是封装好的 DB 类、或共享主机不允许多数设置),只能在 fetch 后手动 cast:
- 简单字段可用
(int)$row['id']或filter_var($row['score'], FILTER_VALIDATE_INT) - 注意
(int)"123abc"会得123,有风险;更安全的是先is_numeric()判断再 cast - 批量处理建议用
array_map():array_map(fn($v) => (int)$v, $rows['id'] ?? []) - JSON 输出前强制字符串化关键字段(如订单号)反而更稳妥:
'order_no' => (string)$row['order_no'],避免前端解析成数字丢失前导零
真正要命的不是“为什么是字符串”,而是你以为它是整数、直接参与计算或比较,结果在边界值(比如 0 vs "0")、大整数或 JSON 序列化时突然翻车。驱动层配置必须在连接建立初期就设好,后面再 setAttribute 就晚了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











