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->testme()
#1 {main}
thrown in <b>[...][...]</b> on line <b>15</b><br />
但是注释掉该行似乎可以解决问题,那么原因是什么?
真题是:
"Indirect modification of overloaded property"发生在哪里?
"Cannot assign by reference to overloaded object" - 为什么只有在 unset
之后?
- 为什么要调用 getter?虽然不应该。
见http://php.net/manual/en/language.oop5.overloading.php
和 http://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。
所以回答你的问题:
- 在 __get() 方法中,如果我没记错的话。
- 只有在你使用 unset() 之后,因为 private $a 是在你的 class 顶部定义的,直到你真正取消设置它。
- 应该调用它,因为如果您的 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
从 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'));
?>
最后一个问题很有意思,通过搜索得到以下信息,希望对你有所帮助。
- 取消设置后属性,该属性将不存在,所以"$this->a"为"overloading"。
arguments of these magic methods can be passed by reference. PHP Manual.
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.
任何了解 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->testme()
#1 {main}
thrown in <b>[...][...]</b> on line <b>15</b><br />
但是注释掉该行似乎可以解决问题,那么原因是什么? 真题是:
"Indirect modification of overloaded property"发生在哪里?
"Cannot assign by reference to overloaded object" - 为什么只有在
unset
之后?- 为什么要调用 getter?虽然不应该。
见http://php.net/manual/en/language.oop5.overloading.php 和 http://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。
所以回答你的问题:
- 在 __get() 方法中,如果我没记错的话。
- 只有在你使用 unset() 之后,因为 private $a 是在你的 class 顶部定义的,直到你真正取消设置它。
- 应该调用它,因为如果您的 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
从 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'));
?>
最后一个问题很有意思,通过搜索得到以下信息,希望对你有所帮助。
- 取消设置后属性,该属性将不存在,所以"$this->a"为"overloading"。
arguments of these magic methods can be passed by reference. PHP Manual.
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.