こんにちはこんにちは!!
ヒコーキ好きのみこやんです。
前回は、Vimmerとして現代的なIDEのようなCode FixerやLinterなどを使って、編集中のコードを動的にチェックして警告やエラーを発してもらうことで即座に不具合を修正するために、ALEというプラグインを導入したお話をしてみました。
そしてPHPerな私としては、やはりphp-cs-fixerの開発支援を受けながら効率の良い実装をすすめられるようにしました。
今回は、php-cs-fixerのコーディングルールにプロジェクト内のルールに整合するようにしてみよう、という試みです。
プロジェクトオリジナルのコーディング規約を適用したい
多くのプロジェクトと同様に、弊社のプロジェクトにも「コーディング規約」を設けています。
コーディングルールとも呼ばれますが、これをプロジェクト共通で適用することにより、コード品質や可読性を一定水準に保つことを心がけていくことになります。
一部では、こうしたルールがプログラマやエンジニアの自由な開発・実装に対して大きな阻害要因となる上に、製品クオリティを担保することができないといった否定的な見解もあるようです。
意見はさまざまあると認識していますが、少なくとも私の関わっているプロジェクトの場合、コーディングルールは品質保証やクオリティの担保で用いられることはありません。あくまでもコード可読性に由来する保守性の担保としてルールを設定しています。
基本的には、PSR-12などの標準的なルールを適用していますが、やはりプロジェクトのコーディグンスタイルの歴史的理由や経緯から、標準に完全に従うのは難しい場面もあります。
そのため、プロジェクト固有のルールをphp-cs-fixerに適用してもらうことで、プロジェクト独自のコーディングスタイルと標準のコーディング規約を共存させ、プロジェクトにマッチしたスタイルのコードベースを持続させることができます。
php-cs-fixerでは、プロジェクトルールファイルを用意して、カスタム仕様にチューニングしていこうと思います。
カスタムルールファイルの作成
専用のルールファイルを作成することで、プロジェクト用のルールを作成できます。
このファイル名やファイルの保存場所自体はどこでも構いません。リポジトリルートに設定して共有することもできるでしょうし、個人のホームディレクトリやデスクトップなどに配置し、使用することもできます。
私の参加しているプロジェクトでは、特にルールファイルを共有せず、独自で設定していため、リポジトリルートなどにはありません。
そこで、ホームディレクトリに .php-cs-fixer というディレクトリを作成し、この中に my-rules.php という個人ルールファイルを作成して運用しています。
1 2 3 4 |
cd ~/ mkdir .php-cs-fixer cd .php-cs-fixer touch my-rules.php |
php-cs-fixer のルールファイルは、PHPのコードとして作成します。
他のfixerなどでは、XMLファイルでルールファイルを作成するものもあるようですので、共通化は難しそうです。
もっとも、原則的にはプロジェクト内でfixerやlinterは同じものを使用するのが通例でしょうから、あまり気にすることはないと思いますが、fixerが変更されたときにルールファイルを手動で変換しなければならないのがVimmerの泣き所と言えそうです。
.vimrcの設定
.vimrcファイルへの設定は、以下の1行のみになります。
バージョンによってはコマンドラインオプションの指定方法が異なる場合がありますので、以下の設定でうまく適用されない場合は、ご自身のphp-cs-fixerのバージョンではどのように指定するかを確認してみてください。
1 |
let g:ale_php_cs_fixer_options = '--config=' . $HOME . '/.php-cs-fixer/my-rules.php' |
カスタムルールファイルの作成
まずは、ファイルの全内容を以下に示してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?php declare(strict_types=1); $finder = PhpCsFixer\Finder::create() ->exclude('vendor') ->in(__DIR__); return (new PhpCsFixer\Config()) ->setCacheFile(__DIR__.'/.php_cs.cache') ->setRules([ '@PSR12' => true, '@PhpCsFixer' => true, 'array_syntax' => ['syntax' => 'short'], 'multiline_whitespace_before_semicolons' => [ 'strategy' => 'no_multi_line' ], 'no_superfluous_phpdoc_tags' => false, 'ordered_class_elements' => [ 'order' => [ 'use_trait', 'constant_public', 'constant_protected', 'constant_private', 'property_public', 'property_protected', 'property_private', 'construct', 'destruct', ], ], 'php_unit_internal_class' => false, 'php_unit_test_class_requires_covers' => false, 'phpdoc_align' => [ 'tags' => [ 'method', 'param', 'property', 'type', 'var', ], ], 'yoda_style' => false, ]) ->setFinder($finder); |
それほど大きなファイルではないですし、コードというよりはやはり設定に近いように見えます。
なお、設定内容については、PHP-CS-Fixer configurationに記載されています。バージョン別に表示できるようになっていますので、ご自身のphp-cs-fixerのバージョンで適用可能なルールを一覧で確認することができます(ただし英語です)。
主要なところだけ解説してみます。
対象外ディレクトリを指定する
さすがにvendor配下まで適用されてしまうと問題なので、以下のようにLinterとFixerの適用外ディレクトリを設定します。
1 2 3 |
$finder = PhpCsFixer\Finder::create() ->exclude('vendor') ->in(__DIR__); |
キャッシュファイルを指定する
デフォルトでも問題ないという人は未設定で問題ありませんが、任意の場所にphp-cs-fixerの結果のキャッシュファイルパスを指定したい場合は、以下のようにキャッシュファイルのフルパスを指定します。
1 2 3 |
return (new PhpCsFixer\Config()) ->setCacheFile(__DIR__.'/.php_cs.cache') ..... |
標準ルールを適用する
私が所属するプロジェクトでは、PSR-12のルールを採用しているので、以下のように指定しています。
また、もっとも細かくルール設定ができるPhpCsFixerを同時に採用します。
1 2 3 4 5 |
..... ->setRules([ '@PSR12' => true, '@PhpCsFixer' => true, ..... |
ただし、これ以降説明するように、これらのルール中にはプロジェクトに不向きなルールも多数存在します。
これらを個別に調整することで、プロジェクトに合わせていきます。
配列の定義方法
PHPでは従来より配列はarray()を用いて定義していましたが、PHP5.4から[ ]を使った定義ができるようになりました。
可読性や見栄えの観点を考えるとarray()は使わず[ ]を使いたいため、以下のルールを適用しています。
1 2 3 |
..... 'array_syntax' => ['syntax' => 'short'], ..... |
ちなみにshortではなくlongを指定すると、array()が適用されるようになります。
セミコロンの手前に空白や空行を入れない
通常セミコロンは、文や式の末尾に付けますが、空白や空行を入れるなどをする書式があります(らしいですね…)。
これを、ちゃんと文や式の末尾に空白等をあけないようにします。
1 2 3 |
..... 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'] ..... |
言葉ではわかりにくいので、例示します。
たとえば、以下のようにセミコロンの手前に空白があったり、改行されていたりした場合……。
1 2 3 4 5 6 7 8 9 10 |
function func(): int { return 1 + 2 ; } $this->method1() ->method2() ->method3() ; |
……行末に付けるようにfixします。
1 2 3 4 5 6 7 8 |
function func(): int { return 1 + 2; } $this->method1() ->method2() ->method3(); |
クラス内要素の並び順の指定
クラス内に記述するプロパティやメソッドなどの要素の順番を指定します。
私の例では、use traitが上位、以下順に定数のpublic、protected、private……といった感じです。
ここに記述されている以外にも要素が指定できます。詳しくはドキュメントをご覧ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
..... 'ordered_class_elements' => [ 'order' => [ 'use_trait', 'constant_public', 'constant_protected', 'constant_private', 'property_public', 'property_protected', 'property_private', 'construct', 'destruct', ], ], ..... |
ユニットテストのPHPDocの不要アノテーションを付与しない
ユニットテストのPHPDocに、私のプロジェクトでは使用していないアノテーションが自動付与されてしまうので、いずれの設定でもfalseを指定して無効にしています。
1 2 3 4 |
..... 'php_unit_internal_class' => false, 'php_unit_test_class_requires_covers' => false, ..... |
PHPDocのアライメント制御
PHPDocのアライメントを指定しています。
1 2 3 4 5 6 7 8 9 10 11 12 |
..... 'phpdoc_align' => [ 'align' => 'vertical', 'tags' => [ 'method', 'param', 'property', 'type', 'var', ], ], ..... |
デフォルトではアライメントはvertical、つまり垂直方向に揃えられるようになります。
すなわち、以下のような処理になります。
1 2 3 4 5 6 7 8 9 10 11 |
/** * @param int $id * @param string $name * @param array $data */ /** * @param int $id * @param string $name * @param array $data */ |
ちなみに、アライメントをleftで指定すると、以下のようになります。
1 2 3 4 5 6 |
/** /** * @param int $id * @param string $name * @param array $data */ |
ヨーダ記法の禁止
最後に、かつて脚光を浴びた(と思われる)「ヨーダ記法」にfixしないように設定します。
1 2 3 |
..... 'yoda_style' => false, ..... |
ヨーダ記法については、興味のある方は各自で歴史をひもといてみてください……。
興味のない方は「いにしえの呪文(をこの設定で禁止している)」と思っていただいても結構でしょう。
おわりに
php-cs-fixerの設定はかなり多くあります。
ただし、すべての設定が必要なわけではなく、デフォルトの設定が希望動作だったり、デフォルト設定でも問題がない場合も多いはずです。
コードを書いていくうちに「あれ、この挙動はウチのプロジェクトでは必要ないな」と思った際には、さきほどの公式リファレンスを参考に設定を追加してみてはいかがでしょうか。
プログラミングもサーバもフロント(チョトだけ)もインフラ(オンプレ中心)もwebもゲームも組み込みも幅広く何でも雑多にやってきた古株ですが、フルスタックではありません。好きなものはプログラミングと計算機科学。我々と一緒に好きなプログラミングを楽しみませんか?