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

3番目と4番目のテストには、予期しない(私による)動作があります。$ this-> object ['bar']は、予想どおりの古い値ではなく、設定する値を返すように見えます。実際に設定される前に、なぜ値がすでに設定されているのですか?

&メソッドからを削除する__getと、これは機能するので、PHPが行う参照管理と関係があると思います。しかし、3番目のテストは2番目のテストと同じように動作することを期待しますが、そうではありません。

私は本当にこれを理解していません。どんな助けでも大歓迎です。

4

1 に答える 1

6

結果は実際(慎重に検討した後)私が期待するものです。


最初のテスト

コード:$o->bar = 2;
出力:Old value: 1 - New value: 2

操作は、順番に:

  • $bar(call __set())に新しい値を割り当てます

明らかに、これは単に古い値を放棄し、その場所に新しい値を置きます。複雑なことは何もありません。


2番目のテスト

コード:$o->bar = $o->bar + 1;
出力:Old value: 2 - New value: 3

操作は、順番に:

  • (call )のコピーを 取得する$bar__get()
  • それに1つ追加します
  • $bar(call __set())に新しい値を割り当てます

式の右側が評価され、結果が左側に割り当てられます。$bar右側の評価中、インスタンス変数の値は変更されないため、__set()呼び出しの開始時に古い値であることがわかります。


3番目のテスト

コード:$o->bar += 1;
出力:Old value: 4 - New value: 4

操作は、順番に:

  • (call )への参照を 取得します$bar__get()
  • それに1つ追加します
  • $bar(call __set())に新しい値を割り当てます

これは興味深いことです。一見する__set()と、この操作で生成された出力が表示されることに少し驚いています。最初の2つの場合のように、まったく新しい値(したがって、まったく新しいzval)を割り当てずに、元のzvalに保持されている値をインクリメントしているため、__set()呼び出しは冗長であるように見えます。

ただし、が呼び出されると、参照される値が呼び出されるにインクリメントされている__set()ため、表示される出力は期待どおりになります。渡される値はインクリメント操作の結果であるため、別の値が追加されることはなく、単にtoの値が割り当てられるため、古い値と新しい値が同じであることがわかります。 __set()__set()$foo->bar$foo->bar


4番目のテスト

コード:++$o->bar;
出力:Old value: 5 - New value: 5

...3番目のテストと意味的に同じです。まったく同じ操作をまったく同じ順序で実行すると、まったく同じ出力になります。繰り返しになりますが、出力がまったくないのは少し奇妙ですが、そこに行きます。


私が発明した5番目のテスト

コード:$o->bar++;
出力:Old value: 5 - New value: 6

...2番目のテストと意味的に同じです。$i++これも興味深いもので、少し予想外ですが、の古い値を返すことを考えると、それも理にかなっています$i


推測で__set()は、3番目と4番目のテストで不必要に呼び出されている理由は次のとおりです。

  • (可能性は低いですが可能です)__get()単純に呼び出すよりも、参照またはコピーを返すかどうかを判断する方が計算コストが高くなる__set()ためです。関数呼び出しは一般に非常に高価であるため、これはありそうにないようです。
  • (より可能性が高い)__set()関数には実際の代入操作とは関係のないコードが含まれている可能性があり、値が変更されたときにこのコードが実行されなかった場合、深刻な不便が生じる可能性があります。たとえば、特定の値が変更されたときにイベントを発生させるある種のイベント駆動型アーキテクチャを実装する場合、それが直接の割り当てでのみ機能するのは面倒です。
于 2012-05-09T16:49:46.797 に答える