ジェネレータ
ジェネレータ自体はPHP 5.5で追加された機能で、繰り返しのデータを効率よく提供できるものです。同じような機能にはIteratorインターフェースを実装したクラスがありますが、複雑なクラスを自作することなく実装することができます。この処理が必要な主な理由には、実行時の消費メモリの節約があります。このジェネレータの機能においてPHP 7では最後の値のみの特別な扱いをするためのreturnと一部の処理を、別のジェネレータに委譲することが可能になりました。リスト10は、PHP 5.5とPHP 7でのサポートされたreturnを使ったサンプルコードです。
//(1)PHP 5.5の場合の実装例 function xrange5($start,$end){ for($i = $start; $i < $end+1; $i++){ yield $i; } } $it = xrange5(0,3); $total = 0; $cnt = 0; foreach($it as $i){ $cnt++; $total += $i; } if($cnt > 0 ){ echo "合計値は[ $total ]です".PHP_EOL; } else{ echo "合計処理は行われませんでした".PHP_EOL; } // PHP 7の場合の記述方法 function xrange7($start,$end){ for($i = $start; $i < $end + 1; $i++){ yield $i; } if($start < $end){ return true; //(2)returnで最後の値を指定可能 } else{ return false; } } $it = xrange7(0,3); $total = 0; foreach($it as $i){ $total += $i; } if($it->getReturn()){ //(3)最後の値の取得 echo "合計値は[ $total ]です".PHP_EOL; } else{ echo "合計処理は行われませんでした".PHP_EOL; }
PHP 5.5で繰り返し処理の終了に応じた処理を記述した場合の例が(1)です。この処理をPHP 7でサポートされた方法で記述すると、(2)のように作成するジェネレータ内で最後の値をreturnで返します。この最後の値はforeach処理内では取得できず、(3)のようにgetReturn()関数を実行して取得できますので、こちらの値を使って特別な処理を記述します。このように最後を意識した処理がある場合には、その判断をジェネレータ側に隠蔽して実装する事ができます。
また、ジェネレータは他のジェネレータに処理を以上することも出来ます。その場合には、yield fromの記述を使い、リスト11は別のジェネレータに処理を委譲した際のサンプルコードです。
function range_from(){ yield array(0,"-- 選択して下さい --"); yield from range1(); //(1)他のジェネレータへの委譲 yield from [array(2,'千葉'),array(3,'埼玉')]; //(2)配列への委譲 return "END_OF_RANGE_0"; //(3)getReturn()で取得できる値 } function range1(){ yield array(1,"東京"); return "END_OF_RANGE_1"; //(4)この値は取得できません } $range = range_from(); foreach($range as $item){ var_dump($item[1]); // "-- 選択して下さい --","東京","千葉","埼玉"の順に表示 } var_dump($range->getReturn()); // string(14) "END_OF_RANGE_0"
(1)のようにyield fromを用いることで別のジェネレータに処理を委譲することができます。また、委譲先は(2)のように配列でも問題ありません。ただし、getReturn()で取得できる値は親となるジェネレータで返している(3)の値であり、子となるジェネレータで指定した(4)の値は返せませんので注意して下さい。
エラー・例外の扱い
PHP 7では、PHPコード内で取得可能であった最上位のクラスであるExceptionよりもさらに上位となるThrowableクラスが作成されました。また、コードの上のエラーを示すErrorクラスがThrowableクラスのサブクラスとして作成され、図1に示すようにコントロールできるエラーの範囲が広がりました。PHP 7で追加されたErrorクラスには表2のようなクラスがあります。
クラス名 | 説明 |
---|---|
TypeError | 引数の型チェックや戻り値の型チェックでエラーになった場合 |
ParseError | eval()などでPHPコードのパースが失敗した場合 |
AssertionError | assert()によるアサーションが失敗した場合 |
ArithmeticError | 数学的操作によるエラーが発生した場合 |
DivisionByZeroError | ゼロの割り算の場合 |
このため、リスト12のように今まではコード内で処理できなかったエラーも処理できるようになりました。また、finallyブロックはPHP 5.5で記述できるようになっています。
function arg_int(int $num){ return $num; } try { arg_int('a'); } catch(Exception $ex){ echo "Exception : ".get_class($ex)."-".$ex->getMessage().PHP_EOL; } catch(Error $err){ // Errorクラスを取得する echo "Error : ".get_class($err)." - ".$err->getMessage().PHP_EOL; // Error : TypeError - Argument 1 passed to arg_int() must be of the type integer, string given, called in ... } finally{ // finally はPHP 5.5で追加されています echo "必ず実行されます".PHP_EOL; }
最後に
PHP 7で新たな言語仕様を見ていると、表面的なPHPコードにはあまり影響がないような印象を受けます。しかし、それでいて大きなパフォーマンスが得られることを考えると、素晴らしいバージョンアップのように思います。一方で、PHPコードをより堅牢にするためや、構造を管理するための変更が入っています。筆者はこれらの機能を取り込んだより使いやすい新しいフレームワークが出てくることのではないかと思っています。
今回はPHP 7の新しい部分にフォーカスして紹介しましたが、次回は今回紹介できなかった機能や、PHP 5.xからの変更された仕様や、注意すべき点などを紹介します。