Engine Yard Blog RSS Feed

Building a Better PHP — Part 1: HHVM and Hack

注:これは「HHVM/Hack」シリーズのパート1です。

本記事は英語版ブログで公開された記事の翻訳版です。

Facebookは2014年3月20日、HHVM用のプログラミング言語「Hack」をリリースしました。この記事ではHHVMとは何かを紹介しつつ、Hackについて詳しく見ていこうと思います。

Facebookは(おそらく)世界最大級のPHPインスタレーションですが、近年はPHP5を離れ独自開発によるソリューションに向かっています。

ここまできても、まだPHP環境と呼べるのでしょうか?答えは「もちろん」と「とんでもない」の中間ぐらいといえます。

HHVM(HipHop Virtual Machine)の簡単な歴史

Facebookは初めに「HPHPc」というPHPからC++へのコンパイラを作りました。 これはFacebookの膨大なコードベースをC++コードに変換し、そこからコンパイルするというものでした――このプロセスには時間が(何時間も)かかり、パフォーマンスは高まるにせよ、それ以上に大きな負担を開発者に与えました。

これと並行して同社はHPHPi(開発者モードの一種で、毎回コードベース全体をコンパイルする必要がない)とHPHPd(デバッガ)も作りました。

HPHPcはある意味でFacebookにとっては成功でした。 ハードウェアはそのまま、従来の限界を超えるパフォーマンス向上に成功したのです。しかしHPHPcはPHP 5.2に対してビルドされており、言語の一部しかサポートしませんでした(たとえばcreate_function()やかの恐るべきeval()を使って作成されたダイナミック関数はサポート外でした)。

状況を打開する次の一手として、Facebookは2011年、HipHop Virtual Machine(HHVM)の最初の公開版をリリースしました。これはHPHPc(とその兄弟)に替わるもので、高速に動作するJIT(Just In Time)コンパイラを搭載し、PHPコードを実行時に動的にマシンコードへコンパイルできます。

HHVMは最初のリリースですでにPHP 5.4にほぼ完全対応しており、その後もアップデートを重ねてPHP 5.5以降の新機能にも対応を果たしています。

HHVMの開発チームが口癖のように繰り返しているのが「バグパリティのためのバグ」という言葉です。つまり、「完璧な」PHP実装を作ろうと思えば作れるけれども、移行を容易にするために、あえてPHPが持つエッジケースや奇妙な挙動を再現しようと試みているのです。

そう言いながらも、彼らはPHP.netに実在するバグを、後方互換性を大きく損なわずに修正できる範囲であれば修正しようと取り組んでいます。

よく知られたオープンソース テスト スイートの結果をHHVMとPHP.netとで比較することによりパリティを達成しようとするFacebookの取り組みは、こちらに公開されています。

HHVM対PHP.net

HHVMはPHP.netより高速です。多くの場合、その差は段違いです。しかしスピードがすべてとは限りません。

HHVMはもっぱらPHP.netよりパフォーマンスが向上した点で注目されがちですが、他にも考慮すべき違いがあるのは確かです。

環境への影響

といっても、CO2ガス排出規制の話ではなくて……いや、HHVMを使うとサーバーの数が劇的に減らせるので、消費電力も減りますから、そういう意味では環境に影響があるという主張もできますけどね。

技術的な意味でいう環境での影響の話です。HHVM 2.3のリリースで、FastCGIインターフェイスのサポートが追加されました。つまり、現在php-fpmを使っているなら、HHVMは実質的なドロップインになるのです。

さらに、一部のリクエストを別のFastCGIプロキシにルートすれば、PHP.netとHHVMを同時に実行することも可能です。

システム管理者にとっては、Ubuntu 12.04/13.10、Debian 7、またはFedora 20用のパッケージをHHVMチームから直接入手できるという点がポイントでしょう。

開発者にとって大きな問題点の1つは、IDEサポートがないことです――特にデバッグでこれは困りものです。コマンドラインスクリプトやPHPUnitなどを実行する都合上、HHVMバイナリをPHPバイナリに置き換えられない場合も考えられます。

しかしおそらく最大の問題点は、PHPで標準的な拡張モジュールをサポートが不足しているという点です――たとえばPECL拡張を使っている場合、そのままではまずHHVMで動きません。しかし、よく使われる拡張のほとんどについてはHHVM開発チームが積極的に実装を進めており、この点は大幅に改善されつつあります。現在利用可能な拡張の一覧はこちらで確認できます。

コミュニティへの影響

HHVMはPHPコミュニティを分裂させると危惧する人もいます――確かに、可能性がないとはいえません。しかし、HHVMチームは開発成果をPHP.netに積極的に還元(少なくともその努力は)しています。

たとえばジェネレータを初めて実装したのはHHVMですが、これは後にPHP 5.5にも追加されました。

もう1つ例を挙げると、最近arrayofシンタックスに関するRFCが却下されましたが、HHVMではそれと重なるところの多いGenericsをHack言語(詳細は後述)でサポートし、これに関する議論にも(Sara Golemon経由で)チームとして活発に参加しています。

拡張

最近はZephirPHP-CPPといったツールにより、PHP拡張に改めて熱い関心が集まっています。HHVMの成功が拍車をかけた可能性はあるにせよ、この10年間でコミュニティがエンタープライズ分野でも成熟を遂げた結果であることは疑いありません。

HHVMでは、拡張がC/C++ライブラリをラップしていないかぎり、それをそのままPHP(またはHack)で書けるのです。HHVMのマシンコードへのコンパイル効率はたいへんすばらしく、同じことを自分でC++で書くより速いとまではいかなくとも、同じぐらいには高速に動きます。

その一例が、HHVMにおけるMongo拡張の代替としてコミュニティから生まれたMongofillです。

どうしてもC++を使いたい場合、HHVMが提供するHHVMネイティブインターフェイス(HNI)が使えます。これはPHP/Hack内でスタブ関数として働き、C++で実装を書くことを可能にしてくれます。

HHVM用の拡張の実装について詳しくは、HHVMのExtension APIに関するドキュメントを参照してください。

Hackって?

HHVMは一般的にPHPの新しいランタイムととらえられていますが、Facebookの独自「言語」であるHackのランタイムでもあります。HackはPHPの文法的な兄弟分というべき言語で、引数と戻り値両方にスカラー値を指定できるタイプヒンティング、ジェネリクス、コレクション、コンストラクタ引数プロモーションといった数々の新しい機能を備えています。

Hackは標準的なPHPシンタックスに加えてサポートされるという形になっており、実は開発者ツールとしての色彩が強いものです――HHVMのスタティック分析ツールと組み合わせると、型の不一致などに関する情報をリアルタイムに取得できます。 また、HHVMがコードをコンパイルするときは、Hackのほうがより的確に解釈されます。

しかしコンパイルが済んでしまえば、HackコードもPHPコードもHHVMにとっては同じです。

HackとHHVMは、全体的にみて、PHPの新しい機能の可能性を試すのに絶好の環境です。将来的には多数のすぐれた機能がPHP.netに還元されることになるだろうと私たちは予想しています。

あと、たぶん史上最もググりにくい名前の言語なんじゃないかと思っています。

標準的なPHPの開始タグは「<?php」ですが、Hackコードの開始タグは「<?hh」で、終了タグは使いません。HackにはふつうのPHPのようにHTMLを散りばめることはできませんが(関心の分離のためにはよいことです)、第一級シンタックスとしてネイティブXHTMLをサポートしています。

注:XHTMLシンタックスはxhp拡張を使ってPHP.netに追加することができます。

<?hh
class Calculator {
    public function add(int $left, int $right): int {
        return $left + $right;
    }
}


$calc = new Calculator();
$calc->add(1, 3); // 4
$calc->add("one", "three"); // Fatal Error

これを実行すると結果は次の致命的エラーになります。

Fatal error: Argument 1 passed to Calculator::add() must be an instance of int, string given in <file> on line 5

HHVMのスタティック分析ツールを使うと、このエラーは「コードを実行する必要さえなく」一般的でないコードパス上にあっても発見できます。

Hackについてはこのシリーズのパート3でさらに詳しく見ていきましょう。

HHVMのインストール

HHVMのインストールはVagrant仮想マシンの起動と同じぐらい簡単です。まず次のように記述したVagrantFileを作ります。

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # Ubuntu 12.04 LTS (64bit) を使う
  config.vm.box = "hashicorp/precise64"
  config.vm.network "public_network" # ネットワークブリッジを作成する

  # HHVM をインストールする
  config.vm.provision "shell", inline:<<-shell
    apt-get update
    apt-get install python-software-properties  -y --force-yes
    add-apt-repository ppa:mapnik/boost
    wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -
    echo deb http://dl.hhvm.com/ubuntu precise main | sudo tee /etc/apt/sources.list.d/hhvm.list
    apt-get update
    apt-get install hhvm-nightly -y --force-yes
    apt-get install screen vim -y --force-yes
    debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password pa$$'
    debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password pa$$'
    apt-get install mysql-server -y --force-yes
  shell
end

VagrantFileの準備ができたら、「vagrant up」とコマンドを打って待つだけです。

マシンが起動したら「vagrant ssh」コマンドでSSHログインしてHHVMを使い始められます。

HHVMの実行

上記の要領でHHVMのインストールが済んでいれば、hhvmコマンドですぐにHHVMが呼び出せます。たとえば、Calculatorクラスがcalc.phpの中にあるとすると、次のように呼び出します。

$ hhvm calc.php

HHVMはこのようにコマンドラインから直接動かすほか、2種類の方法でWebで動かすことができます。1つ目はビルトインサーバー――最初はこの方法しかなかったのですが、最近2つ目の方法としてFastCGIが使えるようになりました。

HHVMをFastCGIとして使うには、php-fpmと同様、nginxなどのWebサーバーの後ろに置く必要があります。簡単にインストールするには次のようにします。

$ sudo apt-get install nginx
$ sudo mkdir /var/www
$ sudo chown vagrant:daemon /var/www

次に、新しいnginx構成ファイルを/etc/nginx/sites-available/hhvm.conf に追加します。

server {
  server_name hhvm.dev;

  root /var/www;
  index index.php;

  location ~ \.(hh|php)$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name;
    include fastcgi_params;
  }
}

そしてそれをデフォルトの場所にインストールします。

$ sudo rm /etc/nginx/sites-enabled/default
$ sudo ln -s /etc/nginx/sites-available/hhvm.conf /etc/nginx/sites-enabled/hhvm.conf
$ sudo service nginx restart

次に、HHVMをFastCGIとして実行するために、2.4.*以前の場合は/etc/hhvm/server.hdfを、最新のnightlyビルドまたは2.5.0以降の場合は/etc/hhvm/server.ini構成ファイルを変更します。

server.hdf

Server {
  Port = 9000
  Type = fastcgi
  SourceRoot = /var/www
}

Log {
  Level = Error
  UseLogFile = true
  File = /var/log/hhvm-error.log
  Access {
    * {
      File = /var/log/hhvm-access.log
      Format = %h %l %u %t \"%r\" %>s %b
    }
  }
}

Repo {
  Central {
    Path = /var/log/hhvm/.hhvm.hhbc
  }
}

server.ini

; php options

pid = /var/run/hhvm/pid

; hhvm specific

hhvm.server.port = 9000
hhvm.server.type = fastcgi
hhvm.server.source_root = /var/www
hhvm.server.default_document = index.php
hhvm.log.level = Error
hhvm.log.use_log_file = true
hhvm.log.file = /var/log/hhvm/error.log
hhvm.repo.central.path = /var/run/hhvm/hhvm.hhbc

HHVMを再起動(または起動)させます。

$ service hhvm restart

注: HHVM 2.5/3.0より前(または最近のnightlyビルド)では、(少なくとも)Ubuntu用のinitスクリプトは壊れています。代わりにsudo killall hhvm && sudo service hhvm startを使って再起動する必要があります。

最後にindex.phpをwebroot、/var/wwwに追加して、 Webサーバーにアクセスします(IPはifconfigを呼び出せば取得できます)。

<?php
phpinfo();
?>

HHVMの場合、これに対する出力はシンプルそのものです。

HipHop

HHVMをクラウドで使う

Engine Yardは常に最新のテクノロジーを追求しており、HHVMの動向については1年以上前からおおいに注目してきました。今のところ、非常に期待がもてる技術だという印象を受けています。当社のスタックはまだHHVMをサポートしていませんが、現在PHPスタックへの組み込みを進めているところです。

HHVMをクラウドに置ければ、クラウドリソースの効率的な活用につながり、浮いた予算を他に回すなど、費用対効果をさらに高めることができるでしょう。

さて次回は……

シリーズ次回は、コードベースを移行する準備ができていなくても**今日から**使えるような、HHVMの実践的な活用例を見ていきます。

(翻訳:福嶋美絵子)


Tagged:

comments powered by Disqus