Всем привет! Сколько день уже пытаюсь решит эту проблему. Нашел решение на хабре http://habrahabr.ru/post/103834/#comment_3232756. Но так не вызывается __set(). Дело в том что мне нужно записывать данные базы данных при изменений. Например: Код (PHP): $foo = new Foo(); $foo->people['girl'] = 'love'; То есть он сначала должен взять данные в базе данных people. И при изменений значений массива сохранить его. Как сохранить в базе данных или файле знаю. Проблема в том что не могу перехватить это событие. То есть реагировать на событие "="
Спасибо за отклик. Есть класс: Код (PHP): class Foo { private $storage = array(); public function &__get($name) { if (!isset($this->storage[$name])) { // Пользовательская функция для выборки из базы данных $this->storage[$name] = db()->select($name); // Там делается unserialize } return $this->storage[$name]; } public function __set($name, $value) { $this->storage[$name] = $value; // Пользовательская функция для сохранение в базу данных db()->update($name, $value); // Там делается serialize } } Все эти работает на ура: Код (PHP): $foo = new Foo(); // Получаем без проблем echo $foo->people; // Такое для сохранение тоже успешно работает $foo->people = array( 'girl' => 'cool' ); // Чтобы не запутано было, и чтобы получить сразу в базе данных для примера unset($foo); $foo = new Foo(); // Получаем без проблем echo $foo->people['girl']; А так сделать не могу: Код (PHP): // А это уже не работает, точнее не сохраняет в базу данных $foo->people['girl'] = 'love';
Так, как вы хотите, не сработает. У вас __get() возвращает ссылку, что уже не правильно. После этого ё будет идти мимо __set(), напрямую. На счёт изменения элементов массива через __set() тоже не уверен.
Спасибо вам большое. Есть ли альтернативные варианты? Просто мне очень такое нужно. Знаю что можно сделать сохранение через $foo->save();.
Можно извратиться, написать дополнительный класс для представления такого значения, который будет реализовывать интерфейс ArrayAccess, и сделать в нём callback, или в нём записывать в базу при индексировании.
в комментарии на хабре сделали ключевое замечание — конструкция Код (PHP): $o->field['index'] = $value; приводит к тому, что сначала читается $o->field, и только потом это значение используется как массив. поэтому предложили в __get() отдавать по ссылке. __set() не вызывается, но если __get() вернул по ссылке, то новое значение сохраняется. и всё работает! как справедливо отметил mkramer, если __get будет возвращать объект с интерфейсом ArrayAccess, то значение также не потеряется. но на самом деле это частный случай предыдущего приема, т.к. реально мы вернем ссылку на наш объект. короче это только усложняет дело. Nonstop, твоя проблема в том, то ты хочешь вызвать update сразу в __set. а он НЕ вызывается для случая с обращением к массиву. так и не надо!!! принято писать сразу всю запись в базу, а не отдельные поля. делай один save() в конце! Код (PHP): <?php error_reporting(-1); ini_set('display_errors', 'on'); $o = new MyClass(); $o->fieldA = 'abcd'; $o->fieldB['x'] = 12345; $o->fieldC = 'ccc'; $o->fieldB['y'] = 999; $o->fieldD = array(0 => 'd'); $o->fieldD[1] = 'dd'; $o->fieldD[] = 'ddd'; $o->save(); //----------------------------------- class MyClass { private $fields; public function __construct() { echo __METHOD__."\n"; $this->fields = array(); } public function &__get($name) { echo __METHOD__."({$name})\n"; if (!array_key_exists($name, $this->fields)) { $this->fields[$name] = null; } return $this->fields[$name]; } public function __set($name, $value) { echo __METHOD__."({$name})\n"; return $this->fields[$name] = $value; } public function save() { echo __METHOD__."\n"; var_export($this->fields); } } результат: Код (Text): MyClass::__construct MyClass::__set(fieldA) MyClass::__get(fieldB) MyClass::__set(fieldC) MyClass::__get(fieldB) MyClass::__set(fieldD) MyClass::__get(fieldD) MyClass::__get(fieldD) MyClass::save array ( 'fieldA' => 'abcd', 'fieldB' => array ( 'x' => 12345, 'y' => 999, ), 'fieldC' => 'ccc', 'fieldD' => array ( 0 => 'd', 1 => 'dd', 2 => 'ddd', ), )