PHP中的预增量,定义了魔法get和set

mak*_*nty 5 php magic-methods pre-increment getter-setter

我有一个问题,一直在破坏我想要做的事情很长一段时间.它与在PHP中使用魔法获取和设置以及尝试对对象进行预增量有关.我有一个类如下的PHP类:

class Foo {
    public $object;

    function __construct() {
            $this->object = array("bar" => 1);
    }

    function & __get($name) {
            return $this->object[$name];
    }

    function __set($name, $value) {
            echo "Old value: ". $this->object[$name] ." - New value: ". $value ."\n";
            $this->object[$name] = $value;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意&__get方法.现在我运行这段代码:

$o = new Foo();

echo "First test\n";
$o->bar = 2;

echo "Second test\n";
$o->bar = $o->bar + 1;

echo "Third test\n";
$o->bar += 1;

echo "Fourth test\n";
++$o->bar;
Run Code Online (Sandbox Code Playgroud)

输出是:

First test
Old value: 1 - New value: 2
Second test
Old value: 2 - New value: 3
Third test
Old value: 4 - New value: 4
Fourth test
Old value: 5 - New value: 5
Run Code Online (Sandbox Code Playgroud)

第三和第四次测试有一个意想不到的(由我)行为.看起来像$ this-> object ['bar']返回要设置的值,而不是我期望的旧值.为什么价值在实际设定之前就已经设定好了?

如果我&__get方法中删除它,这将工作,所以我想它与PHP的引用管理有关.但我希望第三次测试的行为方式与第二次测试相同,但事实并非如此.

我真的不明白这一点.任何帮助将非常感激.

Dav*_*dom 6

结果实际上(仔细考虑之后)我会期待什么.


第一次测试

代码:$o->bar = 2;
输出:Old value: 1 - New value: 2

按顺序执行操作:

  • $bar(调用__set())分配新值

显然,这只是放弃旧的价值,并在其位置上放置一个新的价值.没什么复杂的.


第二次测试

代码:$o->bar = $o->bar + 1;
输出:Old value: 2 - New value: 3

按顺序执行操作:

  • 得到(电话)的副本 $bar__get()
  • 添加一个
  • $bar(调用__set())分配新值

计算表达式的右侧,并将结果分配给左侧.$bar在评估右侧时,实例变量的值不受影响,因此您可以在__set()调用开始时看到它的旧值.


第三次测试

代码:$o->bar += 1;
输出:Old value: 4 - New value: 4

按顺序执行操作:

  • 获取参考 $bar(通话__get())
  • 添加一个
  • $bar(调用__set())分配新值

这很有趣 - 乍一看,我看到通过__set()此操作生成的输出有点惊讶.因为你正在递增原始zval中保存的值,而不是像前两个那样分配一个全新的值 - 因此是一个全新的zval,看起来这个__set()调用是多余的.

但是,当__set()调用时,您看到的输出就是您所期望的,因为在调用之前引用的值已经增加.传递给的值是递增操作的结果,因此它不会导致向其添加另一个值,它只是将值赋值为- 所以您将旧值和新值视为相同. __set()__set()$foo->bar$foo->bar


第四次测试

代码:++$o->bar;
输出:Old value: 5 - New value: 5

...在语义上与第三次测试相同.完全相同的操作以完全相同的顺序产生完全相同的输出.再一次,有点奇怪,根本没有任何输出,但你去了.


我发明的第五次测试

代码:$o->bar++;
输出:Old value: 5 - New value: 6

...在语义上与第二次测试相同.这又是有趣的,有点意外,但它也有意义,因为它$i++返回旧的值$i.


猜测__set()在第三和第四次测试中被不必要地调用的原因是:

  • (不太可能,但可能)因为计算__get()返回引用或副本而不是简单地调用会更加计算成本__set()- 这似乎不太可能,因为函数调用通常非常昂贵.
  • (更有可能)因为该__set()函数可以包含与实际赋值操作无关的代码,如果在更改值时此代码未运行,则可能会非常不方便.例如,如果要实现某种事件驱动的体系结构,当某些值被更改时触发事件,那么如果只能使用直接赋值,则会很烦人.