循环后您对 Itterator 的实现中的指针不正确,因为您没有设置/重置从 foreach 循环最后一次调用 valid() 的值,它保留了数组外部的最后一个增量值边界(在本例中为 3 ),这里最简单的做法是设置指针 null 以便它反映 PHP 的行为(在迭代器中使用本机数组函数时会得到什么,稍后会详细介绍)
有一点需要注意,正如您指出的那样,PHP7 发生了变化
注意:在 PHP 5 中,当 foreach 第一次开始执行时,内部
数组指针自动重置为第一个元素
大批。这意味着您不需要在执行之前调用 reset()
foreach 循环。由于 foreach 依赖于 PHP 中的内部数组指针
5、在循环内更改它可能会导致意外行为。在 PHP 中
7、foreach不使用内部数组指针。
我个人认为这不是一个大问题,除非您在内部指针上进行中继。以我的经验,这不会发生太多。
现在修复您的迭代器实现:
选项 1:
让 PHP 使用原生数组函数来处理跟踪指针:
class myIterator implements Iterator {
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);
public function __construct() {
reset( $this->array );
}
public function rewind() {
reset( $this->array );
}
public function current() {
return current( $this->array );
}
public function key() {
return key( $this->array );
}
public function next() {
next( $this->array );
}
public function valid() {
return isset( $this->array[$this->key()] );
}
//alias of key()
public function showPosition() {
return $this->key();
}
}
echo str_pad('= NATIVE TRACKED POINTER =', 60, '=') ."\n";
$it = new myIterator;
$it->next(); //move to index 1
var_dump($it->key()); //<-- should print 1
foreach($it as $key => $value) {}
var_dump($it->key()); //<--shows NULL,
echo str_pad('= NATIVE ARRAY POINTER =', 60, '=') ."\n";
$array = array(
"firstelement",
"secondelement",
"lastelement",
);
next($array); //move to index 1
var_dump(key($array)); //<-- should print 1
foreach( $array as $key => $value ){}
var_dump(key($array)); //<-- NULL (PHP < 7), 1 PHP 7+
这个输出(在 PHP7.0.1 中)
= NATIVE TRACKED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL
这个输出(在 PHP5.6.29 中)
= MANUAL TRACKED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL
选项2:
确保在$myIterator->valid()' when the pointer is out of bounds. (This should be done after the call tovalid()you could check on next, by setting the pointer toNULL`中重置$position,我们至少可以模拟使用本机指针跟踪时的行为)
class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);
public function __construct() {
$this->position = 0;
}
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() {
$valid = isset($this->array[$this->position]);
if(!$valid){
$this->position = null;
}
return $valid;
}
public function showPosition() {
return $this->position;
}
}
echo str_pad('= MANUAL TRACKED POINTER =', 60, '=') ."\n";
$it = new myIterator;
$it->next(); //move to index 1
var_dump($it->key()); //<-- should print 1
foreach($it as $key => $value) {}
var_dump($it->key()); //<--shows NULL (PHP < 7),
echo str_pad('= NATIVE ARRAY POINTER =', 60, '=') ."\n";
$array = array(
"firstelement",
"secondelement",
"lastelement",
);
next($array); //move to index 1
var_dump(key($array)); //<-- should print 1
foreach( $array as $key => $value ){}
var_dump(key($array)); //<-- NULL (PHP < 7), 1 PHP 7+
对于 Option2,我将其设置为反映 PHP Iterator 接口没有简单的方法。
http://sandbox.onlinephpfunctions.com/code/2d38af15e5b0269dcdd341d0a77b601ce2713cce
这个输出(在 PHP7.0.1 中)
= MANUAL TRACKED POINTER ===================================
int(1)
int(0)
= NATIVE ARRAY POINTER =====================================
int(1)
int(1)
这个输出(在 PHP5.6.29 中)
= MANUAL TRACKED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL
更新
现在,如果您想完全模拟 PHP7 的行为,则需要做更多的工作。 (我相信可能还有其他方法可以做到这一点,但这是我想出的方法)
class myIterator implements IteratorAggregate{
protected $innerIterator = [];
public function __construct(array $array = []){
$this->innerIterator = new myInnerIterator($array,$this);
}
public function getIterator(){
$this->innerIterator->cachePointer();
return $this->innerIterator;
}
public function seek($index){
$this->innerIterator->seek($index);
}
public function rewind() {
$this->innerIterator->rewind();
}
public function current() {
return $this->innerIterator->current();
}
public function key() {
return $this->innerIterator->key();
}
public function next() {
$this->innerIterator->next();
}
public function valid() {
return $this->innerIterator->valid();
}
}
class myInnerIterator implements SeekableIterator{
protected $array;
protected $pointer_cache = null;
public function __construct( array $array = [], $wrapper){
if( !is_a($wrapper, 'myIterator') ) throw new Exception('myInnerIterator can only be constructed by myIterator');
$this->array = new ArrayIterator($array);
}
public function cachePointer(){
$this->pointer_cache = $this->key();
}
public function seek($index){
$this->array->seek($index);
}
public function rewind() {
$this->array->rewind();
}
public function current() {
return $this->array->current();
}
public function key() {
return $this->array->key();
}
public function next() {
$this->array->next();
}
public function valid() {
$valid = $this->array->valid();
if(!$valid && $this->pointer_cache ){
if( defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 70000 )
$this->seek( $this->pointer_cache );
$this->pointer_cache = null;
}
return $valid;
}
}
echo str_pad('= CACHED POINTER =', 60, '=') ."\n";
$array = array(
"firstelement",
"secondelement",
"lastelement",
);
$it = new myIterator($array);
$it->next(); //move to index 1
var_dump($it->key()); //<-- should print 1
foreach($it as $key => $value) {}
var_dump($it->key()); //<--shows NULL,
echo str_pad('= NATIVE ARRAY POINTER =', 60, '=') ."\n";
next($array); //move to index 1
var_dump(key($array)); //<-- should print 1
foreach( $array as $key => $value ){}
var_dump(key($array)); //<-- NULL (PHP < 7), 1 PHP 7+
然后输出
这个输出(在 PHP7.0.1 中)
= CACHED POINTER ===================================
int(1)
int(1)
= NATIVE ARRAY POINTER =====================================
int(1)
int(1)
这个输出(在 PHP5.6.29 中)
= CACHED POINTER ===================================
int(1)
NULL
= NATIVE ARRAY POINTER =====================================
int(1)
NULL
这是一个沙盒,您可以在其中进行测试
http://sandbox.onlinephpfunctions.com/code/66dc26003347ec40184bb0283e78a3d67e86c51a
这种设置方式你永远不必接触myInnerIterator 类,因为它包含在myIterator 中,事实上,如果你尝试创建它而不向它传递myIterator 的实例,它会抛出一个异常。诀窍是您需要IteratorAggregate::getIterator,它在foreach 启动时调用,然后是SeekableIterator::seek,位置的缓存也必须在InnerIterator 中完成。至于还有什么会自动调用getIterator 以及所有这些会影响什么,我真的不知道。
无论如何希望对你有所帮助,或者给你一些想法。