pre-commitのススメ

この記事は [Git Advent Calendar 2019](https://qiita.com/advent-calendar/2019/git) 10日目の記事です。

# はじめに

最近、gitのhooksでpre-commitを使い始めた
なかなか良いことが多かったので書いてみる

# モチベーション

- PHPという自由な言語なので、他人のコードを読む時に苦しみたくない
- コードレビューの時にスペースがないとかくだらないことをチェックしたくない
- シンタックスエラーを事前に検知したい

# やったこと

- `php -l`でのシンタックスチェック
- `phpmd`でのお作法チェック
- `php-cs-fixer`での自動修正

上記3点をcommit時にチェック。違反や引っかかることがあった時に、__commitできない__という結構厳しめのルール設定

# php -lについて

- phpのシンタックスエラーをチェックしてくれる

```php
<?php

$a = "test"
echo $a;

?>
```

```bash
$ php -l file.php

Parse error: syntax error, unexpected 'echo' (T_ECHO) in test.php on line 5
Errors parsing test.php
```

このようにあらかじめエラーを見つけてくれる

# phpmdについて

[公式](https://phpmd.org/)

## インストール方法

```bash
$ brew install phpmd

$ phpmd --version
PHPMD 2.7.0snapshot201907302127
```

このように表示されればOK

## 使用法

- 使っていない変数のチェック
- 長すぎる変数名
- 複雑すぎるクラス

などなど様々なバグの温床をあらかじめ見つけてくれる

使い方は以下のように2通り

```bash
$ phpmd file.php text unusedcode,codesize,naming

or

$ phpmd file.php text phpmd-ruleset.xml
```

phpmdコマンドの後ろにファイル名またはディレクトリ、レポートフォーマット（text or xml）、使用ルールまたはルール記述xmlファイルの指定という風に書く。

## ルールセットについて

上記のようにxmlファイルでルールを設定することができる。

```phpmd-ruleset.xml
<?xml version="1.0"?>
<ruleset name="My first PHPMD rule set"
        xmlns="http://pmd.sf.net/ruleset/1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                    http://pmd.sf.net/ruleset_xml_schema.xsd"
        xsi:noNamespaceSchemaLocation="
                    http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>
    My custom rule set that checks my code...
</description>

    <rule ref="rulesets/codesize.xml">
        <exclude name="CyclomaticComplexity" />
    </rule>
    <rule ref="rulesets/controversial.xml" />
    <rule ref="rulesets/unusedcode.xml" />
    <rule ref="rulesets/naming.xml">
        <exclude name="ShortVariable" />
        <exclude name="ShortMethodName" />
    </rule>
    <rule ref="rulesets/design.xml">
        <exclude name="CouplingBetweenObjects" />
    </rule>
</ruleset>

```

このように、\<rule>タグで使用ルールを指定。\<exclude>タグでルール内の細かいルールの除外をする。  

例えば、

```xml
<rule ref="rulesets/naming.xml">
    <exclude name="ShortVariable" />
    <exclude name="ShortMethodName" />
</rule>
```

この場合、ネーミングに関するルールを使用するが、短すぎる(3文字以内)の変数名は引っかかるというルールを除外している。

# php-cs-fixerについて

## インストール方法

```bash
// composerのあるディレクトリに移動
$ cd path/to/your/project

$ composer install

$ ./vendor/bin/php-cs-fixer --version

PHP CS Fixer 2.16.0 Yellow Bird by Fabien Potencier and Dariusz Ruminski
```

こうなればOK。

## 使い方

```bash
$ ./vendor/bin/php-cs-fixer fix file.php
```

このようにすると、これが

```php
<?php

$a="test";
echo $a

?>
```

こうなる

```php
<?php

$a = "test";
echo $a;
```

これでコードの統一性がある程度出るし、変な指摘をしないで済む。

## ルールについて

`.php_cs.dist`というファイルで設定できる

```php
<?php

return PhpCsFixer\Config::create()
    ->setRiskyAllowed(true)
    ->setRules([
        '@PSR2' => true, //基準となるルールの設定
        'array_syntax' => ['syntax' => 'short'], //配列の書き方について
        'global_namespace_import' => ['import_classes' => true, 'import_constants' => true, 'import_functions' => true],  //namespaceの書き方について
        'concat_space' => ['spacing' => 'one'] //文字列連結の際のスペースについて
    ])
    ->setFinder(PhpCsFixer\Finder::create()
        ->exclude('vendor')
        ->in(__DIR__)
    );

```

これ以外にも[これ](https://mlocati.github.io/php-cs-fixer-configurator/#version:2.16)を使えば便利に比較することができる。



# pre-commitの設定

```bash
$ cd path/to/your/project/.git/hooks

$ touch pre-commit

$ vim pre-commit
```

```pre-commit
#!/bin/sh
against=HEAD

IS_ERROR=0

for FILE in `git diff-index --name-status $against -- | grep -E '^[AUM].*\.php$'| cut -c3-`; do
    if php -l $FILE; then
        ./vendor/bin/php-cs-fixer fix $FILE
        git add $FILE

        if ! phpmd $FILE text phpmd-ruleset.xml; then
            IS_ERROR=1
        fi
    else
        IS_ERROR=1
    fi
done
exit $IS_ERROR
```

.phpファイルのみだけがチェック対象。


# 導入後

- コードレビューでクリティカルな指摘に集中できるようになった
- コーディングスタイルが整うようになり、可読性が上がった
- 潜在的なバグが減らせた（と思ってる）
- 一度にcommitするとしんどいからcommit粒度が細かくなり、振り返りやすくなった

良いことだらけ。
ぜひ良いコミットライフを！