Я знаю, что этот вопрос задавали несколько раз, но ни у кого из них нет реального ответа на обходной путь. Может быть, есть один для моего конкретного случая.
Я создаю класс mapper, который использует магический метод __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
не имеет свойства, называемого «роль», поэтому он использует магический метод __get()
для создания этого объекта и возвращает его свойство из объекта «role».
Но когда я пытаюсь изменить «rolename»:
$user = createUser(); $user->role->rolename = 'Test';
Тогда это дает мне следующую ошибку:
Примечание: косвенная модификация перегруженного свойства не влияет
Не уверен, что это все еще некоторая ошибка в PHP или если это «ожидаемое поведение», но в любом случае это не работает так, как я хочу. Это действительно шоу-стоппер для меня … Потому что, насколько я могу изменить свойства ленивых загруженных объектов?
РЕДАКТИРОВАТЬ:
Фактическая проблема возникает только при возврате массива, который содержит несколько объектов.
Я добавил пример кода, который воспроизводит проблему:
http://codepad.org/T1iPZm9t
Вы действительно должны запустить это в своей среде PHP, действительно видите «ошибку». Но здесь есть что-то действительно интересное.
Я пытаюсь изменить свойство объекта, что дает мне уведомление «can not change overloaded property». Но если я повторю свойство после этого, я вижу, что он действительно 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 ; } }
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;
вы должны увидеть 'Test'
Хотя я очень опаздываю в этом обсуждении, я думал, что это может быть полезно для кого-то в будущем.
Я столкнулся с подобной ситуацией. Самое простое решение для тех, кто не против сброса и сброса переменной, – это сделать это. Я уверен, что причина, по которой это не работает, понятна из других ответов и из руководства php.net. Простейшее обходное решение для меня работало
Предположение:
$object
– это объект с перегруженными __get
и __set
из базового класса, который я не __set
изменять. 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);
с// 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);
данные// 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);
Примечание. Это решение является одним из быстрых способов решения проблемы и позволяет скопировать переменную. Если массив слишком сгусток, может быть полезно заставить переписать метод __get
чтобы вернуть ссылочное довольно дорогое копирование больших массивов.
Это происходит из-за того, как PHP обрабатывает перегруженные свойства тем, что они не изменяются или не передаются по ссылке.
Дополнительную информацию о перегрузке см. В руководстве .
Чтобы обойти эту проблему, вы можете либо использовать функцию __set
либо создать метод createObject
.
Ниже приведены __get
и __set
которые обеспечивают обходной путь к аналогичной ситуации для вас, вы можете просто изменить __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";
Я столкнулся с той же проблемой, что и w00, но у меня не было возможности переписать базовую функциональность компонента, в котором возникла эта проблема (E_NOTICE). Я смог исправить проблему, используя ArrayObject вместо базового массива типов () . Это вернет объект, который по умолчанию будет возвращен ссылкой.
Я получил это уведомление за это:
$var = reset($myClass->my_magic_property);
Это фиксировало это:
$tmp = $myClass->my_magic_property; $var = reset($tmp);