Php foreach by reference循环中unset的奇怪行为
我知道在引用循环时不应该修改数组的物理结构,但我需要解释代码中发生了什么。我们开始:Php foreach by reference循环中unset的奇怪行为,php,foreach,reference,Php,Foreach,Reference,我知道在引用循环时不应该修改数组的物理结构,但我需要解释代码中发生了什么。我们开始: $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 i
$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脚本引擎的一个bug吗?一个简单的代码来重现您的问题:
$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,则不会复制数组(引用需要与原始数组匹配,因此无法复制)
Foreach在内部总是保存下一个要迭代的元素的位置。
但是当数组的下一个位置被移除时,foreach需要返回数组并检查其内部数组指针(IAP)
在这种情况下,下一个位置被破坏,IAP通过末端,结束循环
这就是你在这里看到的
同样有趣的是:hhvm的行为与php不同:
附录:无限foreach循环:
$x = [0,1,2];
foreach ($x as $k => &$v) {
print $k;
if ($k == 1) {
unset($x[2]);
} else {
$x[2] = 1;
}
reset($x);
}
如果你理解我上面的解释,猜猜为什么会无限期地循环。那么你期待什么?添加一些调试输出。将一个
var\u dump($x)
放入代码的各个部分,您将确切地看到数组发生了什么。问题是您在foreach期间未设置值,并导致php中断,因为foreach在其循环期间被修改。我会重新考虑您的逻辑,或者复制您取消设置的数组,或者尝试其他操作。@nl-x请参阅我的编辑。到目前为止,我们所讨论的任何理论都无法解释这个案例。@juzerali我同意你的观点。奇怪的是,您说foreach使用自己的指针,当该指针失败时,它使用数组内部指针。但是我同意OP的编辑不应该也打印'4',但它确实…@nl-x哪一个是你的问题?如果在$i==0时删除2和3?好的,在这里你不需要删除下一个要迭代的条目,但是只需要删除一个,所以没问题。重要位置总是下一个位置,其他所有位置都从(在本例中不是复制的)数组中重新获取。如果我遵循您的逻辑,当我“在i0j2删除2和3”时,外部foreach应该完成0,通过1并运行到缺少的“2”。然后查看内部数组指针,并在数组末尾看到它(上一个循环(1)将其关闭的地方)@nl-x no.foreach将1存储为下一个键。打印0。将4存储为下一个键。打印1。将5存储为下一个键。印刷品4。查看数组的结尾。印刷品5。退出循环。当存储的键在它想要获取下一个值的点上丢失时,这只是一个问题。你说5,但你的意思是2?