PHPで数字の配列の中に偶数があるか?という判定用の関数を作成したいとする。

配列の中に偶数が一つでもあればtrue(真)を返すわけだから、

<?php
function isEven($arr){
        foreach($arr as $int){
                if($int % 2 === 0) return true;
        }
        return false;
}

このように偶数があった場合にreturn true;で終わらせたくなるけれども、本当にこれで良いのか?疑問が生じた。

何故疑問が生じたか?というと、PHPのVLDでforとforeachを比較するの記事で、

foreach.php

<?php
$arr = range(1, 100000);
foreach($arr as $i => $v){
        //以後の処理は省略
}
$ php -d vld.active=1 -d vld.execute=0 /path/to/dir/foreach.php
Finding entry points
Branch analysis from position: 0
2 jumps found. (Code = 77) Position 1 = 6, Position 2 = 9
Branch analysis from position: 6
2 jumps found. (Code = 78) Position 1 = 7, Position 2 = 9
Branch analysis from position: 7
1 jumps found. (Code = 42) Position 1 = 6
Branch analysis from position: 6
Branch analysis from position: 9
1 jumps found. (Code = 62) Position 1 = -2
Branch analysis from position: 9
filename:       /path/to/dir/foreach.php
function name:  (null)
number of ops:  11
compiled vars:  !0 = $arr, !1 = $v, !2 = $i
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    2     0  E >   INIT_FCALL                                               'range'
          1        SEND_VAL                                                 1
          2        SEND_VAL                                                 100000
          3        DO_ICALL                                         $3      
          4        ASSIGN                                                   !0, $3
    3     5      > FE_RESET_R                                       $5      !0, ->9
          6    > > FE_FETCH_R                                       ~6      $5, !1, ->9
          7    >   ASSIGN                                                   !2, ~6
          8      > JMP                                                      ->6
          9    >   FE_FREE                                                  $5
    6    10      > RETURN                                                   1

branch: #  0; line:     2-    3; sop:     0; eop:     5; out0:   6; out1:   9
branch: #  6; line:     3-    3; sop:     6; eop:     6; out0:   7; out1:   9
branch: #  7; line:     3-    3; sop:     7; eop:     8; out0:   6
branch: #  9; line:     3-    6; sop:     9; eop:    10; out0:  -2
path #1: 0, 6, 7, 6, 9, 
path #2: 0, 6, 9, 
path #3: 0, 9, 

foreach文の処理の最後に、FE_FREEでforeach内で使用した変数を解放している(たぶん)。

であれば、冒頭のコードでforeachの最中でreturnで関数自体から抜けてしまったら、foreach内で余計なメモリの使用分が残ってしまうのではないか?

ということで調べてみることにした。




下記のようなコードを作成して、オペコードを調べてみた。

<?php
function isEven($arr){
        foreach($arr as $int){
                if($int % 2 === 0) return true;
        }
        return false;
}

$array = array(3, 5, 2, 1, 7);
if(isEven($array)       ){
        echo "is even";
}else{
        echo "no even";
}

オペコードは下記の通り(isEven関数の処理のところだけピックアップ)

Function iseven:
Finding entry points
Branch analysis from position: 0
2 jumps found. (Code = 77) Position 1 = 2, Position 2 = 9
Branch analysis from position: 2
2 jumps found. (Code = 78) Position 1 = 3, Position 2 = 9
Branch analysis from position: 3
2 jumps found. (Code = 43) Position 1 = 6, Position 2 = 8
Branch analysis from position: 6
1 jumps found. (Code = 62) Position 1 = -2
Branch analysis from position: 8
1 jumps found. (Code = 42) Position 1 = 2
Branch analysis from position: 2
Branch analysis from position: 9
1 jumps found. (Code = 62) Position 1 = -2
Branch analysis from position: 9
filename:       /path/to/dir/foreach.php
function name:  isEven
number of ops:  12
compiled vars:  !0 = $arr, !1 = $int
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    2     0  E >   RECV                                             !0      
    3     1      > FE_RESET_R                                       $2      !0, ->9
          2    > > FE_FETCH_R                                               $2, !1, ->9
    4     3    >   MOD                                              ~3      !1, 2
          4        IS_IDENTICAL                                     ~4      ~3, 0
          5      > JMPZ                                                     ~4, ->8
          6    >   FE_FREE                                                  $2
          7      > RETURN                                                   <true>
    3     8    > > JMP                                                      ->2
          9    >   FE_FREE                                                  $2
    6    10      > RETURN                                                   <false>
    7    11*     > RETURN                                                   null

branch: #  0; line:     2-    3; sop:     0; eop:     1; out0:   2; out1:   9
branch: #  2; line:     3-    3; sop:     2; eop:     2; out0:   3; out1:   9
branch: #  3; line:     4-    4; sop:     3; eop:     5; out0:   6; out1:   8
branch: #  6; line:     4-    4; sop:     6; eop:     7; out0:  -2
branch: #  8; line:     3-    3; sop:     8; eop:     8; out0:   2
branch: #  9; line:     3-    7; sop:     9; eop:    11
path #1: 0, 2, 3, 6, 
path #2: 0, 2, 3, 8, 2, 9, 
path #3: 0, 2, 9, 
path #4: 0, 9, 
End of function iseven

なんとforeachの途中でreturn trueで抜ける箇所と、foreachを終えた時の二箇所にFE_FREEの命令があるではないか!

こういう細かい対応を見ると嬉しかったりする。