返回首页 - Notes - 2019

PHP foreach reference “特性”


问题描述

现有如下代码:

<?php

$arr = ['aaa', 'bbb', 'ccc'];

// 循环里面引用
foreach ($arr as &$item) {}
print_r($arr);

// 循环里面引用
foreach ($arr as &$item) {}
print_r($arr);

// 循环里面无引用
foreach ($arr as $item) {}
print_r($arr);

输出结果是:

Array
(
    [0] => aaa
    [1] => bbb
    [2] => ccc
)
Array
(
    [0] => aaa
    [1] => bbb
    [2] => ccc
)
Array
(
    [0] => aaa
    [1] => bbb
    [2] => bbb
)

可以发现,最后的那次 foreach 没有使用引用,输出的最后一个数组元素丢失了,值变成了倒数第二个元素的值


原理分析

实质上,foreach 在循环时,会有个隐式的赋值过程

在前一个 foreach 引用循环结束时,item 被指向了数组的最后一个元素,当进入新的 foreach 非引用循环时,首先会把 item 修改为数组的第一个元素的值,然后是第二个、第三个

所以其实数组的最后一个元素,其值一直在变,等到循环到它时,它的值已经被前一轮循环修改成倒数第二个元素的值了

这是 PHP 很容易踩坑的一个“特性”,绝对需要注意


解决办法

方法一:将后面进行 foreach 非引用循环的临时变量改成不一样的名字,比如将 item 改成 item2

<?php

$arr = ['aaa', 'bbb', 'ccc'];

foreach ($arr as &$item) {}
print_r($arr);

foreach ($arr as $item2) {}
print_r($arr);

方法二:在进行 foreach 非引用循环前,断开 item 的指向关系,将其 unset 掉即可

<?php

$arr = ['aaa', 'bbb', 'ccc'];

foreach ($arr as &$item) {}
print_r($arr);

unset($item);

foreach ($arr as $item) {}
print_r($arr);

date:2019-04-12