Passingforreferenceinphp
December 17, 2017 php zicaiPHP的引用在foreach中使用需要注意的事
最近1个月在掘金上经常看到类似这样的问题:
juejin.cn/post/684490… juejin.cn/post/684490…
本篇文章就此问题做一个详细说明,下面是问题的简单重现:
<?php
$arr = [1,2,3];
foreach ($arr as &$item) {
echo $item."\n";
}
foreach ($arr as $item) {
echo $item."\n";
}
运行后的显示结果:
1
2
3
1
2
2
看似没有做任何修改$arr变量的动作,但打印出来后结果却是出人意料,在第二次foreach的最后一次输出,其结果居然是2而不是3,为什么呢?
PHP文档中的解释
文档中告诉了大家如何避免这个问题,那就是在foreach结束后unset($item)来取消引用。
如果我们不取消引用呢?
第一次foreach
那么在第一次foreach循环结束后:
<?php
$arr = [1,2,3];
foreach ($arr as &$item) {
echo $item."\n";
}
echo '$item:'.$item."\n";
我们可以看到其结果为:
1
2
3
$item:3
也就是说此时$item的依然代表着$arr[2]的值,下面来代码证实:
<?php
$arr = [1,2,3];
foreach ($arr as &$item) {
echo $item."\n";
}
echo '$item:'.$item."\n";
$arr[2] = 10;
echo '$item:'.$item."\n";
运行后结果是:
1
2
3
$item:3
$item:10
在第一次foreach结束并打印一次$item之后我们修改$arr[2]=10,再次打印$item 得到 10,这就印证了我们的想法
第二次foreach
第二次foreach中我们同样使用了$item作为循环当前值的赋值对象,这与我们第一次foreach中的赋值对象$item相同,问题出在第一次foreach中$item是赋值的形式,那么当第一次循环时我们不光将$arr[0]的值赋给了$item并显示出来,还同时用$arr[0]更改了它所代表的$arr[2]的值。同样的,当第二次循环时我们将$arr[1]赋值给了$item并显示出来,还同时用$arr[1]更改了$item引用的$arr[2]。而到了第三次循环,显示的自然就是被改变后的$arr[2]了。
为了看到跟明显一些,我们在第二次foreach中打印$arr[2]的值:
<?php
$arr = [1,2,3];
foreach ($arr as &$item) {
echo $item."\n";
}
foreach ($arr as $item) {
echo '当前$arr[2]的值是:'.$arr[2]."\n";
echo $item."\n";
}
运行的结果是:
1
2
3
当前$arr[2]的值是:1
1
当前$arr[2]的值是:2
2
当前$arr[2]的值是:2
2
解决办法
1.按照PHP官方手册所说,在第一次foreach后我们使用unset($item)来取消引用
2.我们可以在第二次foreach时同样使用引用的方式&$item来重现赋值
总结:
foreach在每一次loop中都将当前值赋给我们定义的$value,如果在foreach后不及时删除$value,那么$value依然可以在foreach后继续使用,所有为了安全起见每次foreach完后都unset()掉定义的$value和$key是一个好的习惯。