我有一个问题已经破坏了我想要做很长时间的事情。它与在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所做的引用管理有关。但我希望第三次测试的行为方式与第二次测试相同,但事实并非如此。
我真的不明白这一点。任何帮助将不胜感激。
答案 0 :(得分: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()
时,您看到的输出就是您所期望的,因为引用值在 {{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()
函数可以包含与实际赋值操作无关的代码,如果在更改值时此代码未运行,则可能会非常不方便。例如,如果要实现某种事件驱动的体系结构,当某些值被更改时触发事件,那么如果只能使用直接赋值,则会很烦人。