因此,我不确定这是否是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()返回true?
发布于 2015-05-30 18:32:00
这里的directoryIterator::seek()方法似乎不是用异常实现的。相反,它将不返回值,并让valid()处理它。
另一个例子是,ArrayObject::seek()“正确”地工作,并抛出一个OutOfBoundsException。
理由很简单:ArrayObject (最有可能,也是大多数定制实现)将事先知道它包含多少个元素,因此可以快速检查其边界。但是,DirectoryIterator必须逐个从磁盘读取目录实体,才能到达给定的位置。它通过在循环中调用valid()和next()来做到这一点。这就是为什么key()发生了变化,valid()返回0的原因。
其他迭代器甚至不会触及当前的迭代器状态,并且可以快速确定您的请求是否在其范围内。
在sidenote上:如果要向后查找DirectoryIterator中的位置,它将首先重置迭代器,然后再开始迭代每个元素。因此,如果您在位置1000上,并执行一个$it->seek(999),它实际上将再次迭代999元素。
IMHO,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()之前有多少个条目(但这并不能解决“反向问题”)。
https://stackoverflow.com/questions/30398762
复制相似问题