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

时间:2012-05-09 16:15:28

标签: php getter-setter magic-methods pre-increment

我有一个问题已经破坏了我想要做很长时间的事情。它与在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;
    }
}

请注意&方法中的__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;

输出是:

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

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

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

我真的不明白这一点。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:6)

事实上(仔细考虑之后)结果是我所期望的。


首次测试

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

按顺序执行操作:

  • $bar(致电__set()
  • 分配新值

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


第二次测试

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

按顺序执行操作:

  • 获取的副本<{em} $bar(致电__get()
  • 添加一个
  • $bar(致电__set()
  • 分配新值

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


第三次测试

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

按顺序执行操作:

  • 获取 $bar的引用(致电__get()
  • 添加一个
  • $bar(致电__set()
  • 分配新值

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

然而,当调用__set()时,您看到的输出就是您所期望的,因为引用值 {{1>之前已经增加 }} 被称为。传递给__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()函数可以包含与实际赋值操作无关的代码,如果在更改值时此代码未运行,则可能会非常不方便。例如,如果要实现某种事件驱动的体系结构,当某些值被更改时触发事件,那么如果只能使用直接赋值,则会很烦人。