PHP 通过引用错误?

PHP pass by reference bug?

不要通过引用传递。我知道.......但是.

谁能解释一下这是怎么回事?我会尝试解释我的想法,也许这是一个 PHP 错误。

下面的代码脚本显示了一个字符串数组通过引用传递的示例(我知道不是...),并且如果等于标志则更改字符串。

数组完整地从 foreach 循环中出来。字符串已按需要更改。所有其他值仍在数组中。

我将更改后的数组 不是通过引用 传递到另一个 foreach 循环中,并尝试使用与在第一个循环中使用。我假设变量名称将被重置为新值(类似于 $variable = "new value")。但是,似乎变量名没有被取消引用。

不知何故,在该循环中,原始数组被更改并出现重复的倒数第二个值,并且缺少原始的最后一个值。对于其他具有更长更复杂数组的情况,情况总是如此。

代码如下,有人可以验证。

1) 这是一个 PHP 错误,在第二个 foreach 循环中使用时变量没有取消引用吗?

2) 如果有人可以提供有关 PHP 幕后可能发生的引用变量的信息,我将很乐意理解。换句话说,为什么 foreach 会导致数组出现重复和缺失元素?

<?php


$array = ["one", "two", "three"];
$other_array = [];
echo "<pre>";
print_r($array);
echo "</pre>";

foreach($array as &$reference){
  if($reference == "one") $reference = "one_changed";
}

echo "<pre>";
print_r($array);
echo "</pre>";

foreach($array as $reference){
  array_push($other_array, $reference);
}
echo "====changed them but did not push onto array====<br>";

echo "====changed also====";
echo "<pre>";
print_r($array);
echo "</pre>";
echo "====changed also====";
echo "<pre>";
print_r($other_array);
echo "</pre>";

不是错误,如果你想重用以前用作参考的变量,你需要这样做:

unset($var);

具体来说,在您的第一个循环之后添加这一行:

foreach($array as &$reference){
  if($reference == "one") $reference = "one_changed";
}
unset($reference); // <--- this

解决所有问题:

Array
(
    [0] => one
    [1] => two
    [2] => three
)
Array
(
    [0] => one_changed
    [1] => two
    [2] => three
)
====changed them but did not push onto array====<br>====changed also====Array
(
    [0] => one_changed
    [1] => two
    [2] => three
)
====changed also====Array
(
    [0] => one_changed
    [1] => two
    [2] => three
)

还有一件事。数组元素也可以是引用,例如:

$a = 1;

$c=[];
$c[0] =& $a;
$c[1] =& $a;
print_r($c);

$a=2;
print_r($c);

那将输出:

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

所以,为了解释您的代码中到底发生了什么……我宁愿通过。你创建一个引用的循环变量,然后你将它用作另一个循环中的 non-referenced 变量,并将它作为一个值推送到另一个数组中......搞砸了,我放弃了。

这不是"pass by reference",因为这里没有经过。

& 中修改 foreach 循环中的数组元素在 PHP manual page on foreach 中有所描述,并带有关于之后需要取消设置变量的警告。

Warning

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset(). Otherwise you will experience the following behavior: