4

したがって、これが PHP のバグのある設計なのか、それとも同じインターフェイスで一貫性のない結果を処理するための理解されたロジックがあるのか​​はわかりません。

SeekableIterator インターフェイスには 2 つのメソッド (seekおよびvalid) があり、これらは互いに競合しているか、互いに一貫して動作するはずですが、両方とも表示されます。

インターフェイスのドキュメントにはseek、クラス OutOfBoundsException の例外をスローする必要があると記載されていますが、これは、例外をスローする前validにイテレーターの位置が更新されない限りvalid(明らかに false を返すように)、の有用性を無効にしているようです (これは明らかにキャッチする必要があります)。

3 つのテスト例

例 1。

ドキュメントの例で提供されているように、SeekableIterator を実装するカスタム クラス:

クラス:

class MySeekableIterator implements SeekableIterator {

    private $position;

    private $array = array(
        "first element",
        "second element",
        "third element",
        "fourth element"
    );

    /* Method required for SeekableIterator interface */

    public function seek($position) {
        if (!isset($this->array[$position])) {
            throw new OutOfBoundsException("invalid seek position ($position)");
        }

        $this->position = $position;
    }

    /* Methods required for Iterator interface */

    public function rewind() {
        $this->position = 0;
    }

    public function current() {
        return $this->array[$this->position];
    }

    public function key() {
        return $this->position;
    }

    public function next() {
        ++$this->position;
    }

    public function valid() {
        return isset($this->array[$this->position]);
    }
}

例 1. テスト:

echo PHP_EOL . "Custom Seekable Iterator seek Test" . PHP_EOL;

$it = new MySeekableIterator;

$it->seek(1);
try {
    $it->seek(10);
    echo $it->key() . PHP_EOL;
    echo "Is valid? " . (int) $it->valid() . PHP_EOL;
} catch (OutOfBoundsException $e) {
    echo $e->getMessage() . PHP_EOL;
    echo $it->key() . PHP_EOL; // outputs previous position (1)
    echo "Is valid? " . (int) $it->valid() . PHP_EOL;
}

テスト 1 の出力:

Custom Seekable Iterator seek Test
invalid seek position (10)
1
Is valid? 1

例 2:

ネイティブ ArrayIterator::seek の使用

テスト 2 コード:

echo PHP_EOL . "Array Object Iterator seek Test" . PHP_EOL;

$array = array('1' => 'one',
               '2' => 'two',
               '3' => 'three');

$arrayobject = new ArrayObject($array);
$iterator = $arrayobject->getIterator();

$iterator->seek(1);
try {
    $iterator->seek(5);
    echo $iterator->key() . PHP_EOL;
    echo "Is valid? " . (int) $iterator->valid() . PHP_EOL;
} catch (OutOfBoundsException $e) {
    echo $e->getMessage() . PHP_EOL;
    echo $iterator->key() . PHP_EOL;  // outputs previous position (1)
    echo "Is valid? " . (int) $iterator->valid() . PHP_EOL;
}

テスト 2 の出力:

Array Object Iterator seek Test
Seek position 5 is out of range
1
Is valid? 1

例 3:

ネイティブ DirectoryIterator::seek の使用

テスト 3 コード:

echo PHP_EOL . "Directory Iterator seek Test" . PHP_EOL;

$dir_iterator = new DirectoryIterator(dirname(__FILE__));
$dir_iterator->seek(1);
try {
    $dir_iterator->seek(500);  // arbitrarily high seek position
    echo $dir_iterator->key() . PHP_EOL;
    echo "Is valid? " . (int) $dir_iterator->valid() . PHP_EOL;
} catch (OutOfBoundsException $e) {
    echo $e->getMessage() . PHP_EOL;
    echo $dir_iterator->key() . PHP_EOL;
    echo "Is valid? " . (int) $dir_iterator->valid() . PHP_EOL;
}

テスト 3 の出力:

Directory Iterator seek Test
90
Is valid? 0

valid()では、有効な位置を確認するために使用するかどうかを合理的に知るにはどうすればよいでしょうか。また、位置を更新する代わりに Exception がスローされる可能性がseek($position)あることを予測して、true を返すのでしょうか?seek()valid()

4

1 に答える 1

4

こちらのdirectoryIterator::seek()方法は例外で実装されていないようです。代わりに、値を返さず、valid()処理させます。

あなたの他の例では、ArrayObject::seek()「正しく」動作し、OutOfBoundsException.

理由は簡単です: ArrayObject(そしてほとんどの場合、ほとんどのカスタム実装も) 含まれる要素の数を事前に知っているため、その境界をすばやく確認できます。ただし、指定された位置に到達するにはDirectoryIterator、ディレクトリ エンティティをディスクから 1 つずつ読み取る必要があります。これは、文字通りvalid()andnext()をループで呼び出すことによって行われます。key()これがが変更され、 をvalid()返す理由0です。

他のイテレーターは現在のイテレーターの状態に触れず、リクエストがその範囲内にあるかどうかをすぐに判断できます。

補足: DirectoryIterator 内の位置を逆方向にシークする場合は、最初に反復子をリセットしてから、各要素の反復を再度開始します。したがって、位置 1000 で を実行すると$it->seek(999)、実際には 999 個の要素が再度反復されます。

IMHO、インターフェイスDirectoryIteratorの適切な実装ではありません。seekableIteratorイテレータ内の特定の要素にすばやくジャンプすることを目的としていますが、ディレクトリイテレータを使用すると、これは実行可能ではありません。代わりに、完全な反復を実行する必要があり、その結果、反復子の状態が変更されます。

このseekableIteratorインターフェースは、反復子の範囲で何かを行う filterIterators に役立ちます。SPL 内では、これはLimitIterator. あなたがするとき:

$it = new ArrayIterator(range('a','z'));
$it = new LimitIterator($it, 5, 10));

limitIterator は、指定されたイテレーターがseekableIteratorインターフェースを実装したことを検出すると、5 番目の要素にすばやくジャンプするように呼び出しますseek()。それ以外の場合は、5 番目の要素に到達するまで反復します。

結論:seekableIteratorある位置にすばやくジャンプしたり、境界を確認したりできない場合は、 を使用しないでください。せいぜい何も得られず、最悪の場合、理由を知らずに状態を変更するイテレータを取得します。

あなたの質問に答えるにseek()は、例外をスローし、状態を変更しないでください。(おそらく他のdirectoryIteratorいくつかも)を実装しないように変更するかseekableIterator、前にいくつのエントリがあるかを調べる必要がありますseek()(ただし、逆方向の問題を探すときの「巻き戻し」は修正されません)。

于 2015-05-30T18:32:00.013 に答える