根据PHP版本不同,get_object_vars返回的结果不同。

3

这并不是一个惊喜,因为函数的实现有时会从一个版本到另一个版本有所变化,但是从来没有像这样...看:

$array = ["abc","def"];
$object = new stdclass();
foreach($array as $index => $value) {
    $object->$index = $value;
}
var_dump(get_object_vars($object));

对于5.6.x版本,以及例如7.0.17和7.1.3版本,我们得到以下结果:

array(2) { 
[0]=> string(3) "abc" 
[1]=> string(3) "def" 
}

但是对于7.0.0、7.0.16和7.1.0,我们得到:

array(2) { 
["0"]=> string(3) "abc" 
["1"]=> string(3) "def" 
} 

示例: https://3v4l.org/jog4A

看到了吗?这些键可以是整数或字符串,具体取决于版本。

为什么会这样呢?这些变化背后的原因是什么?为什么没有在任何地方记录这些变化?或者...其实已经记录了?

2个回答

2
如果你仔细查看3v4.org的输出版本,会发现7.0.0至7.0.16以及7.1.0至7.1.2版本存在漏洞。因此,这是在7.0中引入的错误,并且在7.0.17和7.1.3(均于2017年3月16日发布)的两个当前版本中得到修复。

PHP更改日志中可以看到相关条目:

修复了Bug #73998 (array_key_exists无法在get_object_vars创建的数组上运行)。

这让我们想到错误跟踪器,从那里进入git存储库中的提交dd9cf23457e21d2bda29dc92d437b9dbd14027b2

BUG#73998:无法从get_object_vars访问数字属性

修复涉及添加一个检查来检查是否存在数字键,并在有时跳过标记为“fast_copy”的块。
因此,这是PHP 7开发期间进行性能优化的不良副作用,现在已在所有支持的版本中得到修复。
有趣的是,Andrea在错误报告上发表评论,指出它与更改对象到数组转换行为的RFC密切相关,该RFC描述了一般问题。
由于数组和对象与基础的HashTable类型在它们可以拥有的键的种类上有不同的限制,Zend引擎必须在实现数组和对象本身的代码中强制执行它们的限制,这意味着如果绕过该代码并直接修改底层的HashTable,则可能存在具有无效内部状态的数组和对象。
RFC所解决的具体情况是:
例如,$arr = [0 => 1, 1 => 2, 2 => 3]; $obj = (object)$arr;会生成一个具有无法访问的属性名为“0”,“1”和“2”的对象,而$obj = new stdClass; $obj->{'0'} = 1; $obj->{'1'} = 2; $obj->{'2'} = 3;则会生成一个具有无法访问的键“0”,“1”和“2”的数组。当使用get_object_vars()时,也会出现相同的问题。
RFC在7.2.0中实现,因为它改变了文档记录的行为,但是get_object_vars()的行为在7.0中实际上是无意中发生的更改,因此被实现为bug修复。

谢谢!我相信这就是答案 :) - konrados

0

实际上这是一个 bug,因为变量名必须以字母开头。

变量名遵循 PHP 中其他标签的规则。有效的变量名以字母或下划线开头,后面可以跟任意数量的字母、数字或下划线。正则表达式表示如下:'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'

http://php.net/manual/en/language.variables.basics.php

因此,当您尝试设置变量$object->$index = $value;时,它必须抛出错误(您不能像这样设置变量$1 = 'foo';$obj->1 = 'foo';)。

array(2) { 
["0"]=> string(3) "abc" 
["1"]=> string(3) "def" 
} 

这是正确的结果,因为get_object_vars返回关联数组。

返回值 ¶

返回指定对象在作用域中定义的可访问非静态属性的关联数组。如果属性没有被赋值,则将返回一个NULL值。

http://php.net/manual/en/function.get-object-vars.php


1
那么你的意思是PHP7有bug吗? 根据get-object-vars链接,在PHP5.6中它应该返回字符串键。@Neodan,你能再澄清一下吗? - Naincy
@Neodan 谢谢,但是...实际上你可以创建一个以数字开头的属性:$x->{'1'} = 23;https://3v4l.org/Je7Jl另外,我支持Naincy的问题:) - konrados
1
关联数组具有命名键,这意味着键必须是字符串。但我认为问题不在于键数据类型,而在于您可以创建违反变量命名规则的变量。 - Neodan
2
变量名和属性名不是同一回事,因此这不是手册中需要查看的相关部分。 - IMSoP
1
@IMSoP 但你不能像这样声明一个属性 public $1 = 'foo'; :) - Neodan
2
当然可以,就像 OP 的代码所示,你可以创建一个。虽然这不是一个好主意,但实际上并没有被禁止。还有其他一些属性也可以访问但不能声明,例如 $foo->{'some-hypenated-phrase'}。这在 JSON 或 XML 解析器中使用,以避免在源数据中破坏名称。 - IMSoP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接