坑点and埋坑点 - foreach使用引用会意外修改数组

  • 作者:KK

  • 发表日期:2016.11.14


$arr1 = [2, 9];
foreach($arr1 as &$item){}

$arr2 = [77, 4];
foreach($arr2 as $item){}

print_r([$arr1, $arr2]);

以上两个foreach的花括号里面没有运行任何代码,可是最后输出却显示$arr1第2个元素变成了4,但它原来的值应该是9才对


原因

原因是第1个foreach在产生$item变量的时候使用了引用,解释逻辑如下:

  1. 当最后一次foreach时,$item就是$arr1的最后一个元素的引用

  2. 由于foreach结束后,临时变量不会消失,所以$item依然存在

  3. 第2次foreach时,又产生临时变量,此时不会重新销毁旧的$item,而是直接利用了上一个$item作为临时变量

  4. 第2次for时每一次循环都将值写入$item的地址,最后一次将$arr2的4写入了$item

  5. 结果由于$item是$arr1最后一个元素的引用,所以这个元素的值就变成了4

而将这两个foreach翻译成基础代码是这样的:

$arr1 = [2, 9];
$item = &$arr1[0];
$item = &$arr1[1];

$arr2 = [77, 4];
$item = $arr2[0];
$item = $arr2[1];

print_r([$arr1, $arr2]);

典型踩坑场景

通常在业务逻辑中,从数据库读取数据后进行foreach加工,但是同一个作用域内有2次foreach,并且这两次foreach都共用了同一个临时变量名:

$data = getDbData();
foreach($data as &$value){}

$data2 = getDbData2();
foreach($data2 as $value){}

//... $data 最后一个元素被改变了

避免方法

  1. 不要随便为foreach的临时变量加上&这个引用符号,除非你确实很清楚会有什么后果以及有防范

  2. 如果非要加&引用,那就不要让同一作用域内的两个foreach临时变量名发生冲突