1. ホーム
  2. php

[解決済み] PHP - オーバーロードされたプロパティの間接的な変更

2022-01-31 07:04:31

質問

この質問は何度かされているようですが、どれも回避策についてまともな答えがないようです。もしかしたら、私のような特殊なケースに対応するものがあるかもしれません。

マジックメソッドを使用するマッパークラスを作成しています。 __get() を使用して、他のオブジェクトを遅延ロードします。以下のような感じです。

public function __get ( $index )
{
    if ( isset ($this->vars[$index]) )
    {
        return $this->vars[$index];
    }

    // $index = 'role';
    $obj = $this->createNewObject ( $index );

    return $obj;
}

私のコードでは、そうしています。

$user = createObject('user');
$user->role->rolename;

これは今のところうまくいっています。その User オブジェクトは 'role' という名前のプロパティを持っていないので、マジックである __get() メソッドを使用してそのオブジェクトを作成し、そのプロパティを 'role' オブジェクトから返します。

しかし、私は'rolename'を変更しようとしたとき。

$user = createUser();
$user->role->rolename = 'Test';

すると、以下のようなエラーが発生します。

お知らせです。オーバーロードされたプロパティの間接的な変更は効果なし

これがPHPのバグなのか、それとも期待された動作なのかは分かりませんが、いずれにせよ、私の望むような動作はしません。これは私にとっては本当にショーストッパーです...。なぜなら、一体どうやって遅延ロードされたオブジェクトのプロパティを変更することができるのでしょうか?


EDIT

実際の問題は、複数のオブジェクトを含む配列を返したときだけ発生するようです。

この問題を再現するコードの例を追加しました。

http://codepad.org/T1iPZm9t

本当に「エラー」を確認するには、PHP環境でこれを実行する必要があります。しかし、ここでは本当に面白いことが起こっているのです。

あるオブジェクトのプロパティを変更しようとすると、「cant change overloaded property」というメッセージが表示されます。しかし、その後にプロパティをechoすると、実際に値が変更されたことがわかります。本当に奇妙です...

どうすればいいですか?

いいものを見せてもらった

走る

class Sample extends Creator {

}

$a = new Sample ();
$a->role->rolename = 'test';
echo  $a->role->rolename , PHP_EOL;
$a->role->rolename->am->love->php = 'w00';
echo  $a->role->rolename  , PHP_EOL;
echo  $a->role->rolename->am->love->php   , PHP_EOL;

出力

test
test
w00

使用クラス

abstract class Creator {
    public function __get($name) {
        if (! isset ( $this->{$name} )) {
            $this->{$name} = new Value ( $name, null );
        }
        return $this->{$name};
    }

    public function __set($name, $value) {
        $this->{$name} = new Value ( $name, $value );
    }



}

class Value extends Creator {
    private $name;
    private $value;
    function __construct($name, $value) {
        $this->name = $name;
        $this->value = $value;
    }

    function __toString()
    {
        return (string) $this->value ;
    }
}      

Edit : ご要望の多かった新しい配列のサポート

class Sample extends Creator {

}

$a = new Sample ();
$a->role = array (
        "A",
        "B",
        "C" 
);


$a->role[0]->nice = "OK" ;

print ($a->role[0]->nice  . PHP_EOL);

$a->role[1]->nice->ok = array("foo","bar","die");

print ($a->role[1]->nice->ok[2]  . PHP_EOL);


$a->role[2]->nice->raw = new stdClass();
$a->role[2]->nice->raw->name = "baba" ;

print ($a->role[2]->nice->raw->name. PHP_EOL);

出力

 Ok die baba

モディファイドクラス

abstract class Creator {
    public function __get($name) {
        if (! isset ( $this->{$name} )) {
            $this->{$name} = new Value ( $name, null );
        }
        return $this->{$name};
    }

    public function __set($name, $value) {
        if (is_array ( $value )) {
            array_walk ( $value, function (&$item, $key) {
                $item = new Value ( $key, $item );
            } );
        }
        $this->{$name} = $value;

    }

}

class Value {
    private $name ;
    function __construct($name, $value) {
        $this->{$name} = $value;
        $this->name = $value ;
    }

    public function __get($name) {
        if (! isset ( $this->{$name} )) {
            $this->{$name} = new Value ( $name, null );
        }

        if ($name == $this->name) {
            return $this->value;
        }

        return $this->{$name};
    }

    public function __set($name, $value) {
        if (is_array ( $value )) {
            array_walk ( $value, function (&$item, $key) {
                $item = new Value ( $key, $item );
            } );
        }
        $this->{$name} = $value;
    }

    public function __toString() {
        return (string) $this->name ;
    }   
}