Intereting Posts

PHP – косвенная модификация перегруженного имущества

Я знаю, что этот вопрос задавали несколько раз, но ни у кого из них нет реального ответа на обходной путь. Может быть, есть один для моего конкретного случая.

Я создаю класс 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. Простейшее обходное решение для меня работало

Предположение:

  1. $object – это объект с перегруженными __get и __set из базового класса, который я не __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); с // 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);