php - 在 PHP 中预增量并定义魔法 get 和 set

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

我有一个问题已经破坏了我想要做事的方式很长时间了。它与在 PHP 中使用 magic 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 所做的引用管理有关。但我希望第三个测试的行为方式与第二个相同,但事实并非如此。

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

最佳答案

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


第一次测试

代码:$o->bar = 2;
输出:旧值:1 - 新值:2

操作,按顺序:

  • $bar赋一个新值(调用__set())

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


第二次测试

代码:$o->bar = $o->bar + 1;
输出:旧值:2 - 新值:3

操作,按顺序:

  • 获取 $bar 的副本(调用 __get())
  • 加一个
  • $bar赋一个新值(调用__set())

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


第三次测试

代码:$o->bar += 1;
输出:旧值:4 - 新值:4

操作,按顺序:

  • 获取对 $bar引用(调用 __get())
  • 加一个
  • $bar赋一个新值(调用__set())

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

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


第四次测试

代码:++$o->bar;
输出:旧值:5 - 新值:5

...在语义上与第三个测试相同。以完全相同的顺序进行完全相同的操作会产生完全相同的输出。同样,有点奇怪的是根本没有任何输出,但是你去吧。


我发明的第五个测试

代码:$o->bar++;
输出:旧值:5 - 新值:6

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


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

  • (不太可能但可能)因为计算 __get() 返回的是引用还是副本比简单调用 __set() 的计算成本更高> - 这似乎不太可能,因为函数调用通常非常昂贵。
  • (更有可能)因为 __set() 函数可以包含与实际赋值操作无关的代码,如果该代码在值被赋值时不运行,可能会带来严重的不便被改变了。例如,如果要实现某种事件驱动架构,当某些值发生变化时会触发事件,如果它只适用于直接赋值,那将很烦人。

关于php - 在 PHP 中预增量并定义魔法 get 和 set,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10520021/

相关文章:

php - 在 Kohana 中,你能触发 404 错误吗?

java - 如何通过反射获取 Hibernate 类的 Getter 和 Setter

ios - 当我重命名属性名称时,Xcode 重构不会重命名 setter 和 getter

当在类中定义魔术方法 __get 和 __set 时,PHP 函数 Empty 不起作用

php、mysql选择

php - 此查询有什么问题?从 mysql 表中加载超过 220,000 条记录的数据需要 0.5 到 0.6 秒

php - 检查属性是否存在

python - 分配(而不是定义) __getitem__ 魔术方法会破坏索引

python - python 的所有内置函数都有神奇的方法吗?

php - 维基私有(private)页面与公共(public)页面