just do it

PHP中foreach和each 的区别分析

先看看下面这个题目:下面这段代码的输出是1,还是12?

1
2
3
4
5
6
7
<?php
$arr = array(1);
foreach($arr as $v)
{
    if($v == 1)$arr[] = 2;
    echo $v;
}

答案是1。为什么呢?通过vld查看这段代码的opcodes可以知道答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@lmt:/var/www# php -dvld.active=1 test.php
filename:       /var/www/test.php
function name:  (null)
number of ops:  16
compiled vars:  !0 = $arr, !1 = $v
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   9     0  >   INIT_ARRAY                                       ~0      1 //初始化数组,并付给临时变量~0
         1      ASSIGN                                                   !0, ~0 //将$arr赋值给临时变量,这就是普通函数参数传值 
  10     2    > FE_RESET                                         $2      !0, ->13 //初始化数组指针,开始循环
         3  > > FE_FETCH                                         $3      $2, ->13 
         4  >   ZEND_OP_DATA
         5      ASSIGN                                                   !1, $3 
  12     6      IS_EQUAL                                         ~5      !1, 1
         7    > JMPZ                                                     ~5, ->11
         8  >   ZEND_ASSIGN_DIM                                          !0
         9      ZEND_OP_DATA                                             2, $7
        10    > JMP                                                      ->11
  13    11  >   ECHO                                                     !1
  14    12    > JMP                                                      ->3  //一次循环结束跳转到3,这是还是使用最初赋值的临时变量~0,所以无法循环到新加进来的第二个元素
        13  >   SWITCH_FREE                                              $2
  18    14      ECHO                                                     ' '
        15    > RETURN                                                   1

简单来说就是因为foreach中传的是$arr的副本,并且foreach循环每次都使用最初那个副本进行循环。

如果输出12的效果该怎么做呢?

1
2
3
4
5
6
7
<?php
$arr = array(1);
while (list($key,$v) = each($arr))
{
    if($v == 1)$arr[] = 2;
    echo $v;
}// echo 12

通过opcodes可以看到原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
root@lmt:/var/www# php -dvld.active=1 test.php
compiled vars:  !0 = $arr, !1 = $key, !2 = $v
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   9     0  >   INIT_ARRAY                                       ~0      1
         1      ASSIGN                                                   !0, ~0
  10     2  >   SEND_REF                                                 !0
         3      DO_FCALL                                      1  $2      'each'
         4      FETCH_DIM_R                                      $3      $2, 1
         5      ASSIGN                                                   !2, $3
         6      FETCH_DIM_R                                      $5      $2, 0
         7      ASSIGN                                                   !1, $5
         8    > JMPZ                                                     $2, ->16
  12     9  >   IS_EQUAL                                         ~7      !2, 1
        10    > JMPZ                                                     ~7, ->14
        11  >   ZEND_ASSIGN_DIM                                          !0
        12      ZEND_OP_DATA                                             2, $9
        13    > JMP                                                      ->14
  13    14  >   ECHO                                                     !2
  14    15    > JMP                                                      ->2
  18    16  >   ECHO                                                     ' '
        17    > RETURN                                                   1

使用each循环的时候每次传入的是$arr的引用,所以每次在循环内部的改变能影响下一
次的循环。

点赞