41
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Linter / FormatterからLaravelへの贈り物

Last updated at Posted at 2020-12-13

はじめに

この記事は Laravel Advent Calendar 2020 - Qiita の 14 日目 の記事です:gift:

アドベントカレンダーの貴重な1枠に、今更感のあるネタを挟んで申し訳ない気持ちもありますが、
Linter(静的解析ツール)Formatter(コード整形ツール) の持つ魅力に感動したので書かせて頂きます。

また、英語スピーキングにご興味をお持ちの方は
本エントリを英語でLT登壇した資料 (登壇日:2020.11.27)も合わせてご覧頂けると嬉しいです。

環境

Package Version
PHP 7.4.11
Laravel 6.18.43
nunomaduro/larastan 0.6.9
friendsofphp/php-cs-fixer 2.16.7

Laravelプロジェクトにとって、どう嬉しいの?

パッケージ名 種別 メリット
Larastan Linter レビューの前に、機械的に指摘できるエラーを摘み取ることが可能なので、レビューでは設計などの高度な問題に集中できる
PHP CS Fixer Formatter あらかじめ設定しておいたコーディングルールと異なるものを検出し、自動でその箇所を修正してくれる

具体例

3人編成のチームがいる想定です。(Aさん, Bさん, Cさん)
癖の強いコーディングをするチームメイト(Bさん・Cさん)が居た場合に、
コマンド一発で3人同じルールの書き方に統一しよう!
というものですね。

まず、整形前の3人のコードをご覧ください。
一目瞭然なくらい、書き方が変なチームメイトを混ぜておきました。

Aさん ( 無難に書いてくれる人 )

UserService.php
<?php

namespace App\Services;

use App\Repositories\UserRepositoryInterface;

class UserService
{
    /**
     * @var UserRepositoryInterface
     */
    private $user;

    /**
     * @param UserRepositoryInterface $user
     */
    public function __construct(UserRepositoryInterface $user)
    {
        $this->user = $user;
    }
}


Bさん ( なるべく短い行数におさめたい派 )

UserService.php
<?php

namespace App\Services;
use App\Repositories\UserRepositoryInterface;
class UserService{
        /**
         * @var UserRepositoryInterface
         */
        private $user;
        /**
         * @param UserRepositoryInterface $user
         */
        public function __construct(UserRepositoryInterface $user) {$this->user = $user;}
}

Cさん ( 気ままに書いてたら、動いちゃったからOK派 )

UserService.php
<?php

namespace App\Services;



use App\Repositories\UserRepositoryInterface;



class UserService
{
/**
 * 
 * 
 *  @var UserRepositoryInterface
 * 
 */
    private $user;
    /**
 *  @param UserRepositoryInterface $user
 * 
 */
public function __construct(\App\Repositories\UserRepositoryInterface $user)
{
$this->user = $user;
}
}

これをコマンド一発で3人の書き方の癖を直して、
同じものに整形しよう!というものですね!

  • 今回の整形に用いる項目は、5つです。
No. ルール
1 クラスの各プロパティおよび各メソッドの間に、空白1行だけ入れる。
2 Docブロックに対して、インデントを揃える。
3 useでインポートの記述がある関数に関して、引数や戻り値として完全修飾形式で書かれている場合、非修飾形式として無駄を省く形に変換。
4 PHPDocの記述において、始めと終わりの周囲には無駄な行を入れない。
5 ルールセット@PSR2の適用。

ポチッとな!

./vendor/bin/php-cs-fixer fix ./app

ワンクリックで3人のコードが以下に統一される!

UserService.php
<?php

namespace App\Services;

use App\Repositories\UserRepositoryInterface;

class UserService
{
    /**
     * @var UserRepositoryInterface
     */
    private $user;

    /**
     * @param UserRepositoryInterface $user
     */
    public function __construct(UserRepositoryInterface $user)
    {
        $this->user = $user;
    }
}

ああ、幸せな世界になった。:sparkles:


導入方法について

どちらも(Larastan & PHP-CS-Fixer)使い方はシンプルです。

  1. composerでインストール
  2. 所定の設定ファイルにルールを書く。
  3. runコマンドを実行。

これだけです。
面倒なのは、 2.所定の設定ファイルにルールを書く 所ですね。


(A) Larastan

(A-1) composerを用いて開発環境用にインストール

composer require --dev nunomaduro/larastan

(A-2) 設定ファイル (phpstan.neon.dist)を作成

phpstan.neon.distの形式

  • 下記は、公式ページの設定そのままです。
phpstan.neon.dist

includes:
    - ./vendor/nunomaduro/larastan/extension.neon

parameters:
    paths:
        - app
    level: 5
    ignoreErrors:
        - '#Unsafe usage of new static#'
    excludes_analyse:
        - ./*/*/FileToBeExcluded.php
    checkMissingIterableValueType: false

(A-3) RUNコマンドの実行

composer.jsonのscriptsキーに省略形を書いておき、
composer.json
"scripts": {
    "larastan": [
        "./vendor/bin/phpstan analyse"
    ],
}
その省略コマンドを実行
composer larastan


(B) PHP-CS-Fixer

(B-1) composerを用いて開発環境用にインストール

composer require --dev friendsofphp/php-cs-fixer

(B-2) 設定ファイル (.php_cs.dist)を作成

php_cs.distの形式

  • 下記は、個人的に好むルールにアレンジした状態です。
.php_cs.dist
<?php

return PhpCsFixer\Config::create()
    ->setRiskyAllowed(true)
    ->setRules([
        '@PhpCsFixer' => true,
        'blank_line_before_statement' => [
            'statements' => [
                'for', 'foreach', 'if', 'switch', 'try'
            ],
        ],
        'final_static_access' => true,
        'global_namespace_import' => true, 
        'phpdoc_add_missing_param_annotation' => [
            'only_untyped' => false
        ],
        'yoda_style' => [
            'always_move_variable' => false,
            'equal' => false, 
            'identical' => false
        ],
    ])
    ->setFinder(PhpCsFixer\Finder::create()
        ->exclude('vendor')
        ->in(__DIR__)
    )
    ;

(B-3) RUNコマンドの実行

composer.jsonのscriptsキーに省略形を書いておき、
composer.json
"scripts": {
    "csfixer-run": [
        "./vendor/bin/php-cs-fixer fix ./app"
    ],
}
その省略コマンドを実行
composer csfixer-run


ルール設定について深掘り

(A) Larastanのルール設定を深掘り

  • 基本的に、PHPStanのルールに準拠しています。
  • 上位の検証レベルは、下位の検証レベルで定義されているルールを内包しています。
    • (具体例を挙げると、検証レベル5は、レベル0からレベル4までの全てのルールも合わせて検証されます。)
Level 処理内容
0 $thisで呼ばれる未定義のクラス、未定義の関数、未定義のメソッドが無いかどうか。
また、$thisで呼ばれるメソッドや関数に渡す引数の数が等しいかどうか。
1 マジックメソッド( __call と __get )によって、未定義の変数などが用いられていないかどうか。
2 PHPDocsを参照しながら、(Level 0, Level 1と異なり、)$thisで呼ばれるもの以外も含め全てにおいて、未定義のメソッドがないかどうか。
3 戻り値の型とクラスのプロパティで宣言された型が一致するかどうか。
4 使用されていないコードが無いかどうか。
instanceofの指定に一致しないインスタンスが無いかどうか。
条件分岐において、通過することのない不要なelse分岐が無いかどうか。
return文の後に記述されているが、処理がなされないコードが無いかどうか。
5 メソッドや関数の引数として型があっているかどうか。
6 タイプヒントの書き忘れが無いかどうか。
7 部分的に誤りのあるユニオン型の記述が無いかどうか。
8 NULL許容型でメソッドを呼び出しを行なっていないかどうか。NULL許容型でプロパティへアクセスしている部分が無いかどうか。


(B) PHP CS Fixerのルール設定を深掘り

全ての公式ルールセットはこちら

  • 筆者が好んで用いるものをピックアップして書きます。
  • なお、既存のルールセット (@PSR2@PhpCsFixer )が検査する項目の◯Xも付けています。
ルール名 処理内容 @PSR2 @PhpCs
Fixer
align_multiline_comment 複数行のDocコメントをPSR-5準拠させ、1行目のアスタリスクの位置に揃える X
blank_line
_after_namespace
名前空間宣言の下に入れるスペースは1行だけにする。 
blank_line
_after_opening_tag
開始タグ <?phpの後には、
1行(以上)空白をあけるように整形
X
blank_line
_before_statement
'break', 'case', 'continue', 'for', 'foreach', 'if', 'include_once', 'require_once', 'return', 'switch', 'throw', 'try', 'while'などを配列で指定してあげれば、その前には、
1行だけ空白をあけるように整形。
X
cast_spaces 型キャストと変数の間に半角スペースを入れるか否か X
class_attributes_separation クラス, トレイト, インターフェイスの各プロパティおよび各メソッドの間に、空白1行を入れるか否か。 X
constant_case 真偽値(true, false)およびnull値に関して、小文字に揃えるか否か。 X
final_static_access finalクラスにおいて宣言されたstatic修飾子をself修飾子に変換する。 X X
fully_qualified_strict_types インポートの記述がある関数に関して、引数や戻り値として完全修飾形式で書かれている場合、非修飾形式として無駄を省く形に変換。 X
function_declaration 関数の宣言において、半角スペース1つを入れるか否か
function_typehint_space 関数の引数とタイプヒントの間に、半角スペース1つを入れるか否か。 X
global_namespace_import クラス名/関数名/定数名を使用する際に、完全修飾形式(バックスラッシュ)で記載していると、グローバルにuse宣言した方法に変換してくれる。 X X
indentation_type タブでインデントを取っていたら、半角スペースに直してくれる。
lowercase_cast 型キャストは、全て小文字で書かれるように変換。 X
magic_constant_casing マジック定数を、全て大文字に変換。 X
magic_method_casing マジックメソッドを、全て小文字に変換。 X
method_argument_space メソッドが複数の引数を取る場合の空白の入れ方。
複数の引数を1行に纏めるのか、複数行に纏めるのか。
method_chaining
_indentation
メソッドチェーンに用いられる矢印のインデントを揃える。 X
new_with_braces new演算子を用いて、新しいインスタンスを生成する箇所では、必ずブレース()を付けるか否か。 X
no_blank_lines
_after_class_opening
クラスの開始ブレースの直後には、空白行を作らない。 X
no_blank_lines
_after_phpdoc
Docブロックと中身の記述の間には、空白行を作らない。 X
no_extra
_blank_lines
空白行は1行ずつと定め、それ以外の余分な空白行を削除する。 X
no_multiline_whitespace
_around_double_arrow
=>演算子の前後を複数行にせず、1行に纏める X
no_null_property
_initialization
クラスのプロパティをnullで初期化しない記法に揃える。 X
no_spaces
_after_function_name
メソッドや関数の呼び出しを行う記述の際には、直後にスペースを入れない。
no_spaces
_around_offset
配列の要素を添字で指定して呼び出す際に、余分な半角スペースを入れない。 X
no_spaces
_inside_parenthesis
小括弧の中にスペースを含めない。
no_trailing_whitespace 文末のセミコロンの後には、半角スペースを含めない。
no_trailing_whitespace
_in_comment
PHPDocの中に余分なスペースを含めない。
no_unneeded
_control_parentheses
'break', 'continue', 'echo', 'print', 'return', 'switch_case', 'yield'などに関して、不要な小括弧を取り除く。 X
no_unused_imports 使用されていないuse句を削除。 X
no_useless_else 不要なelse分岐を除く。 X
no_useless_return 不要なreturn宣言を除く。 X
no_whitespace
_before_comma_in_array
配列処理の記述の中で、コンマの直前に半角スペースを入れない。 X
ordered_class_elements クラス内に宣言されているclasses/interfaces/traitsの規則性を持たせて順序を揃える。 X
ordered_imports use句でインポートするクラスなどをアルファベット順に並べる。 X
php_unit_fqcn_annotation PHPUnitで記載するアノテーションには、完全修飾形式で書くように修正する。 X
phpdoc_add_missing
_param_annotation
全ての引数に@paramを付与する。 X
phpdoc_indent Docブロックに対して、インデントを揃える。 X
phpdoc
_no_empty_return
@return void@return nullのアノテーションを、PHPDocから削除する。 X
phpdoc_order PHPDoc内におけるアノテーションの順序は、@param => @throws => @return と整列させる。 X
phpdoc_scalar スカラー型に関して、
integer => int,
boolean => bool,
real, double => float
で表記を統一する。
X
phpdoc_trim PHPDocの記述において、始めと終わりの周囲には無駄な行を入れない。 X
phpdoc_types PHPDocの型記述は、全て小文字で書く。 X
phpdoc_types_order PHPDoc内にユニオン型で書かれた型の並び順をソートする。 X
phpdoc_var
_without_name
@varアノテーションや@typeアノテーションには、変数名を書かない。 X
protected_to_private finalクラスなどで宣言されているprotectedプロパティとprotectedメソッドを、privateに変換する。 X
return_type_declaration 戻り値の型宣言の前のコロンの前後に関しては、コロン前には半角スペース無しで、コロン後に1つだけ半角スペースを入れる。 X
single_line
_comment_style
1行コメントの場合は、//記法に整形する。 X
single_quote 変数の展開などを含まない単純文字列を囲むクォーテーションは、シングルクォートに統一する。 X
ternary_operator_spaces 三項演算子を用いている前後のスペースは、半角スペース一個に統一する。 X
yoda_style ヨーダ記法に則るか否か。 X

Fixer Setの種類

  • 頻繁に使われるルールの組み合わせは、Fixer Setとして纏めて下さっているようです。
  • また、筆者は、risky が付いたルールセットは好んで利用しないようにしています。
Fixer セット
@PSR2
@PhpCsFixer
@Symfony
@PHP70Migration
@PHP73Migration
@PHP80Migration
@PhpCsFixer:risky
@Symfony:risky
etc

さいごに

クリスマスイブまで、たった10日なんですね!
来年もどうぞよろしくお願いします :tada:

参考文献

41
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?