【问题标题】:How to unset nested array with ArrayObject?如何使用 ArrayObject 取消设置嵌套数组?
【发布时间】:2012-04-02 18:36:07
【问题描述】:

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。我有很多代码依赖于能够取消设置这样的嵌套元素,那么我怎样才能让它工作呢?

【问题讨论】:

  • 遗憾的是,由于ArrayObject::offsetGet 返回了一个副本,因此您将无法让这些对unset 的调用起作用。不过,我为您留下了下面这些电话的临时替代品,希望它可以为您节省一些时间!
  • 好的,我收回这个,一定是今天早些时候匆忙调试什么的,看看我最新的编辑。

标签: php arrayobject


【解决方案1】:

一种方法

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

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

【讨论】:

  • 我想我在某处读到,只要我们不覆盖offsetGet,它就会返回一个引用,因为ArrayObject 是用C 实现的。可能是Sam 的评论:php.net/manual/en/arrayobject.offsetget.php#79496
  • 这真的很不幸……我有大约 340 次执行这样的 unset 操作,这种设置遍布我的项目。我不确定尝试同时更新它们以及核心框架是否安全。我希望让它与旧的数组语法向后兼容。
  • 还是要把$a = new ArrayObject()这样的电话改成$a = new MoxuneArrayObject();我能做的就是尽量减少必要的改变。
  • 没关系。到目前为止,我只有一个 ArrayObject,它是派生的(MyClass 扩展了 ArrayObject),所以我只需要扩展 MoxuneArrayObject。
  • 我决定放弃尝试将我的数组变成一个类。太多的array_* 函数不能使用它。相反,我正在创建一个包装数组的辅助对象(保留对它的引用)。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我似乎有一个partial solution。如果所有嵌套数组都是ArrayObject 的实例,unset 似乎可以工作。为了确保所有嵌套数组也是 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。

      【讨论】:

        猜你喜欢
        • 2020-01-31
        • 2020-05-02
        • 2020-05-01
        • 2016-02-03
        • 2021-12-05
        • 2015-05-08
        • 1970-01-01
        • 1970-01-01
        • 2016-11-30
        相关资源
        最近更新 更多