json_decode默认不将数字字符串转为数字类型,需用JSON_NUMERIC_CHECK标志;手动转换时应避免intval()等截断函数,优先用filter_var校验并按需转int/float。

PHP json_decode 后数字字符串没变整数?
默认情况下,json_decode 不会把 JSON 里的 "123" 这种数字字符串自动转成整数 —— 它老老实实按 JSON 类型来:双引号包着就是 string。你看到数组里 $arr['id'] === "456"(string),而不是 456(int),不是 bug,是设计如此。
解决办法很简单:传 true 给第二个参数(关联数组模式),再加第三个参数 JSON_NUMERIC_CHECK:
$json = '{"count":"100","price":"29.99","code":"007"}';
$arr = json_decode($json, true, 512, JSON_NUMERIC_CHECK);
// 结果:'count' => 100 (int), 'price' => 29.99 (float), 'code' => '007' (string)
-
JSON_NUMERIC_CHECK只对纯数字字符串生效(如"123"、"-45"、"3.14"),带前导零("007")、科学计数法("1e3")或非数字字符的仍保留为 string - 它不递归处理嵌套结构里的值,只作用于最外层 JSON 的直接字段值
- 如果原始 JSON 里本来就是
123(没引号),那根本不用这个 flag,json_decode自动就是 int/float
用 array_map 批量转数组里所有数字字符串
当你拿到的是已解码的 PHP 数组(比如从数据库或 API 拿到的 array,但里面混着 "1"、"2.5" 这类 string),又不想改上游,就得手动遍历转换。
别用 intval() —— 它会把 "2.5" 截成 2,丢失精度;也别全用 floatval() —— 整数会被变成 1.0,类型不对。
立即学习“PHP免费学习笔记(深入)”;
稳妥做法是先判断是否为纯数字字符串,再决定转 int 还是 float:
$arr = ['a' => '100', 'b' => '3.14', 'c' => 'abc', 'd' => '-42'];
$converted = array_map(function($v) {
if (!is_string($v)) return $v;
if (is_numeric($v)) {
return strpos($v, '.') !== false ? (float)$v : (int)$v;
}
return $v;
}, $arr);
// ['a'=>100, 'b'=>3.14, 'c'=>'abc', 'd'=>-42]
-
is_numeric()能识别"-123"、"0x1A"、"1e2",但注意它也会放行" 123 "(带空格)和"0123"(八进制写法),实际业务中可能得加trim()或正则校验 - 如果数组有嵌套(如多维),
array_map默认不递归,需要自己写递归函数或用array_walk_recursive
filter_var 转单个值更可控
对单个变量做类型转换时,filter_var 比强制类型转换更安全,尤其面对用户输入或不可信数据:
$str = "0042";
$int = filter_var($str, FILTER_VALIDATE_INT);
// 返回 false —— 因为 "0042" 不是合法十进制整数格式
$int = filter_var(ltrim($str, '0'), FILTER_VALIDATE_INT); // 42
$float = filter_var("3.14", FILTER_VALIDATE_FLOAT); // 3.14
-
FILTER_VALIDATE_INT默认拒绝前导零、空格、小数点;加FILTER_FLAG_ALLOW_THOUSAND可支持逗号分隔符(如"1,234") - 它返回
false而不是 0,能明确区分“转换失败”和“值就是 0” - 性能略低于
(int),但换来的是可预测性和错误反馈
为什么 (int) 和 intval() 在这里容易出错
很多人第一反应是 (int)"123",但它在边界场景下行为反直觉:
var_dump((int)"123abc"); // int(123) —— 截断而非报错 var_dump((int)"0x1A"); // int(0) —— 十六进制不识别 var_dump((int)" 42 "); // int(42) —— 会忽略首尾空格 var_dump((int)"007"); // int(7) —— 八进制解析(PHP 8.1+ 已弃用)
- 这种“尽力而为”的转换,在数据校验场景下等于埋雷:本该报错的非法输入,被静默转成了看似合理的值
- 如果你只是做展示或临时计算,
(int)快且够用;但涉及数据库写入、API 输出、条件判断时,优先用filter_var或显式校验 - PHP 8.1+ 对八进制字符串(
"0123")转(int)会触发E_DEPRECATED,这点常被忽略
真正麻烦的不是“怎么转”,而是“什么时候该转、转成什么类型、失败了怎么处理”。数字字符串看着像数字,但它携带的语义(ID?金额?编码?)决定了转换策略 —— ID 必须是 int 且非负,金额必须是 float 或 string(防精度丢失),编码可能是 string(如 "001" 和 "1" 含义不同)。别让类型转换掩盖了业务逻辑的模糊地带。



















