Joh*_*ith 7 php reference getter-setter
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;
在这里您可以看到,我创建了一个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;
一切正常,但为什么呢?
在内部,PHP 有一个 \xe2\x80\x98reading\xe2\x80\x99 属性的概念,以便执行写入/修改操作。尽管乍一看似乎不连贯,但这允许 PHP 以更少的开销执行某些就地修改操作,否则就必须依赖读取-修改-写入往返。使用它的情况包括附加到数组和创建对属性的新引用。为了使其能够与动态解析的属性一起使用,__get魔术方法必须返回一个引用,也就是说,一个可以修改其值的地方;否则将触发一条通知,消息为\xe2\x80\x98间接修改重载属性无效\xe2\x80\x99。
class Globalses {\n    public function& __get(string $name) {\n        echo "{$name} GET!\\n";\n        return $GLOBALS[$name];\n    }\n}\n\n$obj = new Globalses;\n\n$foo = [0, 1, 2, 3];\n$obj->foo[] = 4;     // foo GET!\necho \'foo=\'; debug_zval_dump($foo);\n\n$bar = 42;\n$var =& $obj->bar;   // bar GET!\n$var = 69;\necho \'bar=\'; debug_zval_dump($bar);\n&如果您从声明中删除,您将在上面的示例中收到此消息function& __get,这将使该方法返回一个值,而不是引用。
当对象属性位于引用赋值 ( ) 的左侧时,会触发 \xe2\x80\x98reading-in-order-to-modify\xe2\x80\x99 的相同代码路径=&。但是,当然,如果属性查找是由用户代码动态执行的(PHP 错误命名 \xe2\x80\x98overloading\xe2\x80\x99),PHP 无法保证 \xe2\x80\x98 执行后$obj->prop =& (expr),访问$obj->prop将解析为(expr)当时执行的相同操作\xe2\x80\x99,因此 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调用时带有表示它正在 \xe2\x80\x98read-in-order-to-modify\xe2\x80\x99 的标志,所以如果__get碰巧没有返回引用(因为未声明function& __get),则会出现E_NOTICE错误被提出。但无论哪种方式,zend_std_read_property都会在为其准备的 zval 中返回其结果,而zend_fetch_property_address不是预先存在的 zval,这会导致后者不会将其包装在所谓的间接 zval中,如zend_assign_to_property_reference中,这会导致该函数依次抛出例外。
如果你问我的话,这是一个非常巴洛克式的实现。我什至同意 Zend 引擎本来可以被实现,所以到了这一点__get被调用时,解释器意识到它\xe2\x80\x99实际上调用起来毫无意义,并立即抛出异常。但考虑到此代码路径仅在不正确且应重写的用户代码中命中,因此优化可能没有什么意义。
如果你编译了 PHP 中没有意义的东西的列表,我\xe2\x80\x99m 不确定这是否会进入前十名;这种语言就是那么奇怪。习惯它。或者 don\xe2\x80\x99t:更改语言也是一种选择。
\n| 归档时间: | 
 | 
| 查看次数: | 750 次 | 
| 最近记录: |