2011-08-26 27 views
6

Tengo una clase Config en mi aplicación, que carga la configuración de configuración estática y la analiza en matrices.
Como necesito anular algunos elementos durante el tiempo de ejecución, necesitaría acceder a la variable pública dentro de la clase Config haciendo esto; $config->values['onelevel']['twolevel'] = 'changed';PHP: ¿La forma más limpia de modificar una matriz multidimensional?

Me gustaría hacer un método que se llama override que hace esto por mí, pero no entiendo cuál sería la mejor manera de hacerlo ya que mis archivos de configuración pueden obtener una cantidad desconocida de niveles anidados en el futuro.

Sería maravilloso hacer algo como $config->onelevel->twolevel = 'changed' y dejar que el método mágico __set se ocupe de la anidación, pero por lo que puedo decir, no es posible.

¿Cuál sería la mejor manera de hacerlo?

+0

Tal vez ese tipo de datos no puede ser representado por la palabra "config"; guárdelo en el DB – Dor

+0

¿Por qué eligió la respuesta de ErikPerik como la mejor respuesta cuando mi respuesta cumple con los requisitos de su pregunta más de cerca? No es que no quiera otorgar puntos de representante de ErikPerik. Solo tengo curiosidad de por qué sentiste que su respuesta fue la mejor respuesta. –

+0

Originalmente pensé que su solución estaba bastante limpia hasta que vi la versión actualizada de la tuya. ¡Utilizar el 'ArrayObject' es algo que nunca he hecho antes, definitivamente necesito buscarlo! – Industrial

Respuesta

6

Es posible hacer lo que quiera.

Este ejemplo está ampliamente inspirado en Zend_Config y el ejemplo dado en los documentos PHP en el ArrayAccess interface.

edición:
Con una pequeña salvedad: es necesario llamar a toArray() en los datos que representan una matriz, para convertirlo en una matriz, como la clase necesita internamente para convertir datos de la matriz a una instancia de sí mismo, a permitir el acceso con el operador de propiedad del objeto ->:

Eh, eso ya no es necesario, por supuesto, ya que ahora implementa ArrayAccess.;-)
/editar

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; 
    } 
} 

editar 2:
La clase Config incluso se puede simplificar en gran medida mediante la extensión de ArrayObject. Como beneficio adicional, también puedes convertirlo en una matriz adecuada.

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); 
    } 
} 

Ejemplo de uso:

$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... 
+0

Hi fireeyedboy. Por favor, eche un vistazo aquí y vea si tiene alguna idea; http://stackoverflow.com/questions/7254675/extending-arrayobject-in-php-properly – Industrial

+0

@Industrial: Coincidentemente, solo estaba viendo la pregunta. :) –

0

Haría una función con una cantidad indefinida de argumentos, y usar func_get_args() para obtener los argumentos, a partir de ahí, es simplemente actualizar.

0

Hace algún tiempo que necesita una función que me deja acceder a una matriz a través del camino de cuerdas, tal vez usted puede hacer uso de lo siguiente:

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; 
} 

Ejemplo: PMA_array_write('onelevel/twolevel', $array, 'value');

+0

Vaya, gracias. fijo – Crack

0

Bueno, usted ha dicho que PARSE ellos en conjunto. ¿Por qué no analizarlos en stdObjects y luego simplemente hacer $config->onelevel->twolevel = 'changed' como quieras? :)

5

Yo también he tenido este problema, y ​​he resuelto esto con este código. Sin embargo, se basó en API como: 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; 

Se acaba de bucle a través de la matriz y la celebración de pista del valor actual con una variable de referencia.

0

Puedes construir un tipo por tu cuenta, que proporciona la interfaz que estás buscando o la función de ayuda que describes.

Este es un ejemplo de código de una función de anulación 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')); 
Cuestiones relacionadas