我知道在通过引用循环时不应该修改数组的物理结构,但是我需要解释代码中发生了什么。我们走了:
$x= [[0],[1],[2],[3],[4]];
foreach ($x as $i => &$upper) {
print $i;
foreach ($x as $j => &$lower) {
if($i == 0 && $j == 2) {
unset($x[2]);
} else if($i == 1 && $j == 3) {
unset($x[3]);
}
}
}
输出为01
。令人惊讶的是,对于索引0
和1
,外循环仅迭代两次。我期待输出为014
。
我已经阅读了很多关于使用数组引用的危险的博客文章和问题,但没有任何东西可以解释这种现象。我现在已经打了好几个小时了。
上面的代码是最小的可重现代码。一个似乎是这种情况的解释(但不正确的一个)是这样的:
在内部指针设置为索引2
之前,外部循环经历两次迭代。但是循环在索引2
找不到任何元素,因此认为没有元素留下并退出。
这个理论的问题是它没有完全解释这个代码:
$x= [[0],[1],[2],[3],[4]];
foreach ($x as $i => &$upper) {
print $i;
foreach ($x as $j => &$lower) {
if($i == 0 && $j == 2) {
unset($x[2]);
// No if else here
unset($x[3]);
}
}
}
出于同样的原因,上面的代码也应该生成01
,但它的实际输出是014
,正如预期的那样。即使删除了一系列中的两个项目,php也知道仍然需要迭代的元素。这可能是php脚本引擎的错误吗?
答案 0 :(得分:6)
重现问题的简单代码:
$x = [0, 1, 2];
foreach ($x as $k => &$v) {
print $k;
if ($k == 0) {
unset($x[1]);
}
end($x); // move IAP to end
next($x); // move IAP past end (that's the same as foreach ($x as $y) {} would do)
}
如果你对一个数组进行预处理,那么它就会被复制(=迭代时没问题,你将迭代整个原始数组)。 但是,如果您通过引用进行预处理,则数组不被复制(引用需要与原始数组匹配,因此无法复制)。
Foreach内部始终保存下一个元素的位置以进行迭代。 但是当删除数组的下一个位置时,foreach需要返回到数组并检查它的内部数组指针(IAP)。
在这种情况下,下一个位置被销毁并且 IAP超过了结束,它结束了循环。
这就是你在这里看到的。
同样有趣:hhvm在这里与php有不同的行为:http://3v4l.org/81rl8
附录:无限的foreach循环:
$x = [0,1,2];
foreach ($x as $k => &$v) {
print $k;
if ($k == 1) {
unset($x[2]);
} else {
$x[2] = 1;
}
reset($x);
}
如果您理解我上面的解释,请猜出为什么会无限循环。