обработка исключений — PHP SeekableIterator: поймать OutOfBoundsException или проверить метод valid ()?

Поэтому я не уверен, является ли это ошибочным дизайном с PHP или есть понятная логика для обработки противоречивых результатов для одного и того же интерфейса.

Интерфейс SeekableIterator имеет два метода (seek а также valid) которые либо находятся в конфликте друг с другом, либо должны работать последовательно друг с другом, но я вижу и то, и другое.

В документации по интерфейсу сказано, что seek должен выдать исключение класса OutOfBoundsException, но это, кажется, сводит на нет полезность valid если позиция итератора не обновляется valid верните false), прежде чем выдать исключение (которое, очевидно, должно быть перехвачено).

Три тестовых примера

Пример 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() подтвердить правильную позицию после seek($position) в то же время ожидая, что seek() может выдать исключение вместо обновления позиции, так что valid() возвращает истину?

4

Решение

Кажется, что directoryIterator::seek() Метод здесь не реализован за исключением. Вместо этого он просто не вернет значение и позволит valid() справиться.

Ваш другой пример, ArrayObject::seek() работает «правильно» и бросает OutOfBoundsException,

Аргументация проста: ArrayObject (и, скорее всего, большинство пользовательских реализаций также) будут заранее знать, сколько элементов в нем содержится, и, таким образом, смогут быстро проверить его границы. DirectoryIterator однако, чтобы получить заданную позицию, необходимо прочитать объекты каталога с диска по одному. Это буквально вызывает valid() а также next() в петле. Это причина, почему key() изменился, и valid() возвращается 0,

Другие итераторы даже не будут касаться текущего состояния итератора и могут быстро решить, попадает ли ваш запрос в его диапазон или нет.

О заметке: если вы хотите искать позицию в DirectoryIterator в обратном направлении, он сначала сбросит итератор, а затем снова начинает итерацию каждого элемента. Так что, если вы находитесь на позиции 1000, и сделать $it->seek(999), он будет фактически повторять 999 элементов снова.

ИМХО DirectoryIterator не очень хорошая реализация seekableIterator интерфейс. Он предназначен для быстрого перехода к определенному элементу внутри итератора и, очевидно, с помощью directoryIterator это не то, что выполнимо. Вместо этого необходимо выполнить полную итерацию, что приведет к изменению состояния итератора.

seekableIterator Интерфейс полезен для filterIterators, которые делают что-то с диапазоном итератора. В пределах SPL это только LimitIterator, Когда вы делаете:

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

Когда limitIterator обнаруживает, что данный итератор реализовал seekableIterator интерфейс, он будет вызывать seek() быстро перейти к 5-му элементу, иначе он будет просто повторяться, пока не достигнет 5-го элемента.

Вывод: не используйте seekableIterator когда вы не можете быстро перейти к позиции или проверить границы. В лучшем случае вы ничего не получаете, в худшем — итераторы, которые меняют состояние, не зная почему.

Чтобы ответить на ваш вопрос: seek() должен выдать исключение и не менять состояние. directoryIterator (может быть, некоторые другие тоже) должны быть изменены или не реализованы seekableIteratorили узнав, сколько записей существует до seek() (но это не исправляет «перемотку» при поиске обратной задачи).

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]