68

この質問が何度か聞かれたことは知っていますが、回避策の本当の答えはありません。たぶん私の特定のケースのためのものがあります。

__get()他のオブジェクトを遅延ロードするためにmagicメソッドを使用するマッパークラスを構築しています。これは次のようになります。

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;

これは今のところ機能します。オブジェクトには「Userrole」というプロパティがないため、マジック__get()メソッドを使用してそのオブジェクトを作成し、「role」オブジェクトからそのプロパティを返します。

しかし、'rolename'を変更しようとすると:

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

次に、次のエラーが発生します。

注意:オーバーロードされたプロパティを間接的に変更しても効果はありません

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


編集:

実際の問題は、複数のオブジェクトを含む配列を返す場合にのみ発生するようです。

問題を再現するサンプルコードを追加しました。

http://codepad.org/T1iPZm9t

PHP環境でこれを実際に実行する必要があります。実際には「エラー」が表示されます。しかし、ここでは本当に興味深いことが起こっています。

オブジェクトのプロパティを変更しようとすると、「cantchangeoverloadedproperty」という通知が表示されます。しかし、その後プロパティをエコーすると、実際に値が変更されたことがわかります...本当に奇妙です...

4

8 に答える 8

103

__get関数の前に「&」を追加して参照として渡すだけです。

public function &__get ( $index )

しばらくこれに苦労しました。

于 2012-11-01T22:47:33.697 に答える
15

素敵なあなたは私に遊んで何かをくれました

走る

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 ;
    }
}      

編集:要求に応じて新しいアレイサポート

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 ;
    }   
}
于 2012-05-04T19:48:14.767 に答える
10

これと同じエラーが発生しました。コード全体がないと、修正方法を正確に特定することは困難ですが、__set関数がないことが原因です。

私が過去にそれを回避した方法は、私が次のようなことをしたことです:

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

今これを行う場合:

echo $user->role->rolename;

「テスト」が表示されます

于 2012-05-04T19:29:33.353 に答える
4

私はこの議論に非常に遅れていますが、これは将来誰かのために役立つかもしれないと思いました。

私も同じような状況に直面していました。変数の設定解除とリセットを気にしない人にとっての最も簡単な回避策は、そうすることです。これが機能しない理由は、他の回答とphp.netのマニュアルから明らかです。私のために働いた最も簡単な回避策は

予測:

  1. $object__getオーバーロードさ__setれた基本クラスのオブジェクトであり、変更する自由はありません。
  2. shippingDataたとえば、次のフィールドを変更する配列です:-phone_number

 

// First store the array in a local variable.
$tempShippingData = $object->shippingData;

unset($object->shippingData);

$tempShippingData['phone_number'] = '888-666-0000' // what ever the value you want to set

$object->shippingData = $tempShippingData; // this will again call the __set and set the array variable

unset($tempShippingData);

注:この解決策は、問題を解決して変数をコピーするための迅速な回避策の1つです。__get配列があまりにも巨大な場合は、メソッドを強制的に書き直して、大きな配列のかなり高価なコピーの参照を返すようにするとよいでしょう。

于 2013-11-01T15:23:54.817 に答える
3

私はこれを行うためにこの通知を受け取っていました:

$var = reset($myClass->my_magic_property);

これはそれを修正しました:

$tmp = $myClass->my_magic_property;
$var = reset($tmp);
于 2017-04-11T15:53:45.480 に答える
3

VinnyDに同意します。必要な結果を参照として返すために、__ get関数の前に「&」を追加する必要があります。

public function &__get ( $propertyname )

ただし、次の2つの点に注意してください。

1)あなたもする必要があります

return &$something;

または、参照ではなく値を返している可能性があります...

2)__getが参照を返す場合は、対応する__setが呼び出されないことも意味することに注意してください。これは、phpが__getによって返される参照を使用してこれを解決するためです。この参照は、代わりに呼び出されます。

それで:

$var = $object->NonExistentArrayProperty; 

__getが呼び出され、__ getには&__ getがあり、&$ somethingが返されるため、$ varは、意図したとおり、オーバーロードされたプロパティへの参照になります。

$object->NonExistentArrayProperty = array(); 

期待どおりに動作し、__setは期待どおりに呼び出されます...

だが:

$object->NonExistentArrayProperty[] = $value;

また

$object->NonExistentArrayProperty["index"] = $value;

オーバーロードされた配列プロパティで要素が正しく追加または変更されるという意味で期待どおりに機能しますが、__ setは呼び出されません:代わりに__getが呼び出されます!

これらの2つの呼び出しは、&__ getを使用せずに&$ somethingを返す場合は機能しませんが、このように機能しますが、__ setを呼び出すことはなく、常に__getを呼び出します。

これが私が参照を返すことにした理由です

return &$something;

$ somethingがarray()の場合、またはオーバーロードされたプロパティに特別なセッターメソッドがなく、代わりに値を返す場合

return $something;

$ somethingが配列ではない場合、または特別なセッター関数がある場合。

いずれにせよ、これを正しく理解するのは非常に難しいことでした。:)

于 2019-05-30T12:03:14.260 に答える
2

w00と同じ問題が発生しましたが、この問題(E_NOTICE)が発生したコンポーネントの基本機能を書き直す自由がありませんでした。基本型array()の代わりにArrayObjectを使用して問題を修正することができました。これによりオブジェクトが返され、デフォルトでは参照によって返されます。

于 2013-06-11T10:28:02.467 に答える
1

これは、PHPがオーバーロードされたプロパティを変更できない、または参照によって渡されないという点で、それらをどのように処理するかによって発生します。

オーバーロードの詳細については、マニュアルを参照してください。

この問題を回避するには、__set関数を使用するか、メソッドを作成しますcreateObject

以下は、__get__set同様の状況の回避策を提供するaであり__set、ニーズに合わせてを変更するだけです。

__get変数を実際に返すことは決してないことに注意してください。オブジェクトに変数を設定すると、オーバーロードされなくなります。

/**
 * Get a variable in the event.
 *
 * @param  mixed  $key  Variable name.
 *
 * @return  mixed|null
 */
public function __get($key)
{
    throw new \LogicException(sprintf(
        "Call to undefined event property %s",
        $key
    ));
}

/**
 * Set a variable in the event.
 *
 * @param  string  $key  Name of variable
 *
 * @param  mixed  $value  Value to variable
 *
 * @return  boolean  True
 */
public function __set($key, $value)
{
    if (stripos($key, '_') === 0 && isset($this->$key)) {
        throw new \LogicException(sprintf(
            "%s is a read-only event property", 
            $key
        ));
    }
    $this->$key = $value;
    return true;
}

これにより、次のことが可能になります。

$object = new obj();
$object->a = array();
$object->a[] = "b";
$object->v = new obj();
$object->v->a = "b";
于 2012-05-04T19:42:54.293 に答える