PHP 代码中发生了什么样的魔法?

What kind of magic does happen in this PHP code?

任何了解 php 内部结构的人能否解释一下这种 PHP 行为?

<?php

class Test {

     private $a;

     public function __get($name) {
       echo('GETTER CALLED!');
       return null;
     }

     public function testme() {
       $v = array(1, 2, 3);
       unset($this->a); // <-- this line *
       $this->a = &$v;
     }

 }

 $test = new Test();
 $test->testme();

unset($this->a) 在这里(即未注释掉)时 php 导致以下输出错误:

GETTER CALLED!<br />
<b>Notice</b>:  Indirect modification of overloaded property Test::$a has no effect in <b>[...][...]</b> on line <b>15</b><br />
<br />
<b>Fatal error</b>:  Uncaught Error: Cannot assign by reference to overloaded object in [...][...]:15
Stack trace:
#0 [...][...](21): Test-&gt;testme()
#1 {main}
  thrown in <b>[...][...]</b> on line <b>15</b><br />

但是注释掉该行似乎可以解决问题,那么原因是什么? 真题是:

  1. "Indirect modification of overloaded property"发生在哪里?

  2. "Cannot assign by reference to overloaded object" - 为什么只有在 unset 之后?

  3. 为什么要调用 getter?虽然不应该。

http://php.net/manual/en/language.oop5.overloading.phphttp://php.net/manual/en/language.oop5.magic.php

Overloading in PHP provides means to dynamically "create" properties and methods. These dynamic entities are processed via magic methods one can establish in a class for various action types.

当你 unset() 私有变量 $a 时,就好像它从未存在过,就好像你从未在 class 的开头设置 "private $a;" 一样。在第 16 行,PHP 注意到 $this->a 没有设置,魔术开始使用 __get 方法。您开始使用 "magically" 创建的重载 属性 $this->a。

所以回答你的问题:

  1. 在 __get() 方法中,如果我没记错的话。
  2. 只有在你使用 unset() 之后,因为 private $a 是在你的 class 顶部定义的,直到你真正取消设置它。
  3. 应该调用它,因为如果您的 class 属性 未设置并且您设置的是 PHP 调用 __get() 的正常行为参考。您将使用下面的简化示例获得相同的结果:
<?php

class Test {
    public function __get($name) {
        echo('GETTER CALLED!');
        return null;
    }

    public function testme() {
        $v = array(1, 2, 3);
        $this->a = &$v;
    }
}

$test = new Test();
$test->testme();

魔术方法的参数__get不能通过引用传递。

更简单的代码:

class Test {
     public function __get($name) {
       return null;
     }
 }

 $test = new Test();
 $a=1;
 $test->a=&$a; # <-- Cannot assign by reference to overloaded object

参见:http://php.net/manual/en/language.oop5.overloading.php

从 DoPPeS 的解释来看,我只做了很少的改动。我将数组的创建移到了 class 之外,并添加了一个 getter 只是为了验证引用赋值是否有效。

简单的修复是,根据 DoPPeS 所说,它必须重新创建 属性,但不能直接分配引用,首先使用虚拟值创建 属性,它现在存在并且可以通过引用分配(可能有更优雅的解决方案,但它有效):

<?php

class Test {

    private $a;

    public function __get($name) {
        echo('GETTER CALLED!');
        return null;
    }

    public function testme(&$val) {
        unset($this->a);
        $this->a = 'test'; // <-- creates the property
        $this->a = &$val; // <-- now reference works
    }

    public function getVal($name) {
        if(!isset($this->{$name})){
            return NULL;
        }
        return $this->{$name};
    }

}

$v = array(1, 2, 3);
$test = new Test();
$test->testme($v);
$v[] = 4;
var_dump($test->getVal('a'));

?>

最后一个问题很有意思,通过搜索得到以下信息,希望对你有所帮助。

  1. 取消设置后属性,该属性将不存在,所以"$this->a"为"overloading"。
  2. arguments of these magic methods can be passed by reference. PHP Manual.

  3. This functionality initially looked like a bug to me, but it turned out to be expected behaviour after asking around for quite a bit. Regression/unset properties behavior.