5

我有一个问题一直在破坏我想做的事情的方式很长一段时间。它与在 PHP 中使用魔法 get 和 set 以及尝试对对象进行预增量有关。我有一个 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 所做的引用管理有关。但我希望第三个测试的行为与第二个相同,但事实并非如此。

我真的不明白这个。任何帮助将非常感激。

4

1 回答 1

6

结果实际上(经过仔细考虑)是我所期望的。


第一次测试

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

操作,按顺序:

  • $bar为(call __set())分配一个新值

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


第二次测试

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

操作,按顺序:

  • 获取(致电)的副本 $bar__get()
  • 给它加一个
  • $bar为(call __set())分配一个新值

计算表达式的右侧,并将结果分配给左侧。实例变量的值$bar在右侧评估期间保持不变,因此您在__set()调用开始时会看到它的旧值。


第三次测试

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

操作,按顺序:

  • 获取对(call )的引用 $bar__get()
  • 给它加一个
  • $bar为(call __set())分配一个新值

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

但是,当__set()被调用时,您确实看到的输出是您所期望的,因为引用的值在被调用之前 已经增加了__set()。传递给的值__set()是递增操作的结果,所以它不会导致另一个被添加到它,它只是分配$foo->barto的值$foo->bar- 所以你看到旧值和新值是相同的。


第四次测试

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

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


我发明的第五个测试

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

...语义上与第二个测试相同。这又很有趣,也有点出乎意料,但它也很有意义,因为它$i++返回了 的旧值$i


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

  • (不太可能但可能)因为计算__get()返回引用还是副本比简单地调用更昂贵__set()- 这似乎不太可能,因为函数调用通常非常昂贵。
  • (更有可能)因为该__set()函数可以包含与实际赋值操作无关的代码,并且如果在更改值时该代码没有运行,则可能会带来严重的不便。例如,如果要实现某种事件驱动的架构,当某些值被改变时触发事件,如果只使用直接赋值,那就太烦人了。
于 2012-05-09T16:49:46.797 回答