1. Home
  2. PHP
  3. Laravel
  4. LaravelにPHP_CodeSnifferを導入しコーディング規約(PSR)に沿った記述を行う

LaravelにPHP_CodeSnifferを導入しコーディング規約(PSR)に沿った記述を行う

  • 公開日
  • 更新日
  • カテゴリ:Laravel
  • タグ:PHP,Laravel,PHP_CodeSniffer
LaravelにPHP_CodeSnifferを導入しコーディング規約(PSR)に沿った記述を行う

Laravel など PHP を用いて Web アプリケーションを開発している際になるべくきれいなコードを書こうと努めていると思いますが、それは設計だけでなく、構文の細かな部分にも気を配りたいところです。

今回は構文チェックを行う事のできる PHP_CodeSniffer を Laravel に導入し、漏れなくコーディング規約に沿った記述を行えるようにしていきます。

Contents

  1. 開発環境
  2. PHP_CodeSniffer
  3. インストール
  4. 使用できるコーディング規約を確認する
  5. phpcs.xml
  6. 構文チェック実行
  7. コマンド登録
  8. 動作確認
  9. 結果をファイルへ出力する
  10. テストケースクラスのメソッド名を除外する
  11. コーディング標準の違反を自動的に修正する

開発環境

今回の開発環境は以下の通りです。

  • PHP 7.3
  • Laravel 5.8
  • Composer 1.8
  • PHP_CodeSniffer 3.4.2

Laravel のルートディレクトリを「 laravel/」としています。

PHP_CodeSniffer

PHP_CodeSniffer は、コーディング規約( PSR など)に沿った構文チェックを行う事のできるパッケージです。

PHP_CodeSniffer
https://github.com/squizlabs/PHP_CodeSniffer

PHP_CodeSniffer には2つの機能があります。

  • 定義されたコーディング標準(規約)の違反を検出する、phpcs スクリプト
  • コーディング標準(規約)の違反を自動的に修正する phpcbf スクリプト

もちろん Laravel 専用ではないので、無印の PHP にも利用でき、PHP 5.4.0 から使用できます。

インストール

以下の composer コマンドを叩いてインストールできます。

# PHP_CodeSniffer インストール
composer require --dev squizlabs/php_codesniffer

使用できるコーディング規約を確認する

以下のコマンドで、使用できるコーディング規約を確認できます。

$ ./vendor/bin/phpcs -i
The installed coding standards are PEAR, Zend, PSR2, MySource, Squiz, PSR1 and PSR12

色々ありますが PSR に準拠させたいので、選択肢としてはPSR1PSR2 ・ PSR12 になります。

今回は PSR12 を適用させていきます。( PSR12 は PSR1 を含み、PSR2 の拡張版{PHP7 対応 etc}です。)

2019 年 8 月現在、まだレビューフェーズですが、そのうち正式に承認 PSR になるでしょう。

phpcs.xml

まずは基本となるルールを作成します。プロジェクトルートに phpcs.xml を作成し、ルールや設定を定義します。

laravel/phpcs.xml
<?xml version="1.0"?>
<ruleset name="PSR12/Laravel">
    <description>PSR12 compliant rules and settings for Laravel</description>

    <arg name="extensions" value="php" />

<!-- 適用コーディング規約の指定 -->
    <rule ref="PSR12" />

<!-- 出力に色を適用 -->
    <arg name="colors" />

<!-- オプション p:進捗表示  s:エラー表示時にルールを表示 -->
    <arg value="ps" />

<!-- 除外ディレクトリ -->
    <exclude-pattern>/bootstrap/</exclude-pattern>
    <exclude-pattern>/config/</exclude-pattern>
    <exclude-pattern>/database/</exclude-pattern>
    <exclude-pattern>/node_modules/</exclude-pattern>
    <exclude-pattern>/public/</exclude-pattern>
    <exclude-pattern>/resources/</exclude-pattern>
    <exclude-pattern>/routes/</exclude-pattern>
    <exclude-pattern>/storage/</exclude-pattern>
    <exclude-pattern>/vendor/</exclude-pattern>
    <exclude-pattern>/server.php</exclude-pattern>
    <exclude-pattern>/app/Console/Kernel.php</exclude-pattern>
    <exclude-pattern>/tests/CreatesApplication.php</exclude-pattern>
</ruleset>
  • コーディング規約として PSR12 を指定しています。
  • エラーレポート出力時に色付けする指定を行っています。
  • オプションとして、エラー表示時にルールを表示する事、進捗を表示する事を指定しています。
  • 除外するディレクトリやファイルを指定しています。今回は Laravel のコアソースをなるべく除外し、app と tests ディレクトリ内をチェックするようにしています。

構文チェック実行

最低限の設定を行ったので、一度 PHP_CodeSniffer を実行してみます。以下のコマンドを叩きます。

# プロジェクトルートへ移動
cd /path/to/laravel

# PHP_CodeSniffer 実行
./vendor/bin/phpcs --standard=phpcs.xml ./

問題なく実行された事が確認出来ました。(この時点ではまだ何も実装していないので、ひとまずの動作確認です。)

コマンド登録

実行コマンドが長いので、composer コマンドで実行できるようにします。

laravel/composer.json
"scripts": {
    "sniffer": [
        "./vendor/bin/phpcs --standard=phpcs.xml ./"
    ]
}

composer コマンドで再度実行してみます。

# PHP_CodeSniffer 実行
composer sniffer

これで簡単に実行できるようになりました。

動作確認

実際にコードを書いて動作を確認してみます。

laravel/app/Http/Controllers/SampleController.php
<?php
declare(strict_types=1);

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SampleController extends Controller
{
    public function index(): \stdClass
    {
        $a = 1;
        $b = 2;
        if ($a < $b) {
            $result = new \stdClass();
            $result->message = '$b is larger than $a.';
            return $result;
        }
    }
}

例えばこんなコードがあったとして(これ自体には何の実用性も無いコードです)、構文としては PSR12 には準拠しているのでエラーは出ず素直に通ります。

これを例えば、以下のようにして、if の後のスペースを無くしてみます。

// if ($a < $b) {
if($a < $b) {

この状態でチェックを走らせると、エラーとなります。

FILE: /path/to/laravel/app/Http/Controllers/SampleController.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
15 | ERROR | [x] Expected 1 space(s) after IF keyword; 0 found
   |       |     (Squiz.ControlStructures.ControlSignature.SpaceAfterKeyword)
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

if 文のスペースについての規約は PSR2 からあるものなので、折角なので別のところでもエラーを出してみます。

// $result = new \stdClass();
$result = new \stdClass;

PSR12 では、クラスのインスタンス化の際にカッコを省略しないでね。という規約があるので、例えばこんな記述をすると、チェック時にそれを知らせてくれます。

FILE: /path/to/laravel/app/Http/Controllers/SampleController.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
16 | ERROR | [x] Parentheses must be used when instantiating a new
   |       |     class
   |       |     (PSR12.Classes.ClassInstantiation.MissingParentheses)
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

(つまり phpcs.xml でのルール指定を PSR2 にするとここは違反にはなりません)

こんな感じで、定義したコーディング規約に沿っていつでも構文チェックを走らせる事が出来ます。

結果をファイルへ出力する

構文チェックの結果をファイルへ出力する事も可能です。

出力できるレポートのタイプは以下から確認できます。
https://github.com/squizlabs/PHP_CodeSniffer/wiki/Reporting

例えば、XML ファイルと CSV ファイルの場合は以下のコマンドで出力出来ます。

# 結果を XML ファイルとして出力
./vendor/bin/phpcs --standard=phpcs.xml --report=xml --report-file=./sniffer-reports/report.xml ./

# 結果を CSV ファイルとして出力
./vendor/bin/phpcs --standard=phpcs.xml --report=csv --report-file=./sniffer-reports/report.csv ./

出力のタイプと出力先を指定しています。(ディレクトリは予め作成しておく)

composer コマンドとして使用するなら以下のようにしておけばいつでも使えます。

laravel/composer.json
"scripts": {
    #
    # 省略
    #
    "sniffer": [
        "./vendor/bin/phpcs --standard=phpcs.xml ./"
    ],
    "sniffer-report-xml": [
        "./vendor/bin/phpcs --standard=phpcs.xml --report=xml --report-file=./sniffer-reports/report.xml ./"
    ],
    "sniffer-report-csv": [
        "./vendor/bin/phpcs --standard=phpcs.xml --report=csv --report-file=./sniffer-reports/report.csv ./"
    ]
}

それぞれの出力ファイルは以下のようになります。

laravel/sniffer-reports/report.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpcs version="3.4.2">
<file name="/var/www/html/laravel/app/Http/Controllers/SampleController.php" errors="2" warnings="0" fixable="2">
    <error line="14" column="9" source="Squiz.ControlStructures.ControlSignature.SpaceAfterKeyword" severity="5" fixable="1">Expected 1 space(s) after IF keyword; 0 found</error>
    <error line="16" column="23" source="PSR12.Classes.ClassInstantiation.MissingParentheses" severity="5" fixable="1">Parentheses must be used when instantiating a new class</error>
</file>
</phpcs>
laravel/sniffer-reports/report.csv
File,Line,Column,Type,Message,Source,Severity,Fixable
"/var/www/html/laravel/app/Http/Controllers/SampleController.php",14,9,error,"Expected 1 space(s) after IF keyword; 0 found",Squiz.ControlStructures.ControlSignature.SpaceAfterKeyword,5,1
"/var/www/html/laravel/app/Http/Controllers/SampleController.php",16,23,error,"Parentheses must be used when instantiating a new class",PSR12.Classes.ClassInstantiation.MissingParentheses,5,1

テストケースクラスのメソッド名を除外する

ユニットテストを定義している際に、メソッド名を日本語で宣言する場合は規約に引っかかるのでエラーが出ます。こんな感じのやつです。

/**
 * @test
 */
public function 私たちの未来がいつまでも明るく、開かれていること() // ← メソッド名が日本語
{
    $this->assertTrue(true);
}

メソッド名はキャメルケース( methodName)で記述しましょうと PSR1 で定義されているのでエラーになります。

とはいえここは不用意な違反ではないので、ここはルール単位で除外してあげる事でエラーを回避出来ます。

laravel/phpcs.xml
<?xml version="1.0"?>
<ruleset name="PSR12/Laravel">
    //
    // 省略
    //

    <!-- tests ディレクトリ配下はメソッド名のキャメルケースチェックを除外する -->
    <rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
        <exclude-pattern>*/tests/*</exclude-pattern>
    </rule>
</ruleset>

これでエラーとみなされなくなります。

コーディング標準の違反を自動的に修正する

構文チェックで違反箇所が解ったら都度修正を行えば良いわけですが、自動で修正する事も可能です。

その場合に、全ての修正を行えるわけではなく、
PHPCBF CAN FIX THE x MARKED SNIFF VIOLATIONS AUTOMATICALLY
と表示されている部分に対して、自動での修正が可能です。

FILE: /path/to/laravel/app/Http/Controllers/SampleController.php
----------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 2 LINES
----------------------------------------------------------------------
 14 | ERROR | [x] Expected 1 space(s) after IF keyword; 0 found
    |       |     (Squiz.ControlStructures.ControlSignature.SpaceAfterKeyword)
 16 | ERROR | [x] Parentheses must be used when instantiating a new
    |       |     class
    |       |     (PSR12.Classes.ClassInstantiation.MissingParentheses)
----------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY ← この表示がある部分
----------------------------------------------------------------------

以下のコマンドで自動修正を行う事が出来ます。

# phpcbf 構文自動修正
./vendor/bin/phpcbf --standard=phpcs.xml ./

実行結果は以下になります。

# 実行結果

PHPCBF RESULT SUMMARY
------------------------------------------------------------------------------------
FILE                                                                FIXED  REMAINING
------------------------------------------------------------------------------------
/path/to/laravel/app/Http/Controllers/SampleController.php     2      0
------------------------------------------------------------------------------------
A TOTAL OF 2 ERRORS WERE FIXED IN 1 FILE
------------------------------------------------------------------------------------

Time: 17.71 secs; Memory: 8MB

対象のファイルを見てみると、構文が修正されている事が確認できます。

もちろん、これも composer.json に登録しておけばいつでも簡単に呼び出せます。

laravel/composer.json
"scripts": {
    #
    # 省略
    #
    "sniffer-rewrite": [
        "./vendor/bin/phpcbf --standard=phpcs.xml ./"
    ]
}

まとめ

以上で作業は終了です。構文は実行時にそれだけではエラーにならなかったりするので、人によって書き方に偏りがあったりします。

こういった仕組みがあると、チームで開発していても同じ記述が出来るようになるし、個人で開発していても、一定の指針が出来て結構安心できます。

コードをクリーンで一貫性のあるものにする為に、心意気だけでなく、こういったものも取り入れてスマートに開発していきたいですね。

サンプルソース

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級