当 foreach 开始执行时,数组内部的指针会自动指向第一个单元。如果移动指针的结果超出了数组单元的末端,则退出循环。
例子:
$array = [
'name' => 'Jobs',
'age' => 50,
];
foreach ($array as $key => $value) {
$value = 22;
//赋值,但是没有影响到$array[$key]位置的值,除非加上 $array[$key] = $value;
}
上面的foreach 对数组内的值没影响。有时候我们为了在循环过程中改变数组项的值,在foreach的时候变量入口可以加个&符号,表示,循环过程中使用数组中原来的值,而不是一个复制的值,如
$array = [
'name' => 'Jobs',
'age' => 50,
];
foreach ($array as $key => &$value) {
//直接改变数组$array[$key]位置的值
$value = 22;
}
结果输出对比:
Array
(
[name] => Jobs
[age] => 50
)
Array
(
[name] => 22
[age] => 22
)
原因分析:
$key和$value都是临时变量,foreach的时候,把每个数组单元的键分别赋值给$key,把每个数组单元的值分别赋给$value,相等于$value=$arr[$key],$value=2仅仅是改变了$value的值(非&传递),并不会影响到$array[$key],自然也就不会影响到$array。
而用第二种方法(引用)的时候,相等于$value=&$array[$key],$array[$key]和$value指向同一内存地址,$value=2自然就改变了$array[$key]的值,也就改变了$array的值
循环时,$key和$value都是临时变量,只是赋值方式不同
陷阱
陷阱:两次循环使用同样的临时变量的情况下,如果第一次循环使用的是引用,那么在第二次循环中即使没有加&符号,临时变量也是引用。这个引用指向了数组中最后一个元素(循环到了最后一个元素结束)。
如:
array = [
'name' => 'php',
'age' => 123,
];
//标记为循环1:
foreach ($array as $key => &$value) {
echo "key=$key, value=$value" . PHP_EOL;
}
//循环完后,最后一个元素 $value = &$array['age'], 这里$value 和 $array['age'] 是引用关系,都是指向的同一个空间。
var_dump($array);
输出结果如下:
array(2) {
["name"]=>
string(3) "php"
["age"]=>
&int(123)
}
//标记为循环2:
foreach ($array as $key => $value) {
//第一次循环进来 $value=$array['name'],此时$value = 'php',由于我们开始的引用中,
$value 和 $array['age'] 建立了引用,所以这里改变了$value的值,
其实就改变了 $array['name']的值,所以 $array['name'] = 'php',
//以后的每次循环处理都和上面第一次相同
//得到的结果:其实每次循环赋值给$value的值,都是给“标记为循环1”中的最后一个元素在赋值,也就是说数组中最后一个元素的值 = 数组中的倒数第2个元素的值
echo "key=$key, value=$value" . PHP_EOL;
}
上面的 foreach循环中输出结果如下:
key=name, value=php
key=age, value=123
key=name, value=php
key=age, value=php
总结:两次循环使用同样的临时变量的情况下,如果第一次循环使用的是引用,那么在第二次循环中即使没有加&符号,临时变量也是引用。这个引用指向了数组中最后一个元素(循环到了最后一个元素结束)。,得到的最后数据结果就是数组中最后一个元素的值 = 数组中的倒数第2个元素的值 (也就是说,最后2个元素的值相同,且为到倒数第2个元素的处理值)。
如何避免这个陷阱呢?
方法1:在第二次循环之前,unset($value)
方法2:第二次foreach的时候使用不同名字的临时变量
扩:
另:查看一个变量是否是引用可以使用xdebug_debug_zval函数(需要有xdebug扩展)。
xdebug_debug_zval的结果形如: value: (refcount=2, is_ref=1)=123