我知道,在foreach中通过引用传递项目可能很危险。
特别是,必须不重用通过引用传递的变量,因为它会影响$array
,如下例所示:
$array = ['test'];
foreach ($array as &$item){
$item = $item;
}
$item = 'modified';
var_dump($array);
array(1){ [0] => & string(8)“modified” }
现在我在这里咬我:在函数should_not_modify
中修改数组的内容,即使我没有按值传递$array
。
function should_not_modify($array){
foreach($array as &$item){
$item = 'modified';
}
}
$array = ['test'];
foreach ($array as &$item){
$item = (string)$item;
}
should_not_modify($array);
var_dump($array);
array(1){ [0] => & string(8)“modified” }
我很想通过我的整个代码库并在每个unset($item);
之后插入foreach($array => &$item)
。
但是,由于这是一项重大任务并且引入了一条可能无用的行,我想知道是否有一条简单的规则可以知道foreach($array => &$item)
何时是安全的,而后面没有unset($item);
,并且什么时候没有。
编辑以澄清
我想我明白会发生什么以及为什么。我也知道最好做什么:foreach($array as &$item){...};unset($item);
我知道foreach($array as &$item)
之后这很危险:
$item
我的问题是:是否存在其他危险情况,我们是否可以建立一份详尽无遗的清单。或者反过来说:是否有可能描述它何时不危险。
答案 0 :(得分:5)
首先,关于PHP的两种行为的一些(可能是显而易见的)澄清:
foreach($array as $item)
将在循环后保持变量$item
不受影响。如果变量是引用,如在foreach($array as &$item)
中,它将“指向”数组的最后一个元素,即使在循环之后。
当变量是参考时,则分配,例如$item = 'foo';
将更改引用指向的内容,不变量($item
)本身。 对于后来的foreach($array2 as $item)
也是如此,如果$item
已经创建,它会将foreach
视为参考,因此会修改引用所指向的内容(最后一个)在这种情况下,前一个unset
中使用的数组元素。)
显然这很容易出错,这就是为什么你应该始终foreach
foreach
中使用的引用,以确保后续写入不会修改最后一个元素(如example #10中的function should_not_modify($array){
$array[0] = 'modified';
$array[1] = 'modified2';
}
$array = ['test', 'test2'];
$item = & $array[0];
should_not_modify($array);
var_dump($array);
类型数组的文档。)
值得注意的是 - 正如@iainn的评论所指出 - 你的例子中的行为与array(2) {
[0] =>
string(8) "modified"
[1] =>
string(5) "test2"
}
无关。仅存在对数组元素的引用将允许修改此元素。例如:
/* Assignment of array variables */
$arr = array(1);
$a =& $arr[0]; //$a and $arr[0] are in the same reference set
$arr2 = $arr; //not an assignment-by-reference!
$arr2[0]++;
/* $a == 2, $arr == array(2) */
/* The contents of $arr are changed even though it's not a reference! */
将输出:
$a = &$b
这无疑是令人惊讶的,但explained in the PHP documentation "What References Do"
但请注意,数组内部的引用可能存在危险。使用右侧的引用执行正常(非引用)赋值不会将左侧转换为引用,但是在这些正常赋值中保留数组内的引用。 这也适用于通过值传递数组的函数调用。 [...]换句话说,数组的引用行为是逐个元素定义的;单个元素的引用行为与数组容器的引用状态分离。
使用以下示例(复制/粘贴):
$a
了解在创建引用时很重要,例如$b
,$a
和$b
都是相同的。 $a
未指向$b
,反之亦然。 $item = & $array[0];
和$array[0]
指向同一个地方。
因此,当您执行$item
时,实际上$item
指向与$array[0]
相同的位置。由于$item
是一个全局变量,并且数组内的引用被保留,因此从任何地方(甚至从函数内部)修改{{1}}都会全局修改它。
是否存在其他危险情况,我们是否可以建立一份详尽的危险清单。或者反过来说:是否有可能描述它何时不危险。
我将再次重复PHP文档的引用:“数组内部的引用具有潜在的危险性。”
所以不,不可能描述它何时不危险,因为它永远不会不危险。很容易忘记{{1}}已被创建为引用(或者已创建并且未被销毁的全局引用),并在代码中的其他地方重用它并破坏数组。这一直是辩论的主题(this bug for example),人们称之为错误或特征......
答案 1 :(得分:0)
接受的答案是最好的,但我想补充一下:unset($item);
之后foreach($array as &$item)
何时没有必要?
$item
:如果之后再也没有重复使用,则无法造成伤害。
$array
:最后一个元素是引用。由于已经陈述的所有原因,这总是很危险。
那么什么会改变该元素形成对值的引用?
引用最多:unlink($item);
当$item
从函数返回数组时超出范围,然后从函数返回后数组变为“正常”。
function test(){
$array = [1];
foreach($array as &$item){
$item = $item;
}
var_dump($array);
return $array;
}
$a = test();
var_dump($a);
array(1){ [0] => &安培; INT(1) }
array(1){ [0] => INT(1) }
但要注意:如果你在返回之前做了其他事情,它可以咬人!
答案 2 :(得分:-2)
您可以通过" json decode / encode"
来中断引用function should_not_modify($array){
$array = json_decode(json_encode($array),false);
foreach($array as &$item){
$item = 'modified';
}
}
$array = ['test'];
foreach ($array as &$item){
$item = (string)$item;
}
should_not_modify($array);
var_dump($array);
这个问题纯粹是学术性的,这有点像黑客。但是,它以一种愚蠢的编程方式很有趣。
当然它输出:
array(1) {
[0]=>string(4) "test"
}
作为一方,同样的事情在JavaScript中起作用,它也可以从参考文献中获得一些好处。
我希望我有一个很好的例子,因为我有一些"怪异的"事情发生了,我的意思是像一些量子纠缠的东西。这一次在PHP阵营,我有一个递归函数(通过引用传递)与foreach(通过引用传递)以及它在空间时间连续体中撕裂了一个洞。