我有一个用例,最好使用PHP的Iterator接口方法,next(),current()和valid()来迭代集合.在我的特定情况下,foreach循环对我不起作用.简化的while循环可能看起来像
<?PHP while ($iter->valid()) { // do something with $iter->current() $iter->next(); }
当$iter实现PHP的Iterator接口时,上述代码是否始终有效? PHP的foreach关键字如何处理迭代器?
我问的原因是我写的代码可能会被赋予ArrayIterator或MongoCursor.两者都实现了PHP的Iterator接口,但它们的行为方式不同.我想知道PHP或PHP的Mongo扩展中是否存在错误.
ArrayIterator :: valid()在任何对next()的调用之前返回true – 在创建ArrayIterator之后立即返回true.
MongoCursor :: valid()仅在第一次调用next()后返回true.因此,上面的while循环永远不会执行.
有可能是冗长的,下面的代码演示了这些断言:
<?PHP // Set up array iterator $arr = array("first"); $iter = new \ArrayIterator($arr); // Test array iterator echo(($iter->valid() ? "true" : "false")."\n"); // Echoes true var_dump($iter->current()."\n"); // "first" $iter->next(); echo(($iter->valid() ? "true" : "false")."\n"); // Echoes false // Set up mongo iterator $m = new \Mongo(); $collection = $m->selectDB("iterTest")->selectCollection("mystuff"); $collection->drop(); // Ensure collection is empty $collection->insert(array('a' => 'b')); $miter = $collection->find(); // should find one object // Test mongo iterator echo(($miter->valid() ? "true" : "false")."\n"); // Echoes false $miter->next(); echo(($miter->valid() ? "true" : "false")."\n"); // Echoes true var_dump($miter->current()); // Array(...)
哪种实现是正确的?我发现很少有文档可以支持这两种行为,官方的PHP文档要么模糊不清,要么我读错了. doc for Iterator :: valid()表示:
This method is called after Iterator::rewind() and Iterator::next() to check if the current position is valid.
这表明我的while循环应首先调用next().
然而PHP documentation for Iterator :: next说:
This method is called after each foreach loop.
这表明我的while循环是正确的.
总结一下 – PHP迭代器应该如何表现?
看看the Iterator interface reference page上给出的示例.它显示了PHP的内部实现foreach调用Iterator方法的顺序.特别要注意的是,首次设置foreach时,第一个调用是rewind().这个例子虽然没有很好地注释,但却是我答案的基础.
我不确定为什么MongoCursor在调用next()之前不会为valid()返回true,但是你应该能够通过在循环之前调用rewind()来重置任何类型的对象.所以你会:
// $iter may be either MongoCursor or ArrayIterator $iter->rewind(); while( $iter->valid() ){ // do something with $iter->current() $iter->next(); }
我相信这对你有用.如果没有,Mongo类可能会有错误.
编辑:Mike Purcell的答案正确地指出ArrayIterator和Iterator是不一样的.但是,ArrayIterator实现了Iterator,因此您应该能够使用rewind(),就像我在其中任何一个上面所示.