LaravelでCS Fixerを使う

タグ: Laravel5.1LTS   Laravel   php-cs-fixer  

前日のインベントカレンダーが、Masashi ShinbaraさんによるコードフォーマットCIサービス"Style CI"の紹介でしたので、急遽その元になっているPHP CS Fixerの紹介をすることにしました。Laravel、コンテナによる依存解決とはは、こちらのLaravelアドベントカレンダーに移します。

日本語の記事としては、様々な紹介記事が既に存在しています。

PHP CS Fixir

PHP CS FixerはPHPの整形を行うコマンドラインツールです。Style CIはこのツールをオンラインで実行してくれるサービスです。

IDEやエディターにもコードの整形機能は付いています。細かく調整すれば、自分好みには整えられます。会社やチーム、特にオープンソースでコードを統一したい場合、設定を共有したり、規約を指定したりすることになります。

これをスタンドアローンのツールとして実行できるのが、PHP CS Fixerです。後に書きますが、Netbeansには統合されていますし、PhpStromなら外部ツールとして起動できます。

Laravelで使用する場合の注意点

PHP CS Fixerでは、様々なコードの整形規則を指定できます。これらはfixer(フィキサー、修正するもの)と呼ばれます。

この中のpsr0と言う規約は、名前の通りpsr0規約に合わせて名前空間の定義を変更してくれます。Laravelではpsr4を利用しています。

psr0フィキサーが起動すると、app下のクラスで指定されているnamespace定義の"App/"の部分をディレクトリー名の"app/"に書き換えてしまします。

そのため、psr0フィキサーが動作しないように指定しましょう。

インストールなど

検索してください。既に日本語情報はあります。

私はComposerのglobalオプションでインストール/アップデートしています。

コマンドの説明

Laravelのnewコマンドと同様に、本来の機能を実行するサブコマンドは一つしかありません。

$ php-cs-fixer fix

これだけで行うと、ディレクトリー下のファイルに対し再帰的に、後述するSymfonyの規約を当てはめ修正していきます。やたらの場所で行ってはいけません。基本PHPプロジェクトのルートディレクトリーへ移動し、そこで実行します。すると、プロジェクト全体のPHPファイルのフォーマットが統一されます。

もちろん、コマンドラインオプションでいろいろな指定が可能です。

$ php-cs-fixer fix --config=設定 --config-file[=設定ファイル] --level=レベル --fixers=フィキサー 対象のファイルパス

configオプション

指定する必要はありません。機能的にはフレームワークやCMSなどでfixerをあらかじめ指定できるようにするための機能です。Laravel用には用意されていませんし、既存のsf23やmagentoも、独自の設定は行っていません。(コードを見てもらえばわかりますが、スッカラからんです。)

levelオプション

レベルとは、あらかじめどのフィキサーを使用するのかを決めてある指定です。Symfonyのコードフォーマットが好きな場合は、デフォルトですので指定する必要がありません。(ドキュメントにはPSR2に加えて「いくつか」のフィキサーが動作すると書かれていますが、前述のconfigオプションのデフォルトではsymfonyレベルが設定されています。)

用意されているレベルはpsr0、psr1、psr2、symfonyです。各レベルはより下位のレベルの設定を含んでいます。ですからpsr1はpsr0のフィキサーを含み、symfonyであればpsr0から2までを含みます。

レベルに含まれているフィキサーはReadmeを読むか、コマンドのヘルプで確認してください。

fixersオプション

levelオプションでよく使用されるオプションをまとめて指定できますが、それらにフィキサーを付け加えたり、特定のフィキサーを除外できます。もちろん、levelオプションによるフィキサーの指定よりも、このfixersオプションの指定のほうが優先されます。

このオプションはフィキサーをカンマで区切り、複数一度に指定可能です。フィキサーの動作を無効にしたい場合は、フィキサー名の先頭にハイフンを付けます。

--fixers=-psr0,align_equals

Laravel5はPSR4規約で、psr0フィキサーを付けると名前空間のAppがappに置き換わると、この記事の先頭で説明しました。そこで、-psr0でこのフィキサーを無効にしています。

フィキサーには既定のレベルに含まれていないフィキサーも存在しています。その中の一つであるalign_equalsは、代入分が続く場合に、=の位置を揃えるために指定します。

config-fileオプション

こうしたレベルやフィキサーを毎回コマンドラインの引数として指定するのは面倒です。そこで、あらかじめ設定ファイルにまとめて指定できるようになっています。この設定ファイルには、どのファイルやディレクトリーに対してCS Fixerを実行するのかという指定も可能です。

デフォルトの設定ファイル名は.php_csです。このファイル名を利用する場合はconfig-fileオプションで指定する必要はありません。自動的に存在がチェックされ、存在している場合はその内容が適用されます。

とりあえずPSR-2規約のレベルまでを適用し、fixersオプションの説明のようにpsr0を無効、align_equalsを有効にしてみましょう。

return Symfony\CS\Config\Config::create()
    ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
    ->fixers(
        [
            '-psr0', // PSR−0を無効にしないと、名前空間の先頭がappに書き換えられる
            'align_equals',
        ]
    );

ご覧の通り、PHPですから簡単に指定できますね。levelとfixersオプションを同名のメソッドで指定します。

$ php-cs-fixer fix --config-file=.php_cs 対象のパス

# 起動するディレクトリーに.php_csが含まれているならば、指定省略可能
$ php-cs-fixer fix 対象のパス

ただし、この指定方法ですと、CS Fixerを実行するのと同じディレクトリーに.php_csもしくは別名の設定ファイルを指定する必要があります。異なったパスに設置するとエラーになります。(追求はしていません。)

Laravelプロジェクトを整形する

さて、Laravelプロジェクト全体に対して実行する場合を考えてみましょう。

laravelプロジェクト全体を整形する場合、たとえば外部パッケージであるvendor下のファイルまで整形されると、Composerが「ファイルが変更された」と注意してくるようになりますし、ファイル数も多いので時間がかかるようになります。他にも整形してほしくないファイルはあります。

そこで、あらかじめどのディレクトリーやファイルを整形対象にするかも、設定ファイルで指定可能です。Laravelプロジェクトに合わせて、指定してみましょう。

<?php

$finder = Symfony\CS\Finder\DefaultFinder::create()
    ->notName('README.md')
    ->notName('.php_cs')
    ->notName('composer.*')
    ->notName('phpunit.xml*')
    ->notName('*.xml')
    ->exclude('nbproject') //除外フォルダー
    ->exclude('resources') //除外フォルダー
    ->exclude('vendor')    //除外フォルダー
    ->exclude('public')    //除外フォルダー
    ->in(__DIR__)
;

return Symfony\CS\Config\Config::create()
    ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
    ->fixers(
        [
            '-psr0', // PSR−0を無効にしないと、名前空間の先頭がappに書き換えられる
            'align_equals',
        ]
    )
    ->finder($finder)
;

このファイルを適当な名前で保存しましょう。ここではプロジェクトルートに.php_cs_projectとして保存します。

これでプロジェクトルートディレクトリーに移動し、設定ファイルを指定してCS Fixerを実行すれば、必要なファイルに対してのみ整形が行われます。

php-cs-fixer fix --config-file=.php_cs_project

長いタイピングが面倒であれば、シェルのエイリアスとして設定しましょう。

この場合、$finderを指定しているため、この設定ファイルによりfixを実行するとプロジェクト全体に対して修正が行われます。

注意:ホームディレクトリーなどにこの設定ファイルを保存し、各プロジェクトから指定しようとしないでください。__DIR__で指定してあるため、ホームディレクトリー下から再帰的にディレクトリーやファイルが処理されてしまいます。その結果、メモリを食い潰すか長時間かかり、適合した全部のファイルに整形がかかります。__DIR__の代わりに、コマンドの起動パスを指定したほうが安全ですが、その場合起動パスがLaravelルートであるか確認したほうがより安心できるでしょう。

安全な運用を目指すのであれば、各Laravelプロジェクトルートへ毎回コピペするほうが楽に使用できそうです。

NetBeansで使用する

NetBeansの場合は、php-cs-fixerがPHPの開発環境と統合されているので、デフォルトで利用できます。

メニューの「ツール」カラ、「オプション」項目を選び、上部「PHP」ボタン、「フレームワークおよびツール」タブを選択します。

左に一覧がありますので、PHP CS Fixerをクリックしてください。最初の項目で、php-cs-fixerのコマンドパスを指定します。後のオプションは今まで説明した通りです。

前述の通り、設定ファイルは癖があるため、levelとfixersオプションで指定するのが素直です。

後はプロジェクトツリーから特定パスに対して実行することも、エディターで開いているファイルに対しても、コンテキストメニューから実行できます。エディターで開いている内容に対して実行する前には、内容を保存してください。エディターの内容にではなく、ファイルに対してコマンドが実行されるためです。

オプションのキーマップを使い、いつでもショートカットが割り付け可能です。

PhpStormで使用する

メニューの"File"から、"Setting..."項目をクリックします。

左側の設定ツリーから、"Tools"、"External Tools"を選びます。

+ボタンで新規追加です。

Nameは"PHP CS Fixer"、Descriptionは"php cs fixer コード整形"など、適当に指定します。

後は下の"Tool Settings"の項目を指定します。

Programingには、php-cs-fixerのコマンドパスを指定します。

Parametersには、たとえば今までの例を指定する場合、"fix --level=psr2 $FilePath$ --fixers=-psr0,align_double"

Working directoryには$ProjectFileDir$でよいでしょう。私は試していませんが、コマンドを実行するワーキングディレクトリーがこのように指定できますので、プロジェクトルートに設置した設定ファイルの指定であれば簡単にできるでしょう。

Key Mapからショートカットを割りつけることもできます。

Style CI

WebサービスのStyle CIはPHP CS Fixerをベースにして、独自のフィキサーをいくつか追加しています。たとえばStyle CIに存在するpsr4フィキサーは、PHP CS Fixerには存在しません。

PHP CS Fixerのレベルに代わるものとして、Presets(プリセット)が用意されています。PSR1、PSR2、Symfony、Laravel、Recommended(おすすめ)です。

この'Laravel'はあくまでも、Laravelのプロジェクトに対して、コード修正などの貢献を行う場合の整形ルールです。Laravelが皆さん個人、チームに対して「この整形を使え」と押し付けているわけでないことに留意してください。

使い分け

私のおすすめは、個人として読みやすい/書きやすいルールはIDEの整形機能で大まかに設定する。チームとしてのルールはphp-cs-fixerとして設定する。余裕があれば念の為にStyle CIを利用するのがよいでしょう。

オープンソースプロジェクトでは、Style CIは有用でしょう。誰から、どんなコードがプルリクされるかわからないですからね。