PHP CS Fixer v2 でもっと快適PHPライフ
2014年に書いた PHP CS Fixer の記事 が今でも読まれているのですが、2016年末にリリースされた PHP CS Fixer v2.0 で後方互換のない変更が多く入っており情報が古くなっているため、改めて v2 について書いてみようと思います。なお現時点での最新版は v2.1.2 となります。
PHP CS Fixer とは
PHP CS Fixer は PHP コードをコーディング規約 (CS = Coding Standards) に沿って補正してくれるライブラリです。歴史を辿ると、元々は Symfony のプロジェクトリードの fabpot 氏が作成したもので、 PSR-½ 、あるいは Symfony のコーディング規約 に沿ってコードを補正する機能が実装されています。
チームでコードを書くときはコーディング規約に沿って書くのが大前提ですが、例えばコードレビューをするときにいちいちコーディング規約の指摘をするのも不毛ですし、かといってメンバーが全員 100% 守れるかというとそれもなかなか大変なわけですが、 PHP CS Fixer を導入することでコーディング規約に沿ったコードに自動変換をかけられるためそういったわずらわしさから解放される、とてもすばらしい仕組みなわけです。
基本的にはコマンドラインツールとして提供されていて、ファイル単位や、特定のディレクトリ以下に適用するなど、 CLI での操作に慣れていればとてもかんたんに使うことが可能です。今日ではメジャーなライブラリとなっており、主要な IDE との連携も様々用意されています。
前回の記事では、 Git のコミットフックで補正する方法を書きましたが、現在僕は保存時に都度 PHP CS Fixer で補正をするように IDE を設定して使っており、今時のだいたいの PHPer は IDE でコード書いてると思うので保存の際に自動補正という結論でよいと思います。
PHP CS Fixer v2 の変更点と使い方
前回の記事の時点では v1.2 でしたが、 v1.13 を経て現在は v2.1 までリリースされています。正直あまり細かくは追っておらず、ある日バージョンアップしたら v2.0 になって色々動かなくなったので、改めて README に目を通した程度なので、ざっくりとした解説となります。
これまでのバージョンアップの流れを見ると、コア機能自体に大きな変更はなく、拡張性・柔軟性の向上と、ルールの細分化・多様化といったことが主な変更内容に見受けられます。
レベル/フィクサーからルール/ルールセットに
v1.x は PSR-½ といったレベル (Level) の設定と、補正項目に当たるフィクサー (Fixer) でコーディング規約の設定をしていましたが、これらの扱いが「ルール (Rule) 」と「ルールセット (RuleSet) 」に変更されました。1つ1つの補正ルールと、複数の補正ルールの集合という形となり、再帰的なルール管理ができる他、複数のルールセットを適用できるため、より柔軟なルールの管理ができる構造になっています。
再帰的なルール管理とは、例えば 「Symfony」というルールは「PSR-2」を内包し、「PSR-2」は「PSR-1」を内包する、といった具合で活用されています。
複数のルールセットの適用については、ルールセットもルールの1つとして設定するような仕組みになっており、例えば次のコマンドのように「@
で始まっているのがルールセット」「それ以外を単一のルール」として --rules
オプションで一括設定できるといったものです。
(ちなみに v1 で level と fixers を分離したのは僕だったりします)
$ php-cs-fixer fix /path/to/src --rules='@PSR2,@MyCompanyRuleSet,my_project_rule'
PHP のバージョンアップ用のマイグレーションルール (@PHP71Migration
など) も用意されており、この構造変更により使い方の幅が広がりました。
ルールの一覧は README 、 v1 からルール名が変更されたものの確認は UPGRADE をご覧ください。
挙動が変わる恐れのあるルールかどうか区別できるように
ルールの中には挙動が変わる恐れがあるもの(リスキーなルール)があります。例えば ereg_to_preg
は ereg 系関数を preg 系関数に置き換えるというルールです。こういったルールについては risky
というフラグが設定されており、 --allow-risky
オプションをつけて呼び出さないと実行に制限がかかる仕様になりました。
名前空間の変更
名前空間が Symfony\CS
から PhpCsFixer
に変更されました。前回の記事で GitHub のリポジトリオーナーが FriendsOfPHP に移管された話を書きましたが、コード側も Symfony から独立したものになりました。合わせて Composer での名称が fabpot/php-cs-fixer
から friendsofphp/php-cs-fixer
になっています。
.php_cs.dist
の導入
プロジェクトごとのルールの設定を .php_cs
というファイルで管理できるようになっていましたが、各自の環境ごとにカスタマイズできるよう、標準設定としての .php_cs.dist
と個別設定用の .php_cs
という扱いで分けられるようになりました。PHPUnit の phpunit.xml.dist
なんかと一緒ですね。プロジェクトのルートディレクトリに .php_cs
があればそれを、なければ .php_cs.dist
を参照します。
Git でプロジェクト管理をしている場合、 v1 までは .php_cs
ファイルを作成してリポジトリに格納していましたが、 v2 を使う場合は .php_cs.dist
としてリポジトリに格納しておき、 .php_cs
は .gitignore
に指定しておきましょう。
.php_cs.dist
の書き方について
改めて .php_cs.dist
の書き方を見てみましょう。まずライブラリの名前空間が PhpCsFixer に変更されています。ルールの設定は setRules()
で行いますが、設定したいルール名を配列のキーに、 true
で有効、 false
で無効となります。ルールによってはオプションの設定もできるようになっています。
ちなみにオプション付きのルールをデフォルト設定で有効化したい場合は []
を指定するのではなく true
を設定します。気になって実装を見てた感じだと、 []
を渡すと false
に変換されて無効になるようでした。
CLI の --allow-risky
オプションに相当するのが setRiskyAllowed()
です。デフォルトでは false
なので、有効にしたい場合は true
にしましょう。
<?php // .php_cs.dist namespace PhpCsFixer; return Config::create() ->setRiskyAllowed(true) ->setRules([ '@PSR2' => true, 'single_import_per_statement' => false, 'array_syntax' => [ 'syntax' => 'short', ], )]) ->setFinder( Finder::create() ->exclude('resources') ->in(__DIR__) ) ;
ルールの一部解説
ルールは PSR や Symfony のコーディング規約に定義されたものを細分化して定義されていて様々な種類があらかじめ用意されています。また、特定のコーディング規約に含まれていないルールもあるため、余裕がある方は README に書かれているルールを一通りチェックしてみるとよいかと思います。 ただ、正直 README だけだとどういう挙動になるのか判断つかないものもあって、 テストケース を確認しながら設定していったので、この辺はもうちょっと充実していると助かりますね。
(追記)
@fivestr @fivestr describe <name>でルール詳細が分かりますよ!
— Akihito Koriyama (@koriym) 2017年3月30日
describe
コマンドなるものを教えていただきました。実行結果イメージを確認できるのでとてもわかりやすいですね。
(追記おわり)
せっかくなのでいくつかのルールをピックアップして紹介してみます。
ちなみにルールを適用した場合に手元のコードがどうなるのか確認したい時は、 CLI で --dry-run
オプションと --diff
オプションをつけて実行すると適用した場合の差分が確認できます。
$ php-cs-fixer fix /path/to/src --dry-run --diff
@PSR1
, @PSR2
, @Symfony
, @Symfony:risky
: 標準ルールセット
PSR-2 にリスキーなルールはありませんが、 Symfony コーディング規約は一部リスキーなものを含んでいるため、リスキーなものを含む Symfony のコーディング規約を指定する場合は2つのルールを設定します。
<?php return Config::create() ->setRules([ '@Symfony' => true, '@Symfony:risky' => true, ]);
ちなみに僕は基本的に PSR-2 をベースにその他のルールから必要な分だけピックアップして使うようにしています。 Symfony のコーディング規約は比較的細かく、以前指定していた際に思わぬタイミングで変更が入ったり、バージョンアップで挙動が変わったりと煩わしいケースがあったので、こだわりがある人は PSR-2 + 必要なルールを定義すると安定します。
@PHP70Migration
, @PHP71Migration
: PHP のマイグレーション用ルールセット
PHP のバージョンアップに追従する用のルールセットもあります。この辺はバージョンあげる際に CLI で実行して差分を見るとかがよいのかなと思いますが、あんまり大したことはしてないので一応あるくらいでみておくとよいでしょう。
array_syntax
: 配列の表記を array()
or []
に統一
配列の表記を統一するルールです。 v1 の short_array_syntax
を設定したい場合は、 syntax
オプションを 'short'
にします。
<?php return Config::create() ->setRules([ 'array_syntax' => [ 'syntax' => 'short', ], ]);
binary_operator_spaces
: =>
や =
のアライメント
連想配列や定数・変数定義で =
や =>
の位置揃えに関するルールです。 align_double_arrow
と align_equals
の2つのオプションがあり、 true
で揃える、 null
で何もしない、 false
で揃えない(方向に補正)となります。デフォルトではどちらも false
で、 @Symfony
だとデフォルトで有効です。
どちらもデフォルトで false
となっており、
下記の例は =>
だけ揃えて、 =
は何もしないという指定です。
<?php return Config::create() ->setRules([ 'binary_operator_spaces' => [ 'align_double_arrow' => true, 'align_equals' => null, ], ]);
blank_line_before_return
, function_typehint_space
, method_separation
: 空白や空行の設定
return
の前やメソッド定義の間は空行を入れるとか、タイプヒントと変数の間にはホワイトスペースを1つ入れるとか、リーダビリティに関するルールです。この辺は @Symfony
で有効になっていますが、可読性のためにも基本有効にしておくとよいと思っています。
だいたい、タイプヒントと変数定義の間にホワイトスペースを入れるとか当たり前だろくらいに思いますけど、用意されているルールにはこういう当たり前だろって思うようなものもたくさんあり、だからこそ確実に設定しておきたいところです。
<?php return Config::create() ->setRules([ 'blank_line_before_return' => true, 'function_typehint_space' => true, 'method_separation' => true, ]);
header_comment
: PHP ファイルの共通コメント設定
コピーライトの表記などをすべてのファイルに共通で記載することができるルールです。たとえば公開用のライブラリでライセンス等を明記しておきたいとか、社内のルールで全てにコピーライトを記載する必要があるとか、あるいは自己顕示欲が強い方とかは活用するとよいでしょう。
<?php $header = <<<'EOF' (c) Ancar Inc. EOF; return Config::create() ->setRules([ 'header_comment' => [ 'header' => $header, ], ]);
no_unused_imports
, ordered_imports
: インポートの整理
no_unused_imports
は未使用の use
文の削除、 ordered_imports
は use
文のソートを行うルールです。 use
文が整理されていた方が依存状況が確認しやすいため、有効にしておくことをおすすめします。
<?php return Config::create() ->setRules([ 'no_unused_imports' => true, 'ordered_imports' => true, ]);
ちなみに ordered_imports
を有効にした場合、 グルーピングしている部分もソート対象になります。
<?php // before use Model\{User, Article}; // after use Model\{Article, User};
return_type_declaration
: リターンタイプヒントのフォーマット
PHP 7.0 から導入されたリターンタイプヒントの書式ルールです。有効にすると function (): string
のように :
の後ろに1つホワイトスペースが入るようになります。 space_before
オプションを 'one'
にすると function () : string
のように :
の前にもホワイトスペースが入ります。
<?php return Config::create() ->setRules([ 'return_type_declaration' => true, ]);
single_import_per_statement
: インポートを1クラスごとに展開
1つの use
文につき1つのクラス定義のみ行うようにするルールです。これは @PSR2
に含まれているルールですが、 PSR-2 は PHP 7 が考慮されておらず、{...}
を使ってグルーピングしているものも展開されてしまうため、僕はあえて無効にしています。
その辺は PSR-12: Extended Coding Style Guide の策定以降により細かく調整されるかと思いますので、 PSR-12 を待ちましょう。
<?php return Config::create() ->setRules([ '@PSR2' => true, 'single_import_per_statement' => false, ]);
まとめ
PHP CS Fixer も v2 になって基本的な API 周りはだいぶ固まったかなという印象ですね。この記事で解説した以外にも設定項目があったり、様々なルールが定義されていますので、ぜひ自分にあった形で活用してもらえるとよいなと思います。
あとは PSR の方で、 PHP 7 対応のコーディング規約が待たれるところですね。あまりそっちも追ってるわけではないためどうなるのか不明ですが、何れにしても秩序をもたらす方向に歩んでいることはとてもすばらしいと思います。
コーディング規約を詳細に書いたドキュメントを用意する以上に .php_cs.dist
にコーディング規約が定義されている状態の方が建設的ですし、 Coding Standards as Code な世界をエンジョイしましょう。
最後に、今僕がプロジェクトで使ってる .php_cs.dist
を参考までに載せておきます。割と細かく指定があり全部解説はできないのですが、適用ルール把握のために PSR-2 + 個別ルールで設定・リスキーな補正はしない・言わなくてもやるようなルールであっても設定する、というポリシーで用意したため、チームの環境や文化に合わせて活用するとよいでしょう。