ArrayAccess 多维 (un)set?

我有一个类实现,我试图让它与多维数组一起工作。 和工作。 虽然给我带来了一个问题。ArrayAccessexistsgetsetunset

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

我知道_arr美元可以公开,这样你就可以直接访问它,但对于我的实现来说,它不是必需的,并且是私有的。

最后 2 行抛出错误:。Notice: Indirect modification of overloaded element

我知道通常不适用于多维数组,但是是否有任何围绕这个或任何稍微干净的实现来允许所需的功能?ArrayAccess

我能想到的最好的主意是使用一个角色作为分隔符,并在其中进行测试并采取相应的行动。虽然如果你正在处理一个可变的深度,这变得非常丑陋。setunset

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

感谢任何人都可以提供的任何帮助。


答案 1

这个问题可以通过更改为(通过添加引用返回)来解决,但它会导致致命错误(“ArrayTest::offsetGet()的声明必须与 ArrayAccess::offsetGet()的声明兼容”)。public function offsetGet($name)public function &offsetGet($name)

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

我们发现,如果不炸毁接口并创建BC或提供额外的接口来支持引用,从而创建内部噩梦,这是无法解决的 - 实际上,我看不到我们可以做到这一点的方法。因此,我们决定强制执行原始设计,不允许引用完成。

编辑:如果你仍然需要该功能,我建议改用 magic 方法(, 等),因为通过引用返回值。这会将语法更改为如下所示:__get()__set()__get()

$arrTest->test['bar'] = 5;

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

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

从 PHP 5.3.4 开始,原型检查被放宽,此方法的实现可以通过引用返回。这使得可以对 ArrayAccess 对象的重载数组维度进行间接修改。


答案 2

这个问题实际上是可以解决的,完全可以正常工作。

从对 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"

?>

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

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

?>

这将为您提供一个可以被视为数组的对象(大多数情况下,请参阅代码中的注释),它支持多维数组集/get/unset。