読者です 読者をやめる 読者になる 読者になる

MANA-DOT

PIXEL ART, PROGRAMING, ETC.

gitのpre-commit hookを使って、綺麗なPHPファイルしかコミットできないようにする

PHP git プログラミング

2013-08-09 00.09.28

PHPのようなゆるふわな言語を安全に書くためには、コードの綺麗さや作法などを担保する手段が大切になります。 IDEを使う、JenkinsなどのCIサーバーを立ててチェックさせるなどの方法が考えられますが、今回はgitの pre-commit hook を利用して、一定の条件を満たしていないコードはそもそもリポジトリにコミットができないようにしてみました。

できるようになったこと

今回以下の様な事ができるようになりました。

git commit時に、

  • コミットされるファイルにシンタックスエラーがあるPHPファイルがる場合、コミットが失敗する。
  • コミットされるファイルに作法の悪いコードが有る場合、(使用してない変数があるなど)コミットが失敗する。
  • PSRに則ってないファイルが有る場合(改行コードやインデントの統一など)、整形してからコミットする。

これにより、レポジトリ上にコミットされるファイルを制限し、綺麗なファイルだけが存在するという状況を作れます。

precommit

以下に実現するためにやったことを書きます。

git の pre-commit hook

gitのリポジトリに、.git/hooks/pre-commit というファイルを作り、実行権限を与えれおくと(忘れがちなので注意)、 git commit時に、コミットが行われる前にそのファイルが実行されます。この時、そのファイルの終了値が1である場合、 その後実行されるはずのコミットはされず、失敗します。くわしくはこちら

今回これを利用し、コミット時にコミットされるファイルについて検査・整形を行います。

PHPのシンタックスチェック ・・・ php -l

コマンドラインphpコマンドでlオプションを与えることでシンタックスチェックが出来ます。コマンドラインphpコマンドがない場合は、インストールします。Ubuntuならaptで入れられます。

$ sudo apt-get install php5-cli

使い方

例えば以下の様なPHPファイルが有る場合、

<?php
$i = 0 // セミコロンを忘れてる

次のようにします。

$ php -l test.php
PHP Parse error:  syntax error, unexpected $end in test.php on line 2
Errors parsing test.php

このように、シンタックスエラーがあるのでそれを教えてくれます。

お行儀のチェック ・・・ phpmd

PHPのコードのお行儀をチェックしてくれる、PHPMDというアプリケーションがあります。 詳しくはサイトを読んで欲しいのですが、例えば使ってない変数を見つける、複雑なループを見つけるなどをしてくれます。

インストール

今回はPEARからインストールしました。サイトに書いてあるとおりにします。

# PEARが入ってない場合
$ sudo apt-get install php-pear

$ sudo pear channel-discover pear.phpmd.org
$ sudo pear channel-discover pear.pdepend.org
$ sudo pear install --alldeps phpmd/PHP_PMD

使い方

例えば以下の様なPHPファイルが有る場合、

<?php
class TestClass {
    private $private; // 使ってないプライベート変数
}

以下の様なコマンドを叩きます。

$ phpmd test.php text unusedcode

/home/mana/test_repo/test.php:5 Avoid unused private fields such as '$private'.

このように、privateというプライベート変数を使ってないのでやめましょう、と教えてくれます。 unusedcodeのほかに、codesizenamingなどのルールが有ります。詳しくはサイトを読んでください。

SuppressWarnings

また、コードによってはどうしてもルールが適応できないという場合があります。そのような場合は、SuppressWarningsというコメントをつけることで、対象となっているクラスやメソッドをそのルールから除外することができます。

/** @SuppressWarnings("unused") */
class TestClass {
    private $private;
}

ですが、せっかくPHPMDを使用している意味がなくなってしまうので、なるべくなら避けたいところです。

PSR準拠に整形 ・・・ php-cs-fixer

PSRPHPのコード規約の一つで、 改行コードはLFを使いましょう、インデントはスペース4つにしましょう、クラスの中括弧は次の行に書きましょう、アクセス修飾子は必ず書きましょうといったコードの書き方に関するルールが定められています。 チーム全員がコード規約に則ったコードを書くことで、コードが統一され、プロジェクト全体としてコードが読みやすくなります。

PHPファイルをPSR準拠に整形(ただし、完全ではない)してくれるアプリケーションとして、PHP Coding Standards Fixerがあります。

インストール

サイトに書いてあるとおりにします。

$ sudo wget http://cs.sensiolabs.org/get/php-cs-fixer.phar -O /usr/local/bin/php-cs-fixer
$ sudo chmod a+x /usr/local/bin/php-cs-fixer

使い方

例えば、クラスの中括弧が同じ行にある以下の様なPHPファイルが有るとします。

<?php
class TestClass {
}

これにたいして、次のようにします。

$ php-cs-fixer fix test.php --fixers=braces

すると、PHPファイルが以下のように整形されます。

<?php
class TestClass
{
}

こちらもPHPMDのように様々なルールが有ります。詳しくはサイトを読んでください。 改行コードを統一する linefeed や、インデントをスペース4つに揃える indentation は汎用性が高いと思われます。

pre-commitスクリプト

以上を踏まえて、今回書いたpre-commitスクリプトは以下のようになりました。 これをローカルレポジトリ.git/hooks/pre-commit に配置することで、冒頭で説明したとおりに、コミット時に色々なチェックと整形をしてくれるようになります。

※8/14追記 php-cs-fixerでの修正がその回のコミットに入らなかったので、git addするようにした

#!/bin/sh
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Redirect output to stderr.
exec 1>&2

IS_ERROR=0
# コミットされるファイルのうち、.phpで終わるもの
for FILE in `git diff-index --name-status $against -- | grep -E '^[AUM].*\.php$'| cut -c3-`; do
    # シンタックスのチェック
    if php -l $FILE; then
        # PSR準拠でコード書き換え
        php-cs-fixer fix $FILE --fixers=linefeed,trailing_spaces,indentation,controls_spaces,eof_ending,php_closing_tag,phpdoc_params,visibility,braces,elseif,include
        git add $FILE

        # PHPMDで未使用変数などのチェック
        if ! phpmd $FILE text unusedcode,codesize,naming; then
            IS_ERROR=1
        fi
    else
        IS_ERROR=1
    fi
done
exit $IS_ERROR

あまり厳しすぎると辛いので、PHPMDとphp-cs-fixerのルールについては全ては使っていません。 自分の使いたいルールだけ選べるのはなかなか便利だと思います。

まとめ

gitのpre-commit hookでphp -l、phpmd、php-cs-fixerを呼び出すことで、gitのレポジトリにお行儀の悪いファイルがコミットされることを完全に禁止できます。 特にチーム開発では、全員の環境に導入することで強力なルールとなるため、コードの品質の担保に大きく貢献してくれると思います。