stout (Laravel用linter) ご紹介

目的

開発初心者向けに事前にソースコードの整形などを確認してもらうため、Laravel用のLinterを導入してもらいましたのでご紹介します。

整形時要求事項

  1. インデントはタブ使用
  2. 複数の要素に値をセットするとき “=>” で値の頭を揃えたい
  3. 不要な () を許容する(可読性のため)
  4. 論理否定のエクスクラメーションと値の間にスペースは入れない

何を使うか

johnbacon/stoutを採用することとしました。

過去にLinterとしてPHP-CS-Fixerを導入していましたが、Laravelでは2022年6月ごろからPintが搭載されるようになっています。

Laravel Pintは、ミニマリストのためのPHPコードスタイルフィクサーです。PintはPHP-CS-Fixerの上に構築されており、コードスタイルをクリーンで一貫性のあるものに保つことができます。Pintはすべての新しいLaravelアプリケーションに自動的にインストールされるので、すぐに使い始めることができます。デフォルトでは、Pintは何の設定も必要とせず、Laravelの意見に基づいたコーディングスタイルに従って、コードのコードスタイルの問題を修正します。

Laravel Pint – Laravel 11.x – The PHP Framework For Web Artisans

しかし、Pintではインデントにタブを使用する設定がありません。弊社コードマスターは「インデントはタブ!」派なので、いろいろ試みましたが実現できませんでした。
コードマスター曰く:

世の中「タブをスペースにしやがれ!」の声が大きいが ”声が大きいだけ” であってタブ派は少なくないはず。いやむしろ多いであろう。

調べてみると確かに「タブインデントのままでええねん」という人が居て、Pintからフォークしてタブインデント使えるようにしたものが公開されていました。それがjhonbacon/stoutです。

StoutはLaravel Pintのフォークで、PHP-CS-Fixerの上に構築されています。PintとStoutの唯一の違いは、インデントスタイルと行末を指定できることです。

GitHub – johnbacon/stout: A simple fork of Laravel Pint, now with tab indentation!

Stout設定

開発ルートフォルダーに"stout.json"ファイルを作成しておきます。今回の設定内容を記載しておきます。(”indent”: “\t” がミソ)

{
    "preset": "laravel",
    "cache-file": ".stout.cache",
    "exclude": [
        "database/migrations",
        "vendor"
    ],
    "indent": "\t",
    "rules": {
        "binary_operator_spaces":{"default":"single_space","operators":{"=>":null,"=":null}},
        "cast_spaces": false,
        "concat_space": false,
        "trailing_comma_in_multiline": false,
        "no_superfluous_phpdoc_tags": false,
        "no_unneeded_control_parentheses": false,
        "not_operator_with_successor_space": false,
        "single_quote": false
    }
}

導入

composer require johnbacon/stout --dev
code --install-extension emeraldwalk.runonsave (Run on SaveをVSCodeにインストール)

VSCode Run on Save でファイル保存時に自動実行

.vscode/setting.json に、以下のように記述して、ファイル保存時にstoutが実行されるようにします。
(細かい設定は各自でお願いします)

{
    // 拡張機能 Run on Save にてファイル保存時にコードを整形
    // 導入:code --install-extencion emeraldwalk.runonsave
    "emeraldwalk.runonsave": {
        "commands": [
            {
                "match": ".php$",
                "notMatch": "**/vendor/**",
                "cmd": "php vendor/bin/stout ${file}"
            }
        ]
    }
}