PHP中foreach和each 的区别分析

php相关 / 2012年11月28日 23时22分 / 10792人浏览
先看看下面这个题目:下面这段代码的输出是1,还是12?


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

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的效果该怎么做呢?


通过opcodes可以看到原因:
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的引用,所以每次在循环内部的改变能影响下一 次的循环。