【问题标题】:ArrayObject doesn't allow me to unset a value, while iterating over itArrayObject 不允许我在迭代时取消设置值
【发布时间】:2013-01-04 05:45:10
【问题描述】:

我收到了这条通知:

ArrayIterator::next(): 数组在对象外部被修改,内部位置在 /var/www 中不再有效...

这是由这段代码在foreach循环开始时产生的。连同通知一起,foreach 循环重新开始迭代。换句话说,只要发生这种情况,内部位置就会被重置。但是根据php手册,ArrayObject默认使用ArrayIterator。

手册上说 ArrayIterator 是这样的

此迭代器允许在迭代数组和对象时取消设置和修改值和键。

我在这里遗漏了什么吗?我发现了一些关于 ArratIterator 的错误报告,但不是这种。是bug还是我的错?

版本:PHP 版本 5.3.10-1ubuntu3.4

<?php
//file 1:
// no namespace
abstract class holder extends \ArrayObject{
    // abstract function init();

    public function __construct($init){
        parent::__construct($init, 1);
    }
}?>

<?php
//file 2:
namespace troops;
class holder extends \holder{
    public function __construct(){
        parent::__construct($this->init());
    }

    private function init(){
        return array( /*... some data from db ...*/ );
    }

    public function saveData(){
        foreach($this as $k  => $v){
            $this->save($v);
            if($v->number_of_items==0) {
                unset($k);
                // $this->offsetUnset($k); // tryed both 
            }
        }
    }
}
?>

【问题讨论】:

    标签: php spl arrayobject


    【解决方案1】:

    ArrayObject 实现了IteratorAggregate,这意味着它有一个名为getIterator() 的方法,该方法返回一个迭代器。

    php 的 foreach 循环将通过调用 getIterator() 方法自动检索迭代器,以便您检索要迭代的迭代器。这很方便,但是您需要获取对该迭代器的引用才能在迭代器本身上调用offsetUnset() 方法。这里的关键是你必须调用 iterators offsetUnset() 方法,而不是 ArrayObjects offsetUnset() 方法。

    $ao = new ArrayObject();
    $ao[] = 9;
    $iter = $ao->getIterator();
    
    foreach ($iter as $k => $v) 
        $iter->offsetUnset($k); // no error
    

    迭代器正在迭代的底层 ArrayObject 发生变异,因此如果您在同一个 Arrayobject 上同时有多个活动迭代器,您仍然会遇到相同的错误。

    这样做的原因可能是迭代器可以节省内存,而不必复制底层的 ArrayObject,因为副本是处理确定当前迭代器位置的复杂性的唯一简单解决方案。被添加到底层数组或从底层数组中删除。

    【讨论】:

    • 我明白了,它确实有效。但这有点奇怪。我希望 ArrayObject 自动调用迭代器的方法。
    【解决方案2】:

    如果您从数组中删除最后一条记录,则下一个 foreach 循环可能会失败(内部调用 $this->next() ) 例如,当我有一个项目的数组并且我将取消设置它时,下一个 foreach 会失败。

    所以它帮助我测试了有效性并在这种情况下打破了下一个循环。

    deleteOffset = true;
    
    foreach ($iterator as $key => $value)
        {
                if ($deleteOffset && $iterator->offsetExists($key) ) 
                {
                     $iterator->offsetUnset($key);                                       
                }                
    
            //if remove last record than the foreach ( $this->next() ) fails so 
            //we have to break in this case the next ->next call
            //after any ->offsetUnset calls
    
            if (!$iterator->valid()) break;
    
        }
    

    【讨论】:

    • 谢谢!这让我烦恼了一段时间,构建了一个特定的 if 语句来处理它,但这解决了它
    【解决方案3】:

    这花了我这么长时间!我使用此页面上的配方(未成功)清除 ArrayObject 存储的内容,然后我发现了这个小笨蛋..

    $this->exchangeArray(array());
    

    魔法!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-30
      • 1970-01-01
      • 1970-01-01
      • 2010-10-22
      • 2017-08-30
      • 1970-01-01
      • 1970-01-01
      • 2018-11-16
      相关资源
      最近更新 更多