PHP SeekableTerator:捕获边界异常还是检查有效()方法?
所以我不确定这是否是PHP的错误设计,或者是否有一个可以理解的逻辑来处理同一接口的不一致结果 SeekableIterator接口有两种方法(PHP SeekableTerator:捕获边界异常还是检查有效()方法?,php,exception-handling,spl,seekableiterator,Php,Exception Handling,Spl,Seekableiterator,所以我不确定这是否是PHP的错误设计,或者是否有一个可以理解的逻辑来处理同一接口的不一致结果 SeekableIterator接口有两种方法(seek和valid),它们要么相互冲突,要么应该一致地工作,但我看到了这两种方法 接口的文档说明seek应该抛出类OutOfBoundsException的异常,但这似乎否定了valid的有用性,除非在抛出异常(显然必须捕获)之前更新迭代器位置(使valid返回false) 三个测试示例 例1。 实现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。测试:
测试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
那么,人们如何合理地期望知道在
seek($position)
之后是否使用valid()
来确认有效位置,同时也期望seek()
可能会抛出异常而不是更新位置,从而valid()
返回true?这里的directoryIterator::seek()
方法似乎没有实现异常。相反,它将不返回任何值,并让valid()
处理它
您的另一个示例是ArrayObject::seek()
确实“正确”工作,并抛出边界外异常
推理很简单:ArrayObject
(很可能也是大多数自定义实现)会事先知道它包含多少元素,因此可以快速检查其边界。然而,DirectoryIterator
必须逐个从磁盘读取目录实体,才能到达给定位置。它通过在循环中直接调用valid()
和next()
来实现。这就是为什么键()
已更改,并且valid()
返回0
的原因
其他迭代器甚至不会触及当前迭代器状态,并且可以快速确定您的请求是否在其范围内
旁注:如果您想向后搜索DirectoryIterator中的位置,它将首先重置迭代器,然后再次开始迭代每个元素。因此,如果您位于位置1000,并执行$it->seek(999)
,它实际上会再次迭代999个元素
总之,directoryinterator
不是seekableIterator
接口的良好实现。它旨在快速跳转到迭代器中的某个元素,显然,对于directoryIterator,这是不可行的。相反,必须进行完整的迭代,这将导致迭代器状态发生变化
seekableIterator
接口对于使用迭代器范围执行某些操作的Filtererator非常有用。在SPL中,这只是LimitIterator
。当您这样做时:
$it = new ArrayIterator(range('a','z'));
$it = new LimitIterator($it, 5, 10));
当limitIterator检测到给定的迭代器已经实现了SeekableTerator
接口时,它将调用seek()
快速跳转到第5个元素,否则它将只进行迭代,直到到达第5个元素
结论:当您无法快速跳转到某个位置或检查边界时,不要使用seekableIterator
。在最好的情况下,您什么也得不到,在最坏的情况下,您得到的迭代器会在不知道原因的情况下更改状态
要回答您的问题:seek()
应该引发异常,而不是更改状态。directoryIterator
(可能还有其他一些)应该更改为不实现seekableIterator
,或者通过查找seek()
(但这并不能解决向后查找时的“倒带”问题)。非常好,谢谢。就我个人而言,我认为抛出异常不应该成为常态,但您关于directoryIterator为什么是SeekableTerator的一个糟糕用例的解释是一个有力的反证,并指出了为什么应该将其作为一个特殊异常处理,而不是重新配置接口,使其不承担为接口抛出的异常。更具体地说,我认为,为特定接口抛出异常不应该成为常态,因为它会增加关于何时依赖valid
方法与何时假设异常的混淆。通过抛出异常并更新位置来改变迭代器的状态是否会更混乱/更具破坏性,从而使valid
返回false?不。我认为这是可能的,但是,我不知道这是否会被视为BC中断,并会在userland代码中引起任何问题(尽管如此,该代码可能太脆弱而无法开始)。也许这是对PHP7的一个很好的修复。@Anthony这实际上是设计的行为,正如a所述。但是,我认为应该有一个异常。@Anthony这是!在PHP5.6.15及更高版本中,DirectoryIterator
将抛出一个异常,就像其他SeekableTerator
实现一样。
$it = new ArrayIterator(range('a','z'));
$it = new LimitIterator($it, 5, 10));