PHP foreach原理详解

当 foreach 开始执行时,数组内部的指针会自动指向第一个单元。若是移动指针的结果超出了数组单元的末端,则退出循环。php

例子:

$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],天然也就不会影响到$arraydebug

而用第二种方法(引用)的时候,相等于$value=&$array[$key]$array[$key]$value指向同一内存地址,$value=2天然就改变了$array[$key]的值,也就改变了$array的值指针

循环时,$key$value都是临时变量,只是赋值方式不一样code

陷阱

陷阱:两次循环使用一样的临时变量的状况下,若是第一次循环使用的是引用,那么在第二次循环中即便没有加&符号,临时变量也是引用。这个引用指向了数组中最后一个元素(循环到了最后一个元素结束)。内存

如:string

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