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?