1. 简介

foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。

2. 语法

foreach 有两种语法:

foreach (array_expression as $value)
    statement

第一种格式遍历给定的 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。

foreach (array_expression as $key => $value)
    statement

第二种格式也一样,还会把当前单元的键名在每次循环中赋给变量 $key。

还能够自定义遍历对象。

3. 注意

3.1 迭代时不再改变内部数组指针

在 PHP7 之前,当数组通过 foreach 迭代时,数组指针会移动。但现在 foreach 不再改变内部数组指针。

$arr = [0, 1, 2];
foreach ($arr as &$value) {
    var_dump(current($arr));
}
/** 结果
int 0
int 0
int 0
*/

3.2 通过值遍历时,操作的值为数组的副本

当默认使用值来遍历数组时,foreach 实际操作的是数组的迭代副本,而非数组本身。这就意味着,foreach 中的操作不会修改原数组的值。

3.3 通过引用遍历时,有更好的迭代特性

当使用引用引用来遍历数组时,foreach 在迭代中能更好的跟踪数组的变化。例如,在迭代中添加一个值到数组中:

$arr = [0];
foreach ($arr as &$value) {
    var_dump($value);
    $arr[1] = 1;
}
/** 结果
int 0
int 1
*/

3.4 通过引用遍历时的陷阱

通过引用遍历数组时,迭代结束,但对最后一个元素的引用还保留着。建议使用 unset() 来将其销毁。否则可能影响后面的结果。

$arr = [1, 2, 3, 4];
foreach ($arr as &$value) {
    $value = $value * 2;
}
// $arr 现在是 [2, 4, 6, 8]

// 没有执行 unset($value),$value 仍然是最后一个元素 $arr[3] 的引用

foreach ($arr as $key => $value) {
    // $arr[3] 将依次被 $arr 里面的值更新
    echo "{$key} => {$value} ";
    print_r($arr);
}

// 直到最后倒数第二个值被赋值给最后一个值。因为倒数第二次时,第三个元素和最后一个元素的值已经是一样的了。

/** 结果
0 => 2 Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 2
)
1 => 4 Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 4
)
2 => 6 Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 6
)
3 => 6 Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 6
)
*/

解决办法:

  1. 在遍历结束时,使用 unset() 将引用变量销毁。
  2. 第二次遍历时,使用其他变量名,别用 $value 了。
  3. 第二次遍历也用引用 &