4

私はこの通知を受け取りました:

ArrayIterator :: next():配列がオブジェクトの外部で変更され、内部位置が/ var /www..で無効になりました。

これは、foreachループの開始時にこのコードによって生成されます。通知とともに、foreachループが繰り返し開始されます。つまり、このようなことが起こると、内部位置がリセットされます。しかし、phpマニュアルによると、ArrayObjectはデフォルトでArrayIteratorを使用しています。

そしてマニュアルはArrayIteratorについてこれを言います

このイテレータを使用すると、配列とオブジェクトを反復処理しながら、値とキーの設定を解除して変更できます。

ここで何かが足りませんか?ArratIteratorに関するバグレポートをいくつか見つけましたが、この種のものは見つかりませんでした。それはバグですか、それとも私の悪いですか?

バージョン: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 
            }
        }
    }
}
?>
4

3 に答える 3

8

ArrayObjectはIteratorAggregateを実装します。これは、イテレータを返すgetIterator()というメソッドがあることを意味します。

phpのforeachループは、getIterator()メソッドを呼び出してイテレーターを自動的に取得し、イテレーターを取得して繰り返し処理します。これは便利ですが、イテレータ自体でoffsetUnset()メソッドを呼び出すには、このイテレータへの参照を取得する必要があります。ここで重要なのは、ArrayObjects offsetUnset()メソッドではなく、イテレータoffsetUnset()メソッドを呼び出す必要があるということです。

$ao = new ArrayObject();
$ao[] = 9;
$iter = $ao->getIterator();

foreach ($iter as $k => $v) 
    $iter->offsetUnset($k); // no error

イテレータが反復処理している基になるArrayObjectは変更れるため、同じArrayobjectに対して同時に複数のアクティブなイテレータがある場合でも、同じエラーが発生します。

これの理論的根拠は、イテレータがメモリ効率が高く、基になるArrayObjectをコピーする必要がないためである可能性があります。これは、コピーが、物事が追加されたときに現在のイテレータの位置を決定する複雑さに対処するための唯一の単純なソリューションだからです。または、基になるアレイから削除されます。

于 2013-01-10T21:50:25.450 に答える
1

配列から最後のレコードを削除すると、次のforeachループが失敗する可能性があります(内部で$ this-> next()を呼び出します)。たとえば、1つのアイテムの配列があり、設定を解除すると、次の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;

    }
于 2015-03-18T06:45:05.430 に答える
0

これにはとても時間がかかりました!このページのレシピを使用して、ArrayObjectのストレージの内容を(失敗して)クリアしたところ、この小さなディディが見つかりました。

$this->exchangeArray(array());

魔法!

于 2016-12-29T19:41:26.673 に答える