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()将只删除引用而不会损害原始数据。