以下是PHP文档中有关Iterator的代码,其中添加了几行以显示位置。
如您所见,该对象有3个元素,位置位于foreach()之前的第2个元素($ this-> position = 1),
在foreach()之后,位置变为无效值($ this-> position = 3)。
class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);
public function __construct() {
$this->position = 0;
}
public function rewind() {
var_dump(__METHOD__);
$this->position = 0;
}
public function current() {
var_dump(__METHOD__);
return $this->array[$this->position];
}
public function key() {
var_dump(__METHOD__);
return $this->position;
}
public function next() {
var_dump(__METHOD__);
++$this->position;
}
public function valid() {
var_dump(__METHOD__);
return isset($this->array[$this->position]);
}
public function showPosition() {
return $this->position;
}
}
$it = new myIterator;
$it->next();
var_dump($it->showPosition()); //shows 1
foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n"; }
var_dump($it->showPosition()); //shows 3 which is an invalid value.
在显示的foreach() doccument中:
注意:在PHP 5中,....在PHP 7中,foreach不使用内部数组指针。
我使用PHP7,显然上面的示例代码显示内部点在foreach()之后发生了变化。
我的问题是 - 是否可以在foreach()之后保留原始位置?
我理解一种可能的方法是添加变量以记住foreach()之前和foreach()之后的位置,手动设置位置。但它似乎与foreach()doc建议的内容相矛盾。
答案 0 :(得分:0)
循环后你的Itterator实现中的指针是不正确的,因为你没有设置/重置最后一次调用valid()
的值来自foreach循环它保留的最后一个增量值数组边界(在本例中为3
),这里最简单的方法是设置指针null
,这样就反映了PHP的行为,(在迭代器中使用本机数组函数时得到的结果) ,稍后会详细说明)
有一点需要注意,正如您所指出的那样,PHP7有一个变化
注意:在PHP 5中,当foreach首次开始执行时,内部 数组指针自动重置为第一个元素 阵列。这意味着您不需要在a之前调用reset() foreach循环。因为foreach依赖于PHP中的内部数组指针 5,在循环内更改它可能会导致意外行为。在PHP中 7,foreach不使用内部数组指针。
我个人认为这不是一个大问题,除非你继续使用内部指针。根据我的经验,这并没有发生太多。
现在修复Iterator的实现:
选项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:
确保重置$position
{(1}}有效()$myIterator->valid()' when the pointer is out of bounds. (This should be done after the call to
NULL中的you could check on next, by setting the pointer to
我们至少可以模拟使用本机指针跟踪获得的行为。
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的本机行为&lt; 7,因为你手动跟踪数组指针,所以只用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
以及所有会影响我的东西,我真的不知道。
无论如何希望有所帮助,或者给你一些想法。