PHP:JSON解码对象转数组后出现的异常数组行为
这个现象确实是PHP 7.1及更早版本的一个bug,根源在于stdClass对象转数组时的键名处理逻辑缺陷,PHP 7.2及以上已经修复了这个问题,下面详细拆解原因:
1. 为什么json_decode转对象再转数组会出现无法访问的键?
当你用json_decode($assoc=false)解析JSON字符串时,得到的是一个stdClass对象。JSON里的数字字符串键(比如"7")会变成这个对象的属性名,但这类纯数字的属性名不符合PHP的标识符规则(PHP属性名不能以数字开头,除非通过特殊语法访问)。
在PHP 7.1及更早版本中,将这种对象强制转为数组时,PHP会对这些"不合法"的属性名做特殊处理:给键名添加不可见的空字节前缀和后缀,格式为"\0*\0属性名"(这里的*代表任意类,因为stdClass是通用对象)。
这就导致你看到的数组键看起来是'7',但实际是包含空字节的特殊字符串——所以无论是用$a[7](整数键)还是$a['7'](普通字符串键)都无法匹配到实际的键,自然返回false。
你可以通过打印数组键的实际内容验证这一点:
$a = (array)json_decode('{"7":"value1"}'); var_dump(array_keys($a)); // PHP 7.1及更早版本输出:array(1) { [0]=> string(3) "\0*\07" }
2. 手动创建的数组为什么没有这个问题?
当你手动创建数组array('7' => 'value1')时,PHP会自动将纯数字的字符串键转换为整数键——因为PHP数组的键如果是可以被解析为整数的字符串,会被自动转为整数类型。
此时数组的实际键是整数7,PHP在访问数组时,会自动将字符串形式的数字键(比如'7')转换为整数键去匹配,所以无论是$c[7]还是$c['7']都能正常访问到值。
3. PHP 7.2+的修复逻辑
PHP官方在7.2版本中优化了对象到数组的转换逻辑,针对stdClass对象的纯数字属性名,转数组时会直接将其转换为整数键,和手动创建数组的行为保持一致。所以在7.2及以上版本中,你测试的代码会返回预期的true true。
虽然这个修复没有在更新日志中单独突出提及,但属于对象转数组时键名处理的bug修复范畴。
内容的提问来源于stack exchange,提问作者Gino Pane

