php7に更新したら使ってみたいと思っていたツールがありました。
このphpの静的解析ツールツールの名前は、Phan。ファンと呼びます。多分、楽しいという英語のfunにひっかけて。Githubでは、以下で公開されています。
さて、これがどうして重要なツールかというと、
phpはもともと開発の敷居を落とすためにCやJavaなどと違ってデータタイプを宣言しなくてよいプログラム言語、しかしプログラムが大きくなり複雑になってくると逆にその緩さが多くの間違いの原因となります。
例えば、
function double($i)
{
  return $i*2;
}
$x = '文字列';
echo double($x);
これを実行するとphpではエラーなしに、0を返します。関数doubleは数字を引数に期待しているはずなのに、phpは数字でなく文字列を渡しても問題なく処理してしまいます。もちろん、ここでの実行は意図的ですが、もし間違いで文字列を渡してしまったら、そのコードがもし他のコードの奥に隠れていたら、原因がわからないバグになりかねません。
phanは、ここの間違いをphpのプログラムを実行することなし(それゆえに静的解析)に見つけてくれる重要な開発検証ツールなのです。
まずは、phanのインストールからなのですが、今回はphanの紹介ということで、Laravelなしの環境でのインストールです。次回には、より複雑なLaravelプログラムの環境でのphanの実行を説明します。
さて、簡単にインストールできるよ、と行きたいのですが、実際は少々ややこしく、astのモジュールとphanのパッケージの2ステップのインストールとなります。
1.astのモジュールのインストール
ast(abstract syntax tree、抽象シンタックスツリー)は、phanに不可欠なphpプログラムの解析モジュールで、特定のプログラムでなくマシン全体でのグローバルのインストールとなります。そして、多分たいていの環境ではデフォルトでインストールされていないと思います。
まずは、そのモジュールが存在するかどうかのチェックとして、
$ php -m
の実行で、astがでて来なかったら、以下を実行してください。
$ pecl install ast
その後、/etc/php.iniを編集して、
extension=ast.so
を追加します。
残念ながらここでくじけたら、もうphanは使えません。くじけたくないなら、以下の開発者のgithubを見てください。
https://github.com/nikic/php-ast
話それますが、このastの開発者、いろいろ面白いツール作成しています。プログラムのコンパイルなどの理論に興味のある方は是非以下も。
2.phanのインストール
次は以下を実行して、パッケージをインストールします。
$ composer require --dev phan/phan
--devは、開発だけに使用という意味です。
これで、現在のディレクトリは、
.
├── composer.json
├── composer.lock
├── test.php
└── vendor
    ├── autoload.php
    ├── bin
    ├── composer
    ├── felixfbecker
    ├── netresearch
    ├── nikic
    ├── phan
    ├── phpdocumentor
    ├── psr
    ├── sabre
    ├── symfony
    └── webmozart
となります。test.phpは先ほどの例のプログラム。
3.phanの実行
実行は簡単です。ターゲットのファイルを指定するのみです。
$ ./vendor/bin/phan test.php
これを実行すると、何も出力ありません!
何がおかしいのでしょう?
phanは、エラーを出すには関数の引数のデータタイプが必要なのです。phpではそのためにDocblock(ドクブロック)をコメントの中に入れる必要あります。test.phpを編集して、@param float $iを関数定義の直上のコメントに入れます。
/**                                                                                                                                                                                                                
 * @param float $i                                                                                                                                                                                                   
 * @return float                                                                                                                                                                                                    
 */                                                                                                                                                                                                                
function double($i)                                                                                                                                                                                                
{                                                                                                                                                                                                                  
  return $i*2;                                                                                                                                                                                                     
}                                                                                                                                                                                                                  
                                                                                                                                                                                                                   
$x = '文字列';                                                                                                                                                                                                     
                                                                                                                                                                                                                   
echo double($x); 
再度、phanを実行すると、
test.php:14 PhanTypeMismatchArgument Argument 1 (i) is string but \double() takes float defined at test.php:7
とエラーを出してくれます。もちろん、$x = 1.5と数字ならエラーは出ません。
簡単なphanの実演でしたが、このツールを活用するにはDocblockの作成がまず必要ということです。Docblockの作成はちょっと面倒な作業かもしれません。しかし、phpはバージョン5から、関数で型宣言ができ、sublimeなどの最近のエディターはそれを探知して、自動的にエディタでドクブロックも作成してくれます。また、それをもとにAPIドキュメントも作成してくれるツールもあります。ドクブロックは必須となっている昨今です。しかし、最終的には、1つの間違いでも減らしたいかどうかなのです。
 
 
最後に、このツールは、つい最近までは、phan/phanではなくetsy/phanという名前でした。
 

 
このetsy(エッチーあるいはイッチーと発音)というのはetsy.comハンドクラフトの人が自分の作品を販売できるサイトです。基本的にはハンドメードのものしか販売ダメという方針で人気があり、2014年末で登録会員(売る方でなく買う方)がなんと5400万人という。
そして凄いことに、この大きなスケールのサイトは、なんとphpで動いています。さらにでかいFacebookもPHPという話だからそう驚く必要はないですけれど。そして、それゆえに、今回のようなphpの開発のためのプログラムツールを公開してくれています。
メルマガ購読の申し込みはこちらから。