什么时候参考参考危险?

Lor*_*yer 6 php arrays foreach pass-by-reference

我知道,在foreach中通过引用传递项目可能很危险.

特别是,必须不重用通过引用传递的变量,因为它会影响$array,如下例所示:

$array = ['test'];
foreach ($array as &$item){
    $item = $item;
}
$item = 'modified';
var_dump($array);
Run Code Online (Sandbox Code Playgroud)

array(1){[0] =>&string(8)"modified"}

现在这里咬我:在函数内部修改数组的内容should_not_modify,即使我没有传递$arrayby值.

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);
Run Code Online (Sandbox Code Playgroud)

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
  • 将数组传递给函数

我的问题是:是否有其他危险的案例,我们是否可以建立一份危险的详尽清单.或者反过来说:是否有可能描述它何时不危险.

rla*_*vin 5

关于foreach

首先,关于PHP的两种行为的一些(可能是显而易见的)澄清:

  1. foreach($array as $item)$item循环后将保持变量不变.如果变量是一个引用,就像在foreach($array as &$item),它将"指向"数组的最后一个元素,即使在循环之后.

  2. 当变量是引用时,则赋值,例如$item = 'foo';将改变引用所指向的内容,而不是变量($item)本身.这也适用于随后的真实foreach($array2 as $item)这将治疗$item如果它已被如此创建作为基准,因此无论参考指向(在先前使用的阵列的最后一个元素将修改foreach在这种情况下).

显然这很容易出错,这就是为什么你应该总是unset使用a中的引用foreach来确保后续写入不修改最后一个元素(如类型数组的doc的示例#10).

关于修改数组的函数

值得注意的是 - 正如@iainn的评论中指出的那样 - 你的例子中的行为与之无关foreach.仅存在对数组元素的引用将允许修改此元素.例:

function should_not_modify($array){
    $array[0] = 'modified';
    $array[1] = 'modified2';
}
$array = ['test', 'test2'];
$item = & $array[0];

should_not_modify($array);
var_dump($array);
Run Code Online (Sandbox Code Playgroud)

将输出:

array(2) {
  [0] =>
  string(8) "modified"
  [1] =>
  string(5) "test2"
}
Run Code Online (Sandbox Code Playgroud)

这无疑是非常令人惊讶的,但在PHP文档"什么参考文献中"中进行了解释.

但请注意,数组内部的引用可能存在危险.使用右侧的引用执行正常(非引用)赋值不会将左侧转换为引用,但是在这些正常赋值中保留数组内的引用.这也适用于通过值传递数组的函数调用.[...]换句话说,数组的引用行为是逐个元素定义的; 各个元素的引用行为与数组容器的引用状态分离.

使用以下示例(复制/粘贴):

/* 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! */
Run Code Online (Sandbox Code Playgroud)

认识到建立一个基准,例如,当是很重要的$a = &$b,然后都$a$b相等.$a没有指向,$b反之亦然.$a$b指向同一个地方.

所以,当你这样做时,$item = & $array[0];你实际上$array[0]指向同一个地方$item.由于$item是全局变量,并且数组内的引用被保留,因此$array[0]从任何地方(甚至从函数内部)进行修改都会全局修改它.

结论

还有其他危险的案例,我们是否可以建立一份危险的详尽清单.或者反过来说:是否有可能描述它何时不危险.

我将再次重复PHP文档中的引用:"数组内部的引用具有潜在的危险性".

所以不,不可能描述它何时不危险,因为它永远不会危险.很容易忘记$item已经创建为引用(或者创建并且未被销毁的全局引用),并在代码中的其他地方重用它并破坏数组.这一直是争论的话题(例如这个bug中),人们称之为bug或者特征......