PHP: Самый чистый способ изменения многомерного массива?

У меня есть класс 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');