PHP – 重载属性的间接修改

我知道这个问题已经被问了好几次了,但是他们没有一个真正的解决方法。 也许有一个针对我的具体情况。

我正在构build一个使用魔术方法__get()来延迟加载其他对象的mapper类。 它看起来像这样:

 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'的属性,所以它使用magic __get()方法来创build该对象,并从'role'对象返回它的属性。

但是当我尝试修改“angular色名称”时:

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

然后它给了我以下错误:

注意:重载属性的间接修改不起作用

不知道这是否仍然是PHP中的一些错误,或者是“预期的行为”,但无论如何,它不会按照我想要的方式工作。 这对我来说真的是一个展示塞…因为我怎么能够改变懒加载对象的属性?


编辑:

实际问题似乎只有当我返回一个包含多个对象的数组时才会发生。

我已经添加了一个代码片段,它重现了这个问题:

http://codepad.org/T1iPZm9t

你应该真的在你的PHP环境中运行这个真正的'错误'。 但是这里有一些非常有趣的事情。

我试图改变一个对象的属性,这给了我的通知“不能改变重载属性”。 但是,如果我之后回声属性,我发现它实际上是DID更改值…真的很奇怪…

很高兴你给了我一些东西来玩

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

编辑:新arrays支持按要求

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

所有你需要做的是在你的__get函数前加上“&”作为参考传递它:

 public function &__get ( $index ) 

这一阵挣扎了一阵子。

我有这个相同的错误,没有你的整个代码,很难确切地确定如何解决它,但它是由没有__set函数引起的。

我过去的做法是我做了这样的事情:

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

现在如果你这样做:

 echo $user->role->rolename; 

你应该看到'testing'

虽然我在这个讨论中已经很晚了,但我认为这对未来的某个人可能是有用的。

我面临类似的情况。 那些不介意取消设置和重置variables的人最简单的解决方法就是这样做。 我很确定这个不工作的原因从其他答案和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); 

注意:这个解决scheme是解决问题并获得复制variables的快速解决方法之一。 如果数组过于狂妄,可能会强制重写__get方法来返回大数组的引用而非昂贵的复制。

这是由于PHP如何处理重载的属性,因为它们不可修改或通过引用传递。

有关重载的更多信息,请参阅手册 。

要解决此问题,可以使用__set函数或创buildcreateObject方法。

下面是一个__get__set ,它提供了一个类似于你的情况的解决方法,你可以简单的修改__set来满足你的需求。

注意__get从来没有实际返回一个variables。 而且一旦你在对象中设置了一个variables,它就不会被重载。

 /** * 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"; 

我遇到了与w00相同的问题,但是我没有自由地重写发生此问题(E_NOTICE)的组件的基本function。 我已经能够解决使用ArrayObject而不是基本types数组()的问题 。 这将返回一个对象,它将默认由引用返回。

我收到这样的通知:

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

这固定了它:

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