JPHPというJavaで書かれたPHP処理系があります。これは2013年10月にはじまった新しいプロジェクトで、大半をメイン開発者一人で開発しているようですが、既に一定以上の完成度です。また、内部的にはPHPファイルを解釈して直接Javaバイトコードにコンパイルしており、かなり高速なPHP処理系になっています。
他のPHP処理系と異なり、既存のPHPコードをWebサーバ上で動かすことを重視していないようにも見えますが、興味深いプロジェクトですので紹介します。
JPHPを動かしてみる
さっそくJPHPを試してみましょう。現時点ではGitHubからソースコードを持ってきてビルドする必要があります。JDK7以上が必要ですので注意してください。JPHPのドキュメント「Getting Started」の通り、次のようにしてJPHPがビルドできます。
$ git clone https://github.com/jphp-compiler/jphp.git $ cd jphp $ chmod +x gradlew $ ./gradlew dist
ビルド後、次のようにすればJPHPが実行できます。
$ build/dist/jphp -f jphp-example-project/src/main/php/bootstrap.php Hello World! $
ベンチマークテスト
PHPソースコード付属のベンチマークテストZend/bench.phpを動かしてみました。ただし、このbench.phpのテストのうちmandel()だけは無限ループしてしまうようだったので、コメントアウトして実行しました。
PHP7 | PHP 5.6 | Phalanger | Quercus | JPHP | |
---|---|---|---|---|---|
simple | 0.094 | 0.112 | 0.069 | 0.195 | 0.058 |
simplecall | 0.028 | 0.116 | 0.019 | 0.179 | 0.009 |
simpleucall | 0.054 | 0.112 | 0.021 | 0.212 | 0.061 |
simpleudcall | 0.053 | 0.116 | 0.021 | 0.229 | 0.075 |
mandel2 | 0.347 | 0.356 | 0.584 | 0.769 | 0.315 |
ackermann(7) | 0.078 | 0.140 | 0.044 | 0.264 | 0.132 |
ary(50000) | 0.008 | 0.023 | 0.025 | 0.097 | 0.073 |
ary2(50000) | 0.008 | 0.019 | 0.017 | 0.044 | 0.049 |
ary3(2000) | 0.136 | 0.152 | 0.338 | 0.394 | 0.138 |
fibo(30) | 0.181 | 0.374 | 0.151 | 0.609 | 0.183 |
hash1(50000) | 0.017 | 0.029 | 0.116 | 0.114 | 0.077 |
hash2(500) | 0.017 | 0.035 | 0.073 | 0.062 | 0.105 |
heapsort(20000) | 0.067 | 0.092 | 0.116 | 0.310 | 0.216 |
matrix(20) | 0.069 | 0.086 | 0.136 | 0.164 | 0.100 |
nestedloop(12) | 0.156 | 0.199 | 0.115 | 0.256 | 0.076 |
sieve(30) | 0.041 | 0.089 | 0.114 | 0.151 | 0.089 |
strcat(200000) | 0.010 | 0.013 | 0.013 | 0.041 | 0.033 |
Total | 1.363 | 2.061 | 1.969 | 4.089 | 1.787 |
JPHPはPHP7より遅いものの、PHP5.6/Phalangerと互角もしくは僅かに速いという結果になりました。ところで、上の結果はJavaバイトコードへのコンパイルやJITコンパイルの時間が含まれています。Webサーバ上での動作を想定すると、このような処理は事前に行うことができますから、真の実力を測る意味でベンチマーク関数を2回呼んだ上で2回目の実行時間だけを測定してみました。
PHP7 | PHP 5.6 | Phalanger | Quercus | JPHP | |
---|---|---|---|---|---|
simple | 0.090 | 0.109 | 0.061 | 0.128 | 0.026 |
simplecall | 0.027 | 0.117 | 0.018 | 0.125 | 0.004 |
simpleucall | 0.050 | 0.115 | 0.019 | 0.170 | 0.051 |
simpleudcall | 0.052 | 0.113 | 0.019 | 0.235 | 0.036 |
mandel2 | 0.336 | 0.336 | 0.575 | 0.770 | 0.205 |
ackermann(7) | 0.074 | 0.133 | 0.041 | 0.208 | 0.113 |
ary(50000) | 0.008 | 0.020 | 0.018 | 0.015 | 0.022 |
ary2(50000) | 0.006 | 0.018 | 0.020 | 0.020 | 0.033 |
ary3(2000) | 0.112 | 0.165 | 0.332 | 0.362 | 0.156 |
fibo(30) | 0.179 | 0.349 | 0.147 | 0.524 | 0.185 |
hash1(50000) | 0.017 | 0.030 | 0.116 | 0.034 | 0.035 |
hash2(500) | 0.016 | 0.033 | 0.066 | 0.047 | 0.038 |
heapsort(20000) | 0.067 | 0.087 | 0.105 | 0.180 | 0.116 |
matrix(20) | 0.069 | 0.083 | 0.127 | 0.131 | 0.068 |
nestedloop(12) | 0.158 | 0.190 | 0.108 | 0.244 | 0.082 |
sieve(30) | 0.040 | 0.088 | 0.106 | 0.111 | 0.038 |
strcat(200000) | 0.010 | 0.013 | 0.012 | 0.018 | 0.006 |
Total | 1.309 | 2.000 | 1.891 | 3.321 | 1.213 |
JPHPが全テストの合計で見ると一番良い結果になりました。mandel2の寄与が大きいので単純に最速とは言えませんが、PHP5.6/Phalanger/Quercusとの比較では高速だと言えそうです。
互換性
JPHPは名前空間や配列の短縮構文、traitやgotoなど新しめのPHPの機能を実装していますが、PHP標準関数については未実装のものが多い印象です。既存のPHPコードを動かそうと思うとまだまだ苦労するでしょう。ちなみに、僕は上のZend/bench.phpを動かすために4個のPull Requestを投げました。
また、現時点でのJPHPで「==」で文字列同士を比較すると常に文字列として比較され、数値文字列同士の比較は考慮されていません。ドキュメントにもPHPやHHVMを置き換えるものではないと書いてあるなど、中の人が互換性にさほどこだわりが無いのかもしれません。
JPHPの狙い
ところで、JPHPではSwingを使ったGUIプログラミングができます。たとえば次のようなコードを実行すると各種OS上で新規ウィンドウが開きます。
<?php use php\lang\System; use php\swing\SwingUtilities; use php\swing\UIForm; use php\swing\UIManager; use php\swing\UIButton; UIManager::setLookAndFeel(UIManager::getSystemLookAndFeel()); SwingUtilities::invokeLater(function(){ $form = new UIForm(); $form->size = [500, 300]; $form->moveToCenter(); $button = new UIButton(); $button->size = [300, 40]; $button->position = [100, 100]; $button->text = 'close'; $form->add($button); $button->on('click', function(){ System::halt(0); }); $form->on('windowClosing', function(){ System::halt(0); }); $form->visible = true; });
このように、PHP文法でWebサーバ以外で使われるプログラムを書くこともJPHPの狙いの一つのようです。他にもデーモンを立ててTCP接続を待ち受けるような使い方や、Androidのコードを書くなどもできるようです。
実装を確認する
JPHPのレキサ・パーサは手書きです(jphp-core/src/org/develnext/jphp/core/tokenizer/ および jphp-core/src/org/develnext/jphp/core/syntax/ 以下)。実装力がある人にとっては既存のパーサジェネレータを覚える方がコストなのかもしれないですが、プロジェクト外の人がクイックハックしたいような場合には逆にコストが上がるので、善し悪しある部分かと思います。とはいえ、僕は今回そのあたりのソースコードも触りましたが、初見でも把握できる程度には整理されている印象でした。
Javaバイトコードの生成にはASMというライブラリを使っています。jphp-core/src/org/develnext/jphp/core/compiler/jvm/ 以下で構文木からバイトコード生成を行っており、この部分だけで7000行ほどの分量になっています。Javaバイトコードを自分で組み立てるというのは知識の意味でも根性の意味でもハードルの高い作業だと思うんですけど、メイン開発者が余暇に一人で書いたということなんでしょうか。世界は広いですね。
まとめ
Javaで書かれたPHP実装のJPHPを紹介しました。PHPソースコードからJavaバイトコードにコンパイルするような実装になっており、ベンチマークテスト結果によれば他の実装と比べても高速な処理系であることがわかりました。JPHPがJVMの実力を十分引き出した結果だといえるでしょう。
JPHPのメイン開発者のDmitriy Zayceffさんはロシア人で、現状ではロシア語の資料の方が多いくらいの状況ですから、OSSとして見ると知名度向上が最初の課題になりそうです。商用環境で使えるようになる日は遠いかもしれませんが、趣味で触る分には面白い処理系だと感じました。
ちなみに、作者の方が忙しいのかここ数ヶ月ほど開発が止まっているようですが、Pull Requestの返事は超スピードで来ました。読者の方もJVMの勉強がてら触ってみてはいかがでしょうか。
参考URL
- jphp-compiler/jphp
- JPHP — Новый движок php для Java VM + JIT / Хабрахабр ロシア語の紹介記事&プレゼン資料。僕には読めません!
- 今回僕が投げたPull Request
- Implement gettimeofday() function by hnw · Pull Request #163 · jphp-compiler/jphp
- Implement range() function by hnw · Pull Request #164 · jphp-compiler/jphp
- Support comma-separeted expressions in condition of FOR statement by hnw · Pull Request #165 · jphp-compiler/jphp
- Fix casting numeric string as array key by hnw · Pull Request #166 · jphp-compiler/jphp