У меня есть класс Config
в моем приложении, который загружает статические настройки конфигурации и анализирует их в массивы.
Поскольку мне нужно переопределить некоторые элементы во время выполнения, мне нужно будет получить доступ к общедоступной переменной внутри класса Config
-class, выполнив это; $config->values['onelevel']['twolevel'] = 'changed';
Я хотел бы сделать метод, который называется override
который делает это для меня, но я не могу обойти то, что было бы лучшим способом сделать это, так как мои конфигурационные файлы могут получить неизвестное количество вложенных уровней в будущем.
Было бы $config->onelevel->twolevel = 'changed'
сделать что-то вроде $config->onelevel->twolevel = 'changed'
и позволить методу __set magic заботиться о гнездовании, но из того, что я могу сказать, это невозможно.
Какой был бы лучший способ сделать это?
Вы можете делать то, что хотите.
Этот пример сильно вдохновлен Zend_Config и примером, приведенным в документах PHP на интерфейсе ArrayAccess .
редактировать:
С одним незначительным оговоркой: вам нужно вызвать toArray()
для данных, представляющих массив, чтобы преобразовать их в массив, поскольку класс внутренне должен скрывать данные массива самому экземпляру, чтобы разрешить доступ с помощью оператора свойства объекта ->
:
Эх, это, конечно, не обязательно, потому что теперь он реализует ArrayAccess. 😉
/редактировать
class Config implements ArrayAccess { protected $_data; public function __construct( array $data ) { foreach( $data as $key => $value ) { $this->$key = $value; } } public function __get( $key ) { return $this->offsetGet( $key ); } public function __isset( $key ) { return $this->offsetExists( $key ); } public function __set( $key, $value ) { $this->offsetSet( $key, $value ); } public function __unset( $key ) { $this->offsetUnset( $key ); } public function offsetSet( $offset, $value ) { $value = is_array( $value ) ? new self( $value ) : $value; if( is_null( $offset ) ) { $this->_data[] = $value; } else { $this->_data[ $offset ] = $value; } } public function offsetExists( $offset ) { return isset( $this->_data[ $offset ] ); } public function offsetUnset( $offset ) { unset( $this->_data[ $offset ] ); } public function offsetGet( $offset ) { return isset( $this->_data[ $offset ] ) ? $this->_data[ $offset ] : null; } public function toArray() { $array = array(); $data = $this->_data; foreach( $data as $key => $value ) { if( $value instanceof Config ) { $array[ $key ] = $value->toArray(); } else { $array[ $key ] = $value; } } return $array; } }
изменить 2:
Класс Config
даже может быть значительно упрощен путем расширения ArrayObject
. В качестве дополнительного преимущества вы можете также применить его к соответствующему массиву.
class Config extends ArrayObject { protected $_data; public function __construct( array $data ) { parent::__construct( array(), self::ARRAY_AS_PROPS ); foreach( $data as $key => $value ) { $this->$key = $value; } } public function offsetSet( $offset, $value ) { $value = is_array( $value ) ? new self( $value ) : $value; return parent::offsetSet( $offset, $value ); } }
Пример использования:
$configData = array( 'some' => array( 'deeply' => array( 'nested' => array( 'array' => array( 'some', 'data', 'here' ) ) ) ) ); $config = new Config( $configData ); // casting to real array var_dump( (array) $config->some->deeply->nested->array ); $config->some->deeply->nested->array = array( 'new', 'awsome', 'data', 'here' ); // Config object, but still accessible as array var_dump( $config->some->deeply->nested->array[ 0 ] ); $config[ 'some' ][ 'deeply' ][ 'nested' ][ 'array' ] = array( 'yet', 'more', 'new', 'awsome', 'data', 'here' ); var_dump( $config[ 'some' ][ 'deeply' ][ 'nested' ][ 'array' ] ); $config[ 'some' ][ 'deeply' ][ 'nested' ][ 'array' ][] = 'append data'; var_dump( $config[ 'some' ][ 'deeply' ][ 'nested' ][ 'array' ] ); var_dump( isset( $config[ 'some' ][ 'deeply' ][ 'nested' ][ 'array' ] ) ); unset( $config[ 'some' ][ 'deeply' ][ 'nested' ][ 'array' ] ); var_dump( isset( $config[ 'some' ][ 'deeply' ][ 'nested' ][ 'array' ] ) ); // etc...
У меня тоже была эта проблема, и я решил это с помощью этого кода. Однако он был основан на API как: Config::set('paths.command.default.foo.bar')
.
<?php $name = 'paths.commands.default'; $namespaces = explode('.', $name); $current = &$this->data; // $this->data is your config-array foreach ( $namespaces as $space ) { $current = &$current[$space]; } $current = $value;
Это просто цикл по массиву и отслеживание текущего значения с помощью ссылочной переменной.
Я бы сделал функцию с неопределенным количеством аргументов и использовал func_get_args()
для получения аргументов, оттуда это просто обновление.
Ну, ты сказал, что разбираешь их в массив. Почему бы не разобрать их в stdObjects
а затем просто сделать $config->onelevel->twolevel = 'changed'
как вы хотите? 🙂
Вы можете либо создать собственный тип, который обеспечивает интерфейс, который вы ищете, либо вы используете вспомогательную функцию, которую вы описываете.
Это пример кода функции переопределения. Demo :
$array = array( 'a' => array( 'b' => array( 'c' => 'value') ), 'b' => array( 'a' => 'value' ), ); function override($array, $value) { $args = func_get_args(); $array = array_shift($args); $value = array_shift($args); $set = &$array; while(count($args)) { $key = array_shift($args); $set = &$set[$key]; } $set = $value; unset($set); return $array; } var_dump(override($array, 'new', 'a', 'b', 'c'));
в$array = array( 'a' => array( 'b' => array( 'c' => 'value') ), 'b' => array( 'a' => 'value' ), ); function override($array, $value) { $args = func_get_args(); $array = array_shift($args); $value = array_shift($args); $set = &$array; while(count($args)) { $key = array_shift($args); $set = &$set[$key]; } $set = $value; unset($set); return $array; } var_dump(override($array, 'new', 'a', 'b', 'c'));
с$array = array( 'a' => array( 'b' => array( 'c' => 'value') ), 'b' => array( 'a' => 'value' ), ); function override($array, $value) { $args = func_get_args(); $array = array_shift($args); $value = array_shift($args); $set = &$array; while(count($args)) { $key = array_shift($args); $set = &$set[$key]; } $set = $value; unset($set); return $array; } var_dump(override($array, 'new', 'a', 'b', 'c'));
Некоторое время назад мне нужна была функция, которая позволила бы мне получить доступ к массиву через строковый путь, возможно, вы можете использовать это:
function PMA_array_write($path, &$array, $value) { $keys = explode('/', $path); $last_key = array_pop($keys); $a =& $array; foreach ($keys as $key) { if (! isset($a[$key])) { $a[$key] = array(); } $a =& $a[$key]; } $a[$last_key] = $value; }
Пример: PMA_array_write('onelevel/twolevel', $array, 'value');