ArrayAccess多维(un)设置?

ano*_*reh 16 php arrays interface multidimensional-array

我有一个类实现ArrayAccess,我试图让它与多维数组一起工作.existsget工作.set并且unset给我一个问题.

class ArrayTest implements ArrayAccess {
    private $_arr = array(
        'test' => array(
            'bar' => 1,
            'baz' => 2
        )
    );

    public function offsetExists($name) {
        return isset($this->_arr[$name]);
    }

    public function offsetSet($name, $value) {
        $this->_arr[$name] = $value;
    }

    public function offsetGet($name) {
        return $this->_arr[$name];
    }

    public function offsetUnset($name) {
        unset($this->_arr[$name]);
    }
}

$arrTest = new ArrayTest();


isset($arrTest['test']['bar']);  // Returns TRUE

echo $arrTest['test']['baz'];    // Echo's 2

unset($arrTest['test']['bar'];   // Error
$arrTest['test']['bar'] = 5;     // Error
Run Code Online (Sandbox Code Playgroud)

我知道$_arr可以公开,所以你可以直接访问它,但对于我的实现,它是不希望的并且是私有的.

最后两行引发错误:Notice: Indirect modification of overloaded element.

我知道ArrayAccess通常不能使用多维数组,但无论如何围绕这个或任何有点干净的实现,将允许所需的功能?

我能想出使用字符作为分隔符,并在测试它的最好的想法setunset和采取相应的行动.虽然如果你处理的是变深度,这真的很难实现.

有谁知道为什么existsget工作,以便可能复制功能?

感谢任何人都能提供的帮助.

Ale*_*nov 19

可以通过更改public function offsetGet($name)public function &offsetGet($name)(通过添加引用返回)解决该问题,它将导致致命错误(" ArrayTest :: offsetGet()的声明必须与ArrayAccess :: offsetGet()的声明兼容 ").

PHP作者不久前搞砸了这个类,现在他们不会为了向后兼容性而改变它:

我们发现,如果不破坏界面并创建BC或提供额外的界面来支持引用并因此创建内部噩梦,这是不可解决的 - 实际上我没有看到我们能够做到这一点的方式.因此,我们决定强制执行原始设计并禁止引用完​​整.

编辑:如果您还需要这个功能,我建议使用魔术方法,而不是(__get(),__set()等等),因为__get()按引用返回值.这会将语法更改为以下内容:

$arrTest->test['bar'] = 5;
Run Code Online (Sandbox Code Playgroud)

当然不是理想的解决方案,但我想不出更好的解决方案.

更新:此问题已在PHP 5.3.4中得到修复,ArrayAccess现在可以按预期工作:

从PHP 5.3.4开始,放松了原型检查,并且可以通过引用返回此方法的实现.这样就可以对ArrayAccess对象的重载数组维度进行间接修改.


Dak*_*ota 6

这个问题实际上是可以解决的,完全是功能性的.

这里对ArrayAccess文档的评论:

<?php

// sanity and error checking omitted for brevity
// note: it's a good idea to implement arrayaccess + countable + an
// iterator interface (like iteratoraggregate) as a triplet

class RecursiveArrayAccess implements ArrayAccess {

    private $data = array();

    // necessary for deep copies
    public function __clone() {
        foreach ($this->data as $key => $value) if ($value instanceof self) $this[$key] = clone $value;
    }

    public function __construct(array $data = array()) {
        foreach ($data as $key => $value) $this[$key] = $value;
    }

    public function offsetSet($offset, $data) {
        if (is_array($data)) $data = new self($data);
        if ($offset === null) { // don't forget this!
            $this->data[] = $data;
        } else {
            $this->data[$offset] = $data;
        }
    }

    public function toArray() {
        $data = $this->data;
        foreach ($data as $key => $value) if ($value instanceof self) $data[$key] = $value->toArray();
        return $data;
    }

    // as normal
    public function offsetGet($offset) { return $this->data[$offset]; }
    public function offsetExists($offset) { return isset($this->data[$offset]); }
    public function offsetUnset($offset) { unset($this->data); }

}

$a = new RecursiveArrayAccess();
$a[0] = array(1=>"foo", 2=>array(3=>"bar", 4=>array(5=>"bz")));
// oops. typo
$a[0][2][4][5] = "baz";

//var_dump($a);
//var_dump($a->toArray());

// isset and unset work too
//var_dump(isset($a[0][2][4][5])); // equivalent to $a[0][2][4]->offsetExists(5)
//unset($a[0][2][4][5]); // equivalent to $a[0][2][4]->offsetUnset(5);

// if __clone wasn't implemented then cloning would produce a shallow copy, and
$b = clone $a;
$b[0][2][4][5] = "xyzzy";
// would affect $a's data too
//echo $a[0][2][4][5]; // still "baz"

?>
Run Code Online (Sandbox Code Playgroud)

然后,您可以扩展该类,如下所示:

<?php

class Example extends RecursiveArrayAccess {
    function __construct($data = array()) {
        parent::__construct($data);
    }
}

$ex = new Example(array('foo' => array('bar' => 'baz')));

print_r($ex);

$ex['foo']['bar'] = 'pong';

print_r($ex);

?>
Run Code Online (Sandbox Code Playgroud)

这将为您提供一个可以像数组一样处理的对象(主要是参见代码中的注释),它支持多维数组set/get/unset.