8

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

4 回答 4

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 在内部使用写时复制并创建第二个数组,其中$temp没有['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]);
}

所以原来的代码会变成

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

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

另一个编辑:OO 解决方案

好的 - 所以我今天早上一定是在争先恐后地 b/c 我在我的代码中发现了一个错误,当修改时,我们可以实现一个基于 OO 的解决方案。

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

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

现在此代码可以按需要工作:

$a      = new MoxuneArrayObject();
$a['b'] = array('c' => array('d'));
unset($a['b']['c']);
var_dump($a);

不过还是要修改一些代码..;我看不出有什么办法。

于 2012-04-02T18:48:15.033 回答
4

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

ArrayObject实现ArrayAccessinterface,这实际上是允许括号运算符在对象上工作的。的文档ArrayAccess::offsetGet表明,从 PHP 5.3.4 开始,ArrayObject可以使用=&运算符获取对内部数组中原始数据的引用,如示例中所示的quickshiftin

于 2012-04-02T19:06:54.760 回答
1

如果对项目中的所有相同情况进行此类替换不会有大问题,您可以使用unset($a->b['c']);而不是unset($a['b']['c']);

于 2014-03-13T21:33:19.183 回答
0

我似乎有一个部分解决方案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。

于 2012-04-02T23:09:07.093 回答