PHP 循环和&引用陷阱
前言
很久之前给电商公司写过一个计算销售人员绩效的方法,那复杂度,懂的都懂。实现时就发生了对同一个数组多次操作并改值的需求,也踩到了PHP作为弱类型语言和引用相关的坑,花了好久才找到问题。今日就做个记录。
陷阱的简单描述
- PHP的循环,没有单独的生命周期,循环产生的变量,在循环结束后不会被释放。
- 循环产生的值变量,如果与已存在的引用变量同名,会给对应的引用变量赋值。
现象
我们先看一段代码,可以根据描述思考下,这段代码的输出是什么?
代码
$a = [1, 2];
$b = [3, 4];
$i = &$a[0];
foreach ($a as &$c) {}
echo 'i: ', var_dump($i);
echo 'c: ', var_dump($c);
echo 'a: ', var_dump($a);
foreach ($b as $i => $c) {
echo $i . ' changed a: ', var_dump($a);
}
echo 'b end,changed a: ', var_dump($a);
输出
i: int(1)
c: int(2)
a: array(2) {
[0]=>
&int(1)
[1]=>
&int(2)
}
0 changed a: array(2) {
[0]=>
&int(0)
[1]=>
&int(3)
}
1 changed a: array(2) {
[0]=>
&int(1)
[1]=>
&int(4)
}
b end,changed a: array(2) {
[0]=>
&int(1)
[1]=>
&int(4)
}
处理思路
由于这是源码带来的特性,所以解决思路并不是修复它,而是规避它。
- 一、尽量避免使用同名的变量,即便是在循环中的“临时变量”。
- 二、循环完毕后,使用
unset()释放循环产生的变量。
这两个特性当然也可以利用起来实现某些特殊的需求,比如:
foreach($arr as $i => $item) {}循环后,$i可以当做count($arr)-1来使用,$item可以当做end($arr)来使用。这会节省一些开销。- 当需要保留对
end($arr)的引用时。 - 当需要覆盖之前的某个变量时。
这其实类似于在switch/case中,特意不使用break一样。可以根据特定的场景进行使用,但可读性较差,知其然,谨慎选择。

浙公网安备 33010602011771号