PHPの最新状況:PHP 7.4の開発状況(第八回)
まだまだ残暑が厳しい中、いかがお過ごしでしょうか。PHP7.4の開発は、引き続き、2019年末のリリースを目標に続けられています。今回のコラムでは、前回に続き、PHP 7.4における変更点について解説します。
PHP 8のJIT機能について
前回、PHP 8で導入が予定されるJIT (Just-In-Time)機能について紹介しました。この記事では、参考として、PHPに付属するベンチマーク用スクリプト(bench.phpおよびmicro_bench.php)によるテストの結果を示しましたが、結果の考察について注意が必要ですので、本稿で補足します。
前記事において、PHP8+jitではPHP7に比べて、アルゴリズム系を扱うbench.phpで約4倍、シンプルな言語機能を扱うmicro_bench.phpで約2倍と、実行速度が大幅に高速化されることを示しました。図1および図2のPHP 7.4とPHP 8.0 JITの結果の比較がそれにあたります。この結果には、注意すべき点があります。それは、PHP 8.0のJITがPHPに標準搭載されたオペコードキャッシュOPCacheの仕組みに基づき実装されていることです。PHPでは、PHPスクリプトを実行する際、スクリプトのコードをバイトコードに変換(コンパイル)してから実行します。オペコードキャッシュは、このバイトコードを共有メモリ上にキャッシュすることで、PHPスクリプトのロード、コンパイルに要する時間を省略し、実行速度を大幅に高速化する仕組みです。PHPでは、標準でこの仕組を利用可能としています。
PHPのJITの仕組みでは、共有メモリ上にバイトコードの代わりにJITで変換・コンパイルされたネーティブな実行コードを配置・利用します。このため、従来からあるオペコードキャッシュによる高速化の効果も併せて享受できるということになります。
それでは、オペコードキャッシュ相当の機能を除いたJITの正味の効果はどのくらいあると言えるでしょうか?これを調べるため、図1、図2にPHP 7.4およびPHP 8.0でOPCacheを有効にして測定した結果も示しています。bench.php (図1)の結果を見ると、PHP 7.4でOPCacheを用いない場合(一番左)と用いる場合(左から2番目)で約2倍の改善効果があることがわかります。この結果は、PHP 8.0でOPCacheのみを有効にした結果(左から3番目)もほぼ同じ値であることから、妥当なものと言えます。これらから、オペコードキャッシュの効果を除いたJITの効果は、PHP 8.0でOPCacheのみを有効にした結果とPHP 8.0でJIT(+OPCache)を有効にした結果を比較すれば得られると考えられ、bench.php(図1)では約2倍、micro_bench.php(図2)では約30%となります。
なお、前稿にも記述しましたが、WordPressのような実用アプリケーションにおける高速化の効果はまだ少ないことが報告されており、プロファイラや最適化等の組み合わせによる更なる高速化が期待されています。
もう一つ、PHPの進化を示す参考として、過去のPHPのバージョンとのベンチマーク比較を図3に示します。同じ計算機(AMD64)でPHP 4.4では14秒近くかかっていたコードの実行時間がPHP 5.6では約3秒となり、PHP 7.4では1秒以下、PHP 8.0では0.2秒に迫ることになります。PHP 4.4がリリースされたのが2005年ですので、2019年に至るまでの約14年の間に60倍以上の高速化が実現されたことになります。この高速化は、計算機(CPU)自体の高速化によるものではなく、主にPHPスクリプトの実行に関わる技術の進歩によるものであり、PHPの開発に関わる開発者の成果であると言えます。あくなき改良をすすめる開発者の努力には頭の下がる思いです。
PHP 7.4の開発状況
PHPの最新版であるバージョン7の最新版はPHP 7.3ですが、次のマイナーバージョンアップ版にあたるPHP 7.4の開発は順調に進んでいます。これまでに、PHP 7.4版に含まれる機能を締切るフィーチャーフリーズが計画通り7月22日に実施され、7月25日にβ1、8月8日にβ2がリリースされています。この後8月22日に最後のベータ版であるβ3がリリースされ、9月からリリース候補版が6回リリースされる計画となっています。
PHP 7.4の以下に示す主な新機能について紹介してきました。
- OpCacheのプリロード機能
- クラスプロパティの型指定追加
- FFI (Foreign Function Interface)機能
- 配列スプレッド構文
- アロー関数
- ??= 構文
このうち、最初の3項目についてはすでに紹介済みですので、今回は4以降について紹介します。
配列スプレッド構文
スプレッド構文により、配列の内部で別の配列の自動アンパックができるようになります。このスプレッド構文はJavaScriptに実装されている機能に類似しているものです。例えば、以下のコードを見てみましょう。
リスト1 配列スプレッド構文の例
———————————————————-
$a = [3,4];
$b = [1,2,…$a,5]; // 配列 $b=[1,2,3,4,5]
function foo() { for ($i=1;$i<5;$i++){ yield $i;}}
$c = […foo()]; // 配列 $c=[1,2,3,4]
———————————————————-
配列$bを定義する際に‘…’の後に別の配列$aを指定すると、その配列の要素が自動的に展開されます。この構文では、上記の配列$cのように、配列の代わりにイタレータを指定することもできます。
アロー関数
この機能は、匿名関数の記述を簡略化してくれるものです。以下のようにarray_map関数で配列を定義する例を見てみましょう。
リスト2 匿名関数を用いてarray_map関数で配列を定義する例
———————————————————-
$y=1;
$a=[1,2,3];
$c = array_map(function ($x) use ($y) {
return ($x*$x+$y); },$a)); // 配列 $c=[2,5,10]
———————————————————-
この例では、配列の要素を返す以下の匿名関数を定義して、array_map関数の第一引数に指定しています。
———————————————————-
function ($x) use ($y) {
return ($x*$x+$y);
}
———————————————————-
新しく定義されるアロー関数を使用すると、この匿名関数の記述は、以下のように簡略化されます。
————————————————————
fn($x)=>$x*$x+$y
————————————————————
従来の匿名関数と異なり、親スコープの変数($y)を値渡しで参照可能です。この構文を利用すると、リスト2に示した例は以下のようにすっきりと記述できます。
リスト3 アロー関数を用いてarray_map関数で配列を定義する例
———————————————————-
$y=1;
$a=[1,2,3];
$c = array_map(fn($x)=>$x*$x+$y,$a)); // 配列 $c=[2,5,10]
———————————————————-
なお、このアロー構文の追加により、fnはPHPの予約語となっていますので、従来のコードで使用されていないことを確認する必要があります。
??=構文
ある配列の要素が定義されているかどうかが不明な場合、従来、PHP 5までは例えば3項演算子(A ? B : C)を用いて以下のような記述をしていました。
———————————————————-
$a[‘user’] = isset($a[‘user’]) ? $a[‘user’] : ‘nobody’;
———————————————————-
この例では、$a[‘user’]が定義されているかを確認し、確認されていない場合には、デフォルト値として’nobody’を代入します。
PHP 7.0では、NULL合体演算子が導入され、上記の例を以下のように簡易的に記述できるようになりました。
———————————————————-
$a[‘user’] = $a[‘user’] ?? ‘nobody’;
———————————————————-
NULLをチェックする部分の記述が不要となり、シンプルに記述できるようになりました。PHP 7.4では、この構文が更に簡略化され、以下のように記述できるようになります。
———————————————————-
$a[‘user’] ??= ‘nobody’;
———————————————————-
この構文は、NULL合体演算子を拡張したもので、ある変数がNULLである場合にはデフォルト値を代入するといったような標準的な処理をよりシンプルに記述できるようになります。
次回以降では、残るPHP 7.4の紹介と一部廃止される機能などについて紹介する予定です。