89

私が完全に間違っていない限り、 メソッド__get__setメソッドは →getとのオーバーロードを許可するはずsetです。

たとえば、次のステートメントは__getメソッドを呼び出す必要があります。

echo $foo->bar;
$var = $foo->bar;

そして、以下は__setメソッドを使用する必要があります:

$foo->bar = 'test';

これは私のコードでは機能していませんでしたが、次の簡単な例で再現可能です:

class foo {

    public $bar;
    public function __get($name) {

        echo "Get:$name";
        return $this->$name;
    }

    public function __set($name, $value) {

        echo "Set:$name to $value";
        $this->$name = $value;
    }
}


$foo = new foo();

echo $foo->bar;
$foo->bar = 'test';

echo "[$foo->bar]";

これにより、次の結果のみが得られます。

[test]

そこにいくつかのdie()呼び出しを入れると、まったくヒットしていないことがわかります。

今のところ、私はそれをねじ込み、__get今のところ必要な場所で手動で使用していますが、それはあまり動的ではなく、特に呼び出されない限り「オーバーロードされた」コードが実際には呼び出されないという知識が必要です。これが私が理解しているように機能するはずがないのか、それともなぜ機能しないのかを知りたいです。

これは で実行されていphp 5.3.3ます。

4

8 に答える 8

175

__get__set__callおよび__callStaticは、メソッドまたはプロパティにアクセスできない場合に呼び出されます。あなた$barは公開されているため、アクセスできません。

マニュアルのプロパティのオーバーロードに関するセクションを参照してください。

  • __set()アクセスできないプロパティにデータを書き込むときに実行されます。
  • __get()アクセスできないプロパティからデータを読み取るために使用されます。

マジック メソッドは、ゲッターとセッターの代わりにはなりません。メソッド呼び出しやプロパティ アクセスを処理できるようにするだけで、そうでなければエラーになります。そのため、エラー処理に関連するものは他にもたくさんあります。また、適切な getter および setter または直接メソッド呼び出しを使用するよりもかなり遅いことに注意してください。

于 2011-01-17T13:34:30.593 に答える
39

を介してすべての値を格納するために配列を使用することをお勧めします__set()

class foo {

    protected $values = array();

    public function __get( $key )
    {
        return $this->values[ $key ];
    }

    public function __set( $key, $value )
    {
        $this->values[ $key ] = $value;
    }

}

このようにして、衝突を避けるために、別の方法で変数にアクセスできないようにします ($values保護されていることに注意してください)。

于 2011-01-17T13:37:04.983 に答える
20

PHPマニュアルから:

  • __set() は、アクセスできないプロパティにデータを書き込むときに実行されます。
  • __get() は、アクセスできないプロパティからデータを読み取るために使用されます。

これは、アクセスできないプロパティの読み取り/書き込みでのみ呼び出されます。ただし、プロパティは公開されているため、アクセス可能です。アクセス修飾子を保護に変更すると、問題が解決します。

于 2011-01-17T13:36:01.683 に答える
8

Berry の回答を拡張するには、アクセス レベルを protected に設定すると、明示的に宣言されたプロパティで __get および __set を使用できるようになり (少なくともクラス外でアクセスする場合)、速度がかなり遅くなります。別の質問からコメントを引用します。このトピックについて、とにかくそれを使用するケースを作成します。

__get はカスタム get 関数 (同じことを行う) よりも遅いことに同意します。これは __get() の時間で 0.0124455 であり、この 0.0024445 は 10000 ループ後のカスタム get() の時間です。 Melsi 12 年 11 月 23 日 22:32 に

Melsi のテストによると、かなり遅いとは約 5 倍遅くなります。これは間違いなくかなり遅いですが、テストでは、このメソッドを使用してプロパティに 10,000 回アクセスできることが示されていることにも注意してください。定義された実際の get および set メソッドと比較するとかなり遅く、それは控えめな表現ですが、物事の全体的なスキームでは、5 倍遅くても実際に遅いことはありません。

操作の計算時間はまだ無視できる程度であり、実際のアプリケーションの 99% で考慮する価値はありません。本当に避けるべき唯一のケースは、実際に 1 回のリクエストで 10,000 回以上プロパティにアクセスする場合です。トラフィックの多いサイトは、アプリケーションを実行し続けるためにさらに数台のサーバーを投入する余裕がない場合、非常に悪いことをしています。アクセス率が問題となるトラフィックの多いサイトのフッターに表示される 1 行のテキスト広告は、おそらくそのテキスト行で 1,000 台のサーバー ファームの料金を支払うことができます。アプリケーションのプロパティへのアクセスには 100 万分の 1 秒かかるため、エンド ユーザーは、ページの読み込みに時間がかかっている原因を知りたいと思って指をタップすることはありません。

私は .NET のバックグラウンドを持つ開発者としてこれを言いますが、消費者に見えない get メソッドと set メソッドは .NET の発明ではありません。それらは単にそれらなしではプロパティではありません。これらの魔法のメソッドは、プロパティのバージョンを「プロパティ」と呼んでさえも、PHP 開発者の救いの恵みです。また、PHP 用の Visual Studio 拡張機能は、保護されたプロパティを使用して IntelliSense をサポートしていますが、そのトリックを念頭に置いていると思います。このように魔法の __get および __set メソッドを使用する十分な数の開発者がいれば、PHP 開発者は実行時間を調整して開発者コミュニティに対応できると思います。

編集: 理論的には、保護されたプロパティはほとんどの状況で機能するように見えました。実際には、クラス定義および拡張クラス内のプロパティにアクセスするときに、getter および setter を使用したい場合が多いことがわかります。より良い解決策は、他のクラスを拡張するときの基本クラスとインターフェイスです。そのため、基本クラスから実装クラスに数行のコードをコピーするだけで済みます。私は自分のプロジェクトの基本クラスでもう少し作業を行っているので、現在提供するインターフェイスはありませんが、リフレクションを使用してプロパティを削除および移動する魔法のプロパティを取得および設定する未テストのストリップダウン クラス定義を次に示します。保護された配列:

/** Base class with magic property __get() and __set() support for defined properties. */
class Component {
    /** Gets the properties of the class stored after removing the original
     * definitions to trigger magic __get() and __set() methods when accessed. */
    protected $properties = array();

    /** Provides property get support. Add a case for the property name to
     * expand (no break;) or replace (break;) the default get method. When
     * overriding, call parent::__get($name) first and return if not null,
     * then be sure to check that the property is in the overriding class
     * before doing anything, and to implement the default get routine. */
    public function __get($name) {
        $caller = array_shift(debug_backtrace());
        $max_access = ReflectionProperty::IS_PUBLIC;
        if (is_subclass_of($caller['class'], get_class($this)))
            $max_access = ReflectionProperty::IS_PROTECTED;
        if ($caller['class'] == get_class($this))
            $max_access = ReflectionProperty::IS_PRIVATE;
        if (!empty($this->properties[$name])
            && $this->properties[$name]->class == get_class()
            && $this->properties[$name]->access <= $max_access)
            switch ($name) {
                default:
                    return $this->properties[$name]->value;
            }
    }

    /** Provides property set support. Add a case for the property name to
     * expand (no break;) or replace (break;) the default set method. When
     * overriding, call parent::__set($name, $value) first, then be sure to
     * check that the property is in the overriding class before doing anything,
     * and to implement the default set routine. */
    public function __set($name, $value) {
        $caller = array_shift(debug_backtrace());
        $max_access = ReflectionProperty::IS_PUBLIC;
        if (is_subclass_of($caller['class'], get_class($this)))
            $max_access = ReflectionProperty::IS_PROTECTED;
        if ($caller['class'] == get_class($this))
            $max_access = ReflectionProperty::IS_PRIVATE;
        if (!empty($this->properties[$name])
            && $this->properties[$name]->class == get_class()
            && $this->properties[$name]->access <= $max_access)
            switch ($name) {
                default:
                    $this->properties[$name]->value = $value;
            }
    }

    /** Constructor for the Component. Call first when overriding. */
    function __construct() {
        // Removing and moving properties to $properties property for magic
        // __get() and __set() support.
        $reflected_class = new ReflectionClass($this);
        $properties = array();
        foreach ($reflected_class->getProperties() as $property) {
            if ($property->isStatic()) { continue; }
            $properties[$property->name] = (object)array(
                'name' => $property->name, 'value' => $property->value
                , 'access' => $property->getModifier(), 'class' => get_class($this));
            unset($this->{$property->name}); }
        $this->properties = $properties;
    }
}

コードにバグがある場合は申し訳ありません。

于 2014-03-26T02:03:59.193 に答える
5

$bar はパブリック プロパティであるためです。

$foo->bar = 'test';

上記の実行時にマジック メソッドを呼び出す必要はありません。

クラスから削除public $bar;すると、これが修正されます。

于 2011-01-17T13:36:14.857 に答える
0

public $bar;宣言を削除すると、期待どおりに機能するはずです。

于 2011-01-17T13:36:42.750 に答える