Расширение ArrayObject в PHP правильно?

Проблема: Я пытаюсь расширить ArrayObject PHP, как показано ниже. К сожалению, я не могу заставить его работать правильно при настройке многомерных объектов, а вместо этого возникает ошибка, так как у меня есть строгие настройки, включенные в PHP. ( Error: Strict standards: Creating default object from empty value )

Вопрос: Как я могу изменить свой класс, чтобы автоматически создавать несуществующие уровни для меня?

Код:

 $config = new Config; $config->lvl1_0 = true; // Works $config->lvl1_1->lvl2 = true; // Throws error as "lvl1" isn't set already class Config extends ArrayObject { function __construct() { parent::__construct(array(), self::ARRAY_AS_PROPS); } public function offsetSet($k, $v) { $v = is_array($v) ? new self($v) : $v; return parent::offsetSet($k, $v); } } 

Взяв более подробное представление о вашей проблеме, вы можете создать класс, который моделирует концепцию многомерного объекта.

Решение im posting не распространяется на ArrayObject для ArrayObject указанных вами целей. Поскольку вы отметили свой вопрос как oop, я думаю, что важно укрепить разделение так, как вы храните состояние объекта от того, как вы к нему обращаетесь.

Надеюсь, это поможет вам архивировать то, что вам нужно!

Из того, что вы сказали, многомерный объект – это тот, который:

  • обрабатывает несколько уровней вложенной информации
  • он делает это, предоставляя чтение / запись доступа к информации через свойства
  • ведет себя хорошо, когда доступны неопределенные свойства. Это означает, что, например, вы делаете следующее в пустом экземпляре: $config->database->host = 'localhost' database и уровни host инициализируются автоматически, и host будет возвращать 'localhost' при запросе.
  • в идеале, будет инициализирован из ассоциативных массивов (поскольку вы уже можете анализировать в них файлы конфигурации)

Предложенное решение

Итак, как эти функции могут быть реализованы?

Второй из них прост: с использованием PHP __get и __set методов. Эти вызовы будут вызваны всякий раз, когда чтение / запись будет выполняться по неприемлемому свойству (которое не определено в объекте). Трюк будет заключаться в том, чтобы не объявлять какое-либо свойство и не обрабатывать операции собственника с помощью этих методов, а сопоставить имя свойства, которое является ключом к ассоциированной группе, используемой в качестве хранилища. Они обеспечат в основном интерфейс для доступа к информации, хранящейся внутри страны.

Для третьего нам нужен способ создания нового уровня вложенности при чтении незаявленного свойства. Ключевым моментом здесь является осознание того, что возвращаемое значение для свойства должно быть многомерным объектом, поэтому из него могут быть созданы дополнительные уровни вложенности: всякий раз, когда мы запрашиваем свойство, имя которого отсутствует во внутреннем массиве, мы свяжем это имя с новым экземпляром MultiDimensionalObject и вернем его. Возвращаемый объект также сможет обрабатывать определенные или неопределенные свойства.

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

Четвертый легко (см. __construct реализации __construct ). Мы просто должны убедиться, что мы создаем MultiDimensionalObject когда значение свойства является массивом.

Наконец, первый из них: способ обработки второй и третьей функций позволяет нам читать и писать свойства (объявленные и необъявленные) на любом уровне вложенности. Вы можете сделать что-то вроде $config->foo->bar->baz = 'hello' в пустом экземпляре, а затем запросить для $config->foo->bar->baz успешно.

Важно. Обратите внимание, что MultiDimensionalObject вместо того, чтобы представлять собой массив, состоит из массива, позволяя вам изменить способ сохранения состояния объекта по мере необходимости.

Реализация

 /* Provides an easy to use interface for reading/writing associative array based information */ /* by exposing properties that represents each key of the array */ class MultiDimensionalObject { /* Keeps the state of each property */ private $properties; /* Creates a new MultiDimensionalObject instance initialized with $properties */ public function __construct($properties = array()) { $this->properties = array(); $this->populate($properties); } /* Creates properties for this instance whose names/contents are defined by the keys/values in the $properties associative array */ private function populate($properties) { foreach($properties as $name => $value) { $this->create_property($name, $value); } } /* Creates a new property or overrides an existing one using $name as property name and $value as its value */ private function create_property($name, $value) { $this->properties[$name] = is_array($value) ? $this->create_complex_property($value) : $this->create_simple_property($value); } /* Creates a new complex property. Complex properties are created from arrays and are represented by instances of MultiDimensionalObject */ private function create_complex_property($value = array()){ return new MultiDimensionalObject($value); } /* Creates a simple property. Simple properties are the ones that are not arrays: they can be strings, bools, objects, etc. */ private function create_simple_property($value) { return $value; } /* Gets the value of the property named $name */ /* If $name does not exists, it is initilialized with an empty instance of MultiDimensionalObject before returning it */ /* By using this technique, we can initialize nested properties even if the path to them don't exist */ /* Ie: $config->foo - property doesn't exists, it is initialized to an instance of MultiDimensionalObject and returned $config->foo->bar = "hello"; - as explained before, doesn't exists, it is initialized to an instance of MultiDimensionalObject and returned. - when set to "hello"; bar becomes a string (it is no longer an MultiDimensionalObject instance) */ public function __get($name) { $this->create_property_if_not_exists($name); return $this->properties[$name]; } private function create_property_if_not_exists($name) { if (array_key_exists($name, $this->properties)) return; $this->create_property($name, array()); } public function __set($name, $value) { $this->create_property($name, $value); } } 

демонстрация

Код: var_dump (новый MultiDimensionalObject ());

Результат:

 object(MultiDimensionalObject)[1] private 'properties' => array empty 

Код:

 $data = array( 'database' => array ( 'host' => 'localhost' ) ); $config = new MultiDimensionalObject($data); var_dump($config->database); 

Результат:

 object(MultiDimensionalObject)[2] private 'properties' => array 'host' => string 'localhost' (length=9) 

Код:

 $config->database->credentials->username = "admin"; $config->database->credentials->password = "pass"; var_dump($config->database->credentials); 

Результат:

 object(MultiDimensionalObject)[3] private 'properties' => array 'username' => string 'admin' (length=5) 'password' => string 'pass' (length=4) 

Код:

 $config->database->credentials->username; 

Результат:

 admin 

offsetGet метод offsetGet . Если вы получаете доступ к не существующему свойству, вы можете создать его по своему усмотрению.

Поскольку вы расширяете ArrayObject, вы должны использовать метод array [] для установки или получения.

Скопированный наклеил ваш код, и он отлично работает на моем тестовом поле PHP (работает PHP 5.3.6). Он упоминает предупреждение о строгих стандартах, но он по-прежнему работает так, как ожидалось. Вот результат print_r:

 Config Object ( [storage:ArrayObject:private] => Array ( [lvl1_0] => 1 [lvl1_1] => stdClass Object ( [lvl2] => 1 ) ) ) 

Стоит отметить, что в документах PHP есть комментарий с указаниями, связанными с тем, что вы пытаетесь сделать:

sfinktah в php dot spamtrak dot org 17-апр-2011 07:27
Если вы планируете вывести свой собственный класс из ArrayObject и хотите поддерживать полную функциональность ArrayObject (например, возможность приведения в массив), необходимо использовать собственное хранилище ArrayObject «хранилище».

Подробное объяснение связано выше, но в дополнение к offsetSet который у вас есть, и offsetGet котором упоминается xdazz, вы также должны реализовать offsetExists и offsetUnset . Это не должно иметь ничего общего с вашей текущей ошибкой, но это то, о чем вы должны помнить.

Обновление: во второй половине xdazz есть ответ на вашу проблему. Если вы используете свой объект Config как массив, он работает без ошибок:

 $config = new Config; $config[ 'lvl1_0' ] = true; $config[ 'lvl1_1' ][ 'lvl2' ] = true; 

Можете ли вы это сделать или вы ограничены синтаксисом Object по какой-то причине?