class Test
{
public $prop1;
public function __get(string $n)
{
echo '__GET: '.$n.' - but why?<br>';
die;
}
}
$t = new Test();
$x1 = new \stdClass();
$t->prop2 = &$x1;
echo '.';
die;
https://onlinephp.io/c/f7f16
在这里,您可以看到,我创建了一个stdClass
对象,尝试将其传递给一个不存在的变量,然后__get()
被执行-即使我只是尝试写入它,而不是阅读它。如果我不执行die()
,我会得到Indirect modification of overloaded property has no effect
异常。
如果我有这个(无参考)
class Test
{
public $prop1;
public function __get(string $n)
{
echo '__GET: '.$n.' - but why?<br>';
die;
}
}
$t = new Test();
$x1 = new \stdClass();
$t->prop2 = $x1;
echo '.';
die;
https://onlinephp.io/c/3afef
一切正常,但为什么
4条答案
按热度按时间nzrxty8p1#
在内部,PHP有一个“阅读”属性的概念来执行写/修改操作。虽然乍看之下似乎不连贯,但这允许PHP以更少的开销执行某些就地修改操作。否则必须依赖于读取-修改-写入的往返过程。使用它的情况包括追加到数组和创建对属性的新引用。为了使它能够与动态解析的属性一起工作,
__get
魔术方法必须返回一个引用,也就是说,一个可以修改其值的位置;否则将触发一个通知,并显示消息“间接修改重载属性无效”。如果从
function& __get
的声明中删除&
,则会在上面的示例中得到此消息,这将使该方法返回值,而不是引用。当对象属性位于引用赋值的 * 左侧 * 时,将触发相同的代码路径“reading-in-order-to-modify当然,如果属性查找是由用户代码动态执行的(PHP错误地命名了“overloading”),PHP无法保证“在执行
$obj->prop =& (expr)
之后,访问$obj->prop
将解析为与(expr)
当时所做的相同的事情',因此PHP抛出异常。然而,解释器只有在调用了__get
魔术方法之后才意识到这一点。通过查看Zend引擎的源代码可以看出这一点。要计算
$obj->prop =& expr
这样的表达式,Zend引擎调用函数zend_assign_to_property_reference
。此函数调用zend_fetch_property_address
以确定哪个zval(PHP值的内部表示),以便它成为右侧表达式的同一个zval的别名。它通过调用对象的get_property_ptr_ptr
内部方法槽来实现;如果没有返回任何值,则返回到read_property
插槽,对于用户代码对象,这意味着调用zend_std_read_property
函数,这是实际调用__get
魔术方法的地方。同样,由于
zend_std_read_property
是用标志调用的,这些标志表示它正在“按顺序读入以修改”,因此如果__get
碰巧没有返回引用(因为它没有被声明为function& __get
),则会引发E_NOTICE
错误。但是无论哪种方式,zend_std_read_property
都将其结果返回到由zend_fetch_property_address
为其准备的zval中,而不是预先存在的zval中,这导致后者 not 将其 Package 在所谓的间接Zval中,如zend_assign_to_property_reference
所期望的,这导致该函数进而抛出异常。如果你问我的话,这是一个相当巴洛克式的实现。我甚至同意Zend引擎可以这样实现,即在
__get
即将被调用时,解释器意识到调用它实际上是毫无意义的,并立即抛出异常。但考虑到只有在不正确的用户代码中才会遇到这个代码路径,应该重写,也许优化它没有什么意义。如果你用PHP列出了一些毫无意义的东西,我不确定这是否能排在前十位;语言就是这么奇怪。习惯它。或者不习惯:改变语言也是一种选择。
brqmpdu12#
当你通过reference 1赋值时,左手变量需要被定义(它存在),如果没有(它未定义),PHP将定义它。
但在
$t->prop2
情况下则不同:变量的成员名(
$t->prop2
)是未定义的,但是$t
的__get()
2魔术方法指示PHP不要像通常那样定义它,而是将成员名作为第一个参数传递给__get()
,以获得其重载值。这是有缺陷的,因为通过引用赋值 * 需要 * 定义变量(或属性)才能成功操作,但undefined(重载)不能满足这一要求。
因此,警告:
重载属性stdClass@anonymous::$prop2的间接修改无效
最后是致命错误:
无法按引用分配给重载对象
示例代码3,4
1.按引用分配- PHP手册
1.属性重载- PHP Manual
rta7y2nd3#
这里有很多复杂的解释,这里有一个简单的答案,为什么它调用
__get()
,以上面的简单示例为例,为了能够将
$obj->var
设置为$test
所指向的内存地址,$obj-〉var需要$obj中的内存地址,如果没有__get
,PHP将根据上面的代码自行创建一个内存地址,如果您使用的__get
PHP不需要并且需要您定义它的话。您可以进行定义的唯一位置是在__get
中wbgh16ku4#
根据文件:
__get()
用于从不可访问(受保护或私有)或不存在的属性读取数据。如果您想通过引用使用
__get
,下面是您应该编写内容: