如何使用ArrayObject取消设置嵌套数组?

时间:2012-04-02 18:36:08

标签: php arrayobject

ideone

示例代码:

<?php
$a = new ArrayObject();
$a['b'] = array('c'=>array('d'));
print_r($a);
unset($a['b']['c']);
print_r($a);

输出

ArrayObject Object
(
    [b] => Array
        (
            [c] => Array
                (
                    [0] => d
                )
        )
)
ArrayObject Object
(
    [b] => Array
        (
            [c] => Array
                (
                    [0] => d
                )
        )
)

您注意到即使在取消设置后$a['b']['c']仍然存在。我希望$a只剩下一个值(b)。

在我的实际应用中,我收到以下警告:

  

间接修改MyClass的重载元素无效

MyClass扩展ArrayObject的位置。我有很多代码依赖于能够取消设置这样的嵌套元素,所以我怎样才能使它工作?

4 个答案:

答案 0 :(得分:11)

一种方法

<?php
$a      = new ArrayObject();
$a['b'] = array('c' => array('d'));
$d      =& $a['b'];

unset($d['c']);
print_r($a['b']);

打印:

Array
(
)

为了解释为什么你最初使用的语法不会删除元素,我必须考虑更长的时间。

编辑:行为说明

正在发生的事情是对unset($a['b']['c']);的调用被翻译成:

$temp = $a->offsetGet('b');
unset($temp['c']);

由于$temp$a的副本而不是对它的引用,因此PHP在内部使用copy-on-write并创建第二个数组$temp没有{{}} 1}},但['b']['c']仍然可以。

另一个编辑:可重复使用的代码

所以,无论你采用哪种方式对其进行切片,似乎都要将$a重载为function offsetGet($index)导致麻烦;所以这里是我提出的最短的辅助方法,可以将它作为静态或实例方法添加到function &offsetGet($index)的子类中,无论你的船是什么浮动:

ArrayObject

因此原始代码将成为

function unsetNested(ArrayObject $oArrayObject, $sIndex, $sNestedIndex)
{
    if(!$oArrayObject->offSetExists($sIndex))
        return;

    $aValue =& $oArrayObject[$sIndex];

    if(!array_key_exists($sNestedIndex, $aValue))
        return;

    unset($aValue[$sNestedIndex]);
}

YET另一个编辑:OO解决方案

好的 - 所以我今天早上一直在争抢b / c我在我的代码中发现了一个错误,经过修改后,我们可以基于OO实现一个解决方案。

只是你知道我试过了,扩展段错误..:

$a      = new ArrayObject();
$a['b'] = array('c' => array('d'));

// instead of unset($a['b']['c']);
unsetNested($a, 'b', 'c');
print_r($a['b']);

另一方面,实现装饰器就像魅力一样:

/// XXX This does not work, posted for illustration only
class BadMoxuneArrayObject extends ArrayObject
{
    public function &offsetGet($index)
    {   
        $var =& $this[$index];
        return $var;
    }   
}

现在这段代码可以正常运行:

class MoxuneArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable
{
    private $_oArrayObject;  // Decorated ArrayObject instance

    public function __construct($mInput=null, $iFlags=0, $sIteratorClass='')
    {
        if($mInput === null)
            $mInput = array();

        if($sIteratorClass === '')
            $this->_oArrayObject = new ArrayObject($mInput, $iFlags);
        else
            $this->_oArrayObject = new ArrayObject($mInput, $iFlags, $sIteratorClass);
    } 

    // -----------------------------------------
    // override offsetGet to return by reference
    // -----------------------------------------
    public function &offsetGet($index)
    {
        $var =& $this->_oArrayObject[$index];
        return $var;
    }

    // ------------------------------------------------------------
    // everything else is passed through to the wrapped ArrayObject
    // ------------------------------------------------------------
    public function append($value)
    {
        return $this->_oArrayObject->append($value);
    }

    public function asort()
    {
        return $this->_oArrayObject->asort();
    }

    public function count()
    {
        return $this->_oArrayObject->count();
    }

    public function exchangeArray($mInput)
    {
        return $this->_oArrayObject->exchangeArray($mInput);
    }

    public function getArrayCopy()
    {
        return $this->_oArrayObject->getArrayCopy();
    }

    public function getFlags()
    {
        return $this->_oArrayObject->getFlags();
    }

    public function getIterator()
    {
        return $this->_oArrayObject->getIterator();
    }

    public function getIteratorClass()
    {
        return $this->_oArrayObject->getIteratorClass();
    }

    public function ksort()
    {
        return $this->_oArrayObject->ksort();
    }

    public function natcassesort()
    {
        return $this->_oArrayObject->natcassesort();
    }

    public function offsetExists($index)
    {
        return $this->_oArrayObject->offsetExists($index);
    }

    public function offsetSet($index, $value)
    {
        return $this->_oArrayObject->offsetSet($index, $value);
    }

    public function offsetUnset($index)
    {
        return $this->_oArrayObject->offsetUnset($index);
    }

    public function serialize()
    {
        return $this->_oArrayObject->serialize();
    }

    public function setFlags($iFlags)
    {
        return $this->_oArrayObject->setFlags($iFlags);
    }

    public function setIteratorClass($iterator_class)
    {
        return $this->_oArrayObject->setIteratorClass($iterator_class);
    }

    public function uasort($cmp_function)
    {
        return $this->_oArrayObject->uasort($cmp_function);
    }

    public function uksort($cmp_function)
    {
        return $this->_oArrayObject->uksort($cmp_function);
    }

    public function unserialize($serialized)
    {
        return $this->_oArrayObject->unserialize($serialized);
    }
}

仍然需要修改一些代码..;我没有看到任何方式。

答案 1 :(得分:4)

在我看来,ArrayObject的“重载”括号运算符正在返回嵌套数组的副本,而不是对原始数据的引用。因此,当您调用$a['b']时,您将获得ArrayObject用于存储数据的内部数组的副本。进一步将其解析为$a['b']['c']只是在副本中提供元素“c”,因此在其上调用unset()并不会取消原始元素“c”。

ArrayObject实现the ArrayAccess interface,这实际上允许括号运算符处理对象。 ArrayAccess::offsetGet的文档表明,从PHP 5.3.4开始,可以使用ArrayObject运算符获取=&内部数组中原始数据的引用,{{3}在他的例子中指出。

答案 2 :(得分:1)

您可以使用unset($a->b['c']);代替unset($a['b']['c']);,以防在项目中针对所有相同情况进行此类替换时不会出现大问题

答案 3 :(得分:0)

我似乎有partial solution。如果所有嵌套数组都是unset的实例,则ArrayObject似乎有效。为了确保所有嵌套数组都是ArrayObjects,我们可以从这个类派生出来:

class ArrayWrapper extends ArrayObject {
    public function __construct($input=array(), $flags=ArrayObject::STD_PROP_LIST, $iterator_class='ArrayIterator') {
        foreach($input as $key=>$value) {
            if(is_array($value)) {
                $input[$key] = new self($value, $flags, $iterator_class);
            }
        }
        parent::__construct($input, $flags, $iterator_class);
    }

    public function offsetSet($offset, $value) {
        parent::offsetSet($offset, is_array($value) ? new ArrayWrapper($value) : $value);
    }
}

(针对递归更新;未经测试)

然后,只要您尝试添加嵌套数组,它就会自动转换为ArrayWrapper

不幸的是,许多其他数组函数(例如array_key_exists)在ArrayObjects上不起作用。