php foreach
foreach是php最常用的遍历数组的方法了,以前也没太关注foreach的原理,直到最近出现了一些比较诡异的情况的时候,发现foreach的一些问题
功能介绍
遍历数组
非引用遍历
$array = [1,2,3];
foreach($array as $k => $v){
$v = 4;
}
var_dump($array);
输出:
array(3) {
[0] =>
int(1)
[1] =>
int(2)
[2] =>
int(3)
}
引用遍历
$array = [1,2,3];
foreach($array as $k => &$v){
$v = 3;
}
var_dump($array);
输出:
array(3) {
[0] =>
int(4)
[1] =>
int(4)
[2] =>
int(4)
}
遍历对象
class testClass
{
public $a = 1;
protected $b = 1;
private $c = 1;
public function test()
{
echo 'test';
}
public function toArray()
{
return [];
}
}
$class = new testClass();
foreach ($class as $k => $v)
{
var_dump($k);
var_dump($v);
$v = 2;
}
var_dump($class);
输出:
string(1) "a"
int(1)
testClass#73 (3) {
public $a =>
int(1)
protected $b =>
int(1)
private $c =>
int(1)
}
函数说明
1,对于非引用的遍历,foreach执行开始的时候,会先拷贝一份key的列表和value的列表,拷贝的数据和原来的数据是独立的,改变key或者value的值,不会对原来的数据有影响,对于对象的遍历也是一样,只是拷贝的数据是对象的public属性值
2,对于引用的遍历,则有些不同,因为是引用类型,拷贝的是key的列表和value的引用的列表,value的引用和原来的数据共用一个内存的地址,所以修改value的引用的值是会影响原来的数据的,对于对象的遍历也是一样
需要注意特殊的使用场景
$arr = [1, 2, 3];
foreach ($arr as $k => &$v) {
$v = $v * 2;
}
var_dump($arr);
foreach ($arr as $k => $v) {
echo $v."\n";
}
输出:
array(3) {
[0] =>
int(2)
[1] =>
int(4)
[2] =>
int(6)
}
2
4
4
为什么是2,4,4不是2,4,6,下面是调用的流程:
循环 | 备注 | $arr值 |
---|---|---|
循环 1-1 | 由于$v是一个引用, 因此 $v = &$arr[0], $v = $v * 2 相当于 $arr[0] * 2 | [2, 2, 3] |
循环 1-2 | $v = &$arr[1] | [2, 4, 3] |
循环 1-3 | $v = &$arr[2] | [2, 4, 6] |
循环 2-1 | 隐含操作 $v = $arr[0] 被触发, 由于此时 $v 仍是 $arr[2] 的引用, 相当于 $arr[2] = $arr[0] | [2, 4, 2] |
循环 2-2 | $v = $arr[1], 即$arr[2] = $arr[1] | [2, 4, 4] |
循环 2-3 | $v = $arr[2], 即$arr[2] = $arr[2] | [2, 4, 4] |
因为引用的原因导致得到不符合预期的数据,所以代码中使用到这种写法,在引用使用完需要unset一下
php7和php5的不同和相同的处理
$arr = [0, 1, 2];
foreach ($arr as $val) {
var_dump($val);
unset($arr[1]);
}
版本 | 结果 | 说明 |
---|---|---|
PHP5 | int(0) int(2) | 会将unset的数据跳过 |
PHP7 | int(0) int(1) int(2) | 对数组的改动不影响循环 |
$arr = [0, 1, 2];
foreach ($arr as &$val) {
var_dump($val);
unset($arr[1]);
}
版本 | 结果 | 说明 |
---|---|---|
PHP5 | int(0) int(2) | 按照引用进行循环的时候, 对数组的修改会影响循环 |
PHP7 | int(0) int(2) | 按照引用进行循环的时候, 对数组的修改会影响循环 |
总结
1,不使用引用的话,一般问题不大,需要注意unset对循环的影响在php7和php5版本下是不同的
2,对象的处理方式和数组一样
3,使用引用的话,unset会影响循环的结果,并且后面的代码如果也使用了这个变量,就会有奇怪的问题了,建议使用完引用之后unset引用的变量