PHP Foreach按引用传递:Last Element Duplicating? (错误?)

我刚刚写了一个简单的PHP脚本,有一些非常奇怪的行为。 我将其降低到重新创build该错误所需的最低限度:

<?php $arr = array("foo", "bar", "baz"); foreach ($arr as &$item) { /* do nothing by reference */ } print_r($arr); foreach ($arr as $item) { /* do nothing by value */ } print_r($arr); // $arr has changed....why? ?> 

这输出:

 Array ( [0] => foo [1] => bar [2] => baz ) Array ( [0] => foo [1] => bar [2] => bar ) 

这是一个错误,或者是应该发生的一些非常奇怪的行为?

在第一个foreach循环之后, $item仍然是一个被$arr[2]使用的值的引用。 因此,第二个循环中的每个foreach调用(不通过引用调用)将用新值replace该值,从而replace$arr[2]

所以循环1,值和$arr[2]变成$arr[0] ,即'foo'。
循环2,值和$arr[2]变成$arr[1] ,这是'bar'。
循环3,值和$arr[2]变成$arr[2] ,这是'bar'(因为循环2)。

值“baz”实际上在第二个foreach循环的第一个调用中丢失了。

debugging输出

对于循环的每一次迭代,我们将回显$item的值以及recursion地打印数组$arr

当第一个循环运行时,我们看到这个输出:

 foo Array ( [0] => foo [1] => bar [2] => baz ) bar Array ( [0] => foo [1] => bar [2] => baz ) baz Array ( [0] => foo [1] => bar [2] => baz ) 

在循环结束时, $item仍然指向与$arr[2]相同的地方。

当第二个循环运行时,我们看到这个输出:

 foo Array ( [0] => foo [1] => bar [2] => foo ) bar Array ( [0] => foo [1] => bar [2] => bar ) bar Array ( [0] => foo [1] => bar [2] => bar ) 

您会注意到每个时间数组如何将一个新值放入$item ,同时也使用相同的值更新了$arr[3] ,因为它们都指向相同的位置。 当循环达到数组的第三个值时,它将包含值bar因为它刚刚由该循环的前一个迭代设置。

这是一个错误?

不。这是引用项目的行为,而不是一个错误。 这将类似于运行类似于:

 for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; } 

foreach循环在本质上并不是特殊的,它可以忽略引用的项目。 这只是简单地将该variables设置为新值,而不是像循环之外那样。

$item是对$arr[2]的引用,并被第二个foreach循环覆盖,正如animuson指出的那样。

 foreach ($arr as &$item) { /* do nothing by reference */ } print_r($arr); unset($item); // This will fix the issue. foreach ($arr as $item) { /* do nothing by value */ } print_r($arr); // $arr has changed....why? 

虽然这可能不会正式成为一个错误,在我看来是这样。 我认为这里的问题是我们期望$item能够在退出循环时超出范围,就像在其他许多编程语言中那样。 但是,似乎并不是这样的…

这个代码…

 $arr = array('one', 'two', 'three'); foreach($arr as $item){ echo "$item\n"; } echo $item; 

给出输出…

 one two three three 

正如其他人已经说过的,你用第二个循环覆盖了$arr[2]的引用variables,但这只是因为$item永远不会超出范围。 你们认为什么…错误?

一个更简单的解释,似乎来自PHP的原创者Rasmus Lerdorf: https ://bugs.php.net/bug.php?id = 71454

PHP的正确行为应该是在我看来是一个注意错误。 如果在循环外部使用在foreach循环中创build的引用variables,则会引起通知。 这种行为很容易出现,发生时很难发现。 没有开发人员要阅读的foreach文档页面,这不是一个帮助。

您应该在循环之后unset()参考,以避免此类问题。 对引用的unset()将只删除引用而不会损害原始数据。