PHP:修改多维数组最干净的方法?

时间:2011-08-26 09:36:25

标签: php arrays oop multidimensional-array

我的应用程序中有一个Config类,它加载静态配置设置并将它们解析为数组。
由于我需要在运行时覆盖一些元素,我需要通过执行此操作来访问Config - 类中的公共变量; $config->values['onelevel']['twolevel'] = 'changed';

我想创建一个名为override的方法来为我做这个,但我无法理解这是最好的方法,因为我的配置文件可能会有未知数量的嵌套未来的水平。

$config->onelevel->twolevel = 'changed'之类的事情并让__set魔法方法处理嵌套会很可爱,但从我所知道的情况来看,这是不可能的。

最好的方法是什么?

6 个答案:

答案 0 :(得分:6)

可以做你想做的事。

这个例子受到Zend_Config的启发,以及ArrayAccess interface上PHP文档中给出的示例。

修改
有一个小警告:你需要在表示数组的数据上调用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...

答案 1 :(得分:5)

我也遇到过这个问题,我用这段代码解决了这个问题。但它基于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;

它只是循环遍历数组并使用引用变量保持当前值的跟踪。

答案 2 :(得分:0)

我使用无限量的参数创建一个函数,并使用func_get_args()来获取参数,从那里,它只是更新。

答案 3 :(得分:0)

前段时间我需要一个让我通过字符串路径访问数组的函数,也许你可以利用它:

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

答案 4 :(得分:0)

嗯,你说你把它们解析成数组。为什么不将它们解析为stdObjects,然后根据需要执行$config->onelevel->twolevel = 'changed'?:)

答案 5 :(得分:0)

您可以自己构建一个类型,即提供您正在寻找的界面,或者您使用您描述的辅助函数。

这是覆盖函数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'));