Проблема: Я пытаюсь расширить 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 по какой-то причине?