PHP-CS-FixerとGit フックで治安保持活動

こちらはJoolen Advent Calendar 2019 8日目の記事です。
カレンダーのURLはこちら
[Joolen Advent Calendar 2019](https://qiita.com/advent-calendar/2019/joolen)

## まえがき
PHPを書いてるとコーディング規約とか守らない人出てきますよね。
私も気付かず破ってしまうことがあります。
それを防ぐためにPHP-CS-FixerとGitフックを利用して治安保持を目指します。

## PHP-CS-Fixerとは:question:
コードの整形ツールと呼ばれるものです。
.php_cs.distという設定ファイルにルールを記述することで、コマンド一つでルールに沿ったコードに整形してくれます。
ルールはPHPの配列形式で設定します。
またFinderと呼ばれるものもあり、こちらを設定すると、整形して欲しくないファイルやディレクトリを指定できます。

例:

```php:.php_cs.dist
return PhpCsFixer\Config::create()
    ->setRiskyAllowed(true)
    ->setRules([
        ...
        'array_syntax' => ['syntax' => 'short'], // 配列の書き方を'short'にする
        ...
    ])
    ->setFinder(PhpCsFixer\Finder::create()
        ->exclude('vendor')
        ->in(__DIR__)
    );
```

実行結果

```diff
-        $arrRet = array();
+        $arrRet = [];
```

- [PHP-CS-Fixer github](https://github.com/FriendsOfPHP/PHP-CS-Fixer)

同じようなツールにPHP_CodeSnifferというツールもあります。
こちらはphpcs.xmlという設定ファイルに記述する形式になります。
こちらに関しては今回は触れません。

## Git フックとは:question:
Gitには発生したアクションに応じてスクリプトを実行する仕組みがあります。
これがGit フックです。
実行するスクリプトはGitディレクトリ(普通は.git)内のhooks/フック名をファイル名にした状態で格納されます。(.git/hooks/pre-commit等)
フックはクライアントサイドフックとサーバーサイドフックの2グループに分けられます。
[公式ドキュメント](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF)
`pre-commit`や`commit-msg`など多くある中で、今回は`pre-commit`フックを使用します。

### `pre-commit`
- コミットメッセージが入力される前に実行されます。
- このフックがゼロでない値を返すと、コミットが中断されます。
- この検査は git commit --no-verify で飛ばすこともできます。
- ここではコーディングスタイルの検査（lintを実行するなど）や、行末の空白文字の検査（デフォルトのフックがまさにそうです）、新しく追加されたメソッドのドキュメントが正しいかどうかの検査といったことが可能です。

## PHP-CS-Fixerのインストール
composerを利用してインストールします。

```shell
$ composer require --dev friendsofphp/php-cs-fixer
 ...
$ vendor/bin/php-cs-fixer -V
PHP CS Fixer 2.16.1 Yellow Bird by Fabien Potencier and Dariusz Ruminski
```

## PHP-CS-Fixerの設定を行う
早速設定をしていきたいところですが、PHP-CS-Fixerの設定を全てやっていると膨大な時間がかかります。
そこで、設定をパッケージ化して公開してくれている方がいるので、自分にあった物を見つけて利用しましょう。
[私が公開しているもの](https://github.com/h1r0-2525/php-cs-fixer-config)やhttps://github.com/suin/php-cs-fixer-rules
などありますので、探してみてください。

探して無事インストールできたら、今度は設定を少々変えていきましょう。
ほとんどのパッケージはルールが上書きできるようになっているはずです。
[php-cs-fixer-configurator](https://mlocati.github.io/php-cs-fixer-configurator/#version:2.16)を参考にして、設定を書き換えていきましょう。

## 整形予定の確認。　そして整形
設定が完了したら整形！
といきたいところですが、その前に整形予定を確認して、設定の間違いがないか以下のコマンドで確認しましょう。

```sh
$ vandor/bin/php-cs-fixer fix --dry-run --diff
```
こちらのコマンドは
`fix`によって設定ファイルに沿って整形
`--dry-run`オプションによって整形せず整形予定ファイルの表示
`--diff`オプションによって整形内容の表示となっています。

実行して予期せぬ変更がなければ以下のコマンドで実際に整形します。

```sh
$ vandor/bin/php-cs-fixer fix
```

## pre-commitフックでPHP-CS-Fixerを実行
PHP-CS-Fixerによる整形ができるようになったので、最初に紹介したpre-commitフックを利用して整形前のコードがコミットされないようにします。

.git/hooksディレクトリにpre-commitという名前でファイルを作成します。

```
$ touch .git/hooks/pre-commit
```

作成したファイルを修正してPHP-CS-Fixerを実行するようにします。

```bash:pre-commit
#!/bin/bash

# ディレクトリ定義
ROOT_DIR=$(git rev-parse --show-toplevel)
LIST=$(git diff --name-only --cached --diff-filter=AM | grep '\.php')

# php-cs-fixer
error=false
for file in $LIST
do
    # --path-modeをintersectionにすることで、Finderが無視されないようにします。
    $ROOT_DIR/vendor/bin/php-cs-fixer fix --path-mode=intersection --dry-run $ROOT_DIR/$file > /dev/null 2>&1
    if [ $? != 0 ]; then
        echo -e "   please, cs fix to $ROOT_DIR/$file"
        error=true
    fi
done

if "${error}"; then
    echo
    echo -e "\033[31mCommit fail\033[m please run \"vendor/bin/php-cs-fixer fix\" command"
    exit 1
fi
```

これで、規約を無視したコードをコミットしようとすると以下のように表示されるようになります。

```
please, cs fix to /path/to/invalid-file.php

Commit fail please run "vendor/bin/php-cs-fixer fix" command
```

これで治安保持できるようになりました！
コーディング規約にそってないコードは精神衛生上よろしくないので、駆逐していきましょう！

明日は@joolenkoyamaさんの[EC-CUBE4のContainerの探検記録](https://qiita.com/joolenkoyama/items/2d3e89a34833b065d221)です。