• トップ
  • ブログ一覧
  • LaravelにPHPの静的解析ツールを導入し、コードをきれいに保つ
  • LaravelにPHPの静的解析ツールを導入し、コードをきれいに保つ

    エンジニア記事エンジニア記事
    2022.09.27

    IT技術

    LaravelにPHPの静的解析ツールを導入し、コードをきれいに保つ

    今回は、PHP(Laravel)にフォーカスを当てた話となります。

    コーディングにはコーディング規約というものが存在し、それに基づいて開発することで、きれいなコードを保つことができます。

    多くのエンジニアさんは、コーディング規約に則っているかどうかのチェックツールや、コーディング規約に則るように整形してくれるツールを導入した上で、開発されている人がほとんどかと思います。

    PHPの場合は、代表的なチェックツールが php_codesniffer、整形ツールが php-cs-fixer となります。

    (例えば JavaScript の場合、チェックツールが ESLint、整形ツールが Prettier といったように各言語でそのようなツールは存在しています)

    一方、PHP のコーディング規約のこと以外にも、アプリケーションを実行せずに静的にコード品質をチェックする静的解析ツールなどが存在しています。

    文法やコーディングスタイルについては、php_codesniffer で対応できるのですが、あくまで文法レベルでのチェックにすぎず、いくら文法が正しくともエラーは発生します。

    では、なぜ PHP のエラーは実行時まで分からないのかと言いますと、PHP はスクリプト言語であるからです。

    本来、C や Java といったコンパイル言語では、コンパイル時にエラーとなって気づくのですが、スクリプト言語である PHP は実行させるまで気づかないのです...

    そこで未定義の変数があるかどうか、メソッドの引数や戻り値の型は問題ないか等、バグとなり得る原因の箇所を実行前に発見できるようにするのが、静的解析です。

    今回はその PHP の静的解析ツールを Laravel に導入する方法について紹介します。

    コーディング規約とは?

    念の為、コーディング規約についてもご説明いたします。

    そもそもコーディング規約とは、コードを書くためにルールを定めたものになります。

    この規約をメンバーと共有しプロジェクトを進める事で、コードの共通化や、ソースコードリーディングの時間短縮が望めます。

    例えば、PHPの言語ですと、コーディング規約として PSR (PHP Standards Recommendations) というものがあります。

    PSR (PHP Standards Recommendations) は、PHP-FIG (PHP Framework Interop Group) が策定している PHP コーディング規約で、PSR1~19 の規約が存在しています。

    その中でも、Laravelでは、ドキュメントに記載ある通りPSR-2とPSR-4の規約に準拠しているとのことですので、最低でもこの二つは守る必要があります。

    https://readouble.com/laravel/9.x/ja/contributions.html#coding-style

    しかし、PSR-2というのは、PHP-FIG によると、2019年の時点でPSR-2は既に非推奨となっており、代わりにPSR-12が推奨されているとのことです。

    Deprecated - As of 2019-08-10 PSR-2 has been marked as deprecated. PSR-12 is now recommended as an alternative.

    参考:https://www.php-fig.org/psr/psr-2/

    そのため、Laravel では

    • PSR-4
    • PSR-12

    を守るようにコーディングしていくことになります。

    ※全てのコーディング規約については、下記リポジトリにまとめられていますので、興味ある方は覗いてみてください。

    https://github.com/php-fig/fig-standards/tree/master/accepted

    静的解析ツールについて

    ここからがようやく本題の静的解析ツールについての説明です。

    静的解析ツールを導入することで、下記のような問題を検知することが可能です。

    • 存在しないクラスをインスタンス化している
    • 存在しないメソッド、関数を呼び出している
    • 参照しているクラス名の大文字小文字が定義と異なる
    • メソッド仮引数とメソッド呼び出し実引数の型が一致しない
    • メソッドに型宣言が指定されていない
    • nullの可能性があるのに、メソッドを読んでいる

    PHP の静的解析ツールとしては、下記のように色々と種類があるのですが、今回は PHPStan を利用します。

    • PHPStan
    • Psalm
    • Phan
    • PHPMD

    今回はPHPで利用するのではなく、フレームワークの Laravel で利用します。

    すると、Laravelの静的解析ツールとして Larastan というものが存在し Larastan 自体は、PHPStan の拡張の一つです。

    Larastanには、Laravel アプリケーションで型情報などを PHPStan に認識させるための設定が含まれています。

    上記の理由を踏まえ、Larastan を使うことにしましたので、利用する静的解析ツールをPHPStanに選定しました。

    PHPStanとは

    PHPStan は、Ondřej Mirtes(@OndrejMirtes)さんが開発している PHP コードの静的解析ツールで、MIT ライセンスで公開されています。

    PHP で実装されており、Composer でインストールして利用できます。

    https://phpstan.org/user-guide/getting-started

    Laravelプロジェクトへの適用

    それでは、ここからは実際の Laravel プロジェクトに対して、Larasan(PHPStan)を導入していきます。

    インストール

    Larastan は、Composer でインストール可能です。

    1$ composer require --dev nunomaduro/larastan

    インストールできているかも確認してみましょう。

    1$ ./vendor/bin/phpstan -V
    2PHPStan - PHP Static Analysis Tool 0.12.96

    設定ファイルの作成

    解析実行の設定ファイルを作成します。

    設定ファイルは NEON という YAML に似たファイル形式で、 phpstan.neon というファイル名で作成します。

    1includes:
    2    - ./vendor/nunomaduro/larastan/extension.neon
    3parameters:
    4    paths:
    5        - app
    6        - bootstrap
    7        - config
    8        - database
    9        - resources/views
    10        - routes
    11    excludePaths:
    12        - ./routes/console.php
    13    level: 2

    一つずつ解説していきます。

    include ブロックでは、設定ファイルの読み込み等を指定しています。

    Larastan を利用する際は、venderディレクトリの中nunomaduro/larastan/extension.neon を読み込みます。

    parameters ブロックの pathでは、解析を実行するディレクトリを指定します。

    反対に excludePaths を利用することで除外することも可能です。

    levelの指定もあります。

    level については、0から段階が設定されており、値が小さいほど緩く、値が高いほど厳しいチェックとなります。

    いきなり導入した段階は多くのエラーが出ることが想定されるため、初めは0からスタートするのが良いでしょう。

    解析実行

    解析を実行するには、※phpstan コマンドを利用します。

    下記コマンドで、先ほど作成した、phpstan.neon ファイルに基づいて解析が実行されます。

    ※実行時にメモリ不足によってエラーが起きる場合は、--memory-limit オプションをつけることでメモリ制限をコントロールできるので、1G等に設定し実行するとエラーは起きなくなるはずです。

    1./vendor/bin/phpstan analyse --memory-limit=1G

    エラーが検出されない場合

    1[OK] No errors

    エラーが検出される場合

    1 ------ --------------------------------------------------------------------
    2  Line   index.php
    3 ------ --------------------------------------------------------------------
    4  13     Function something not found.
    5         💡 Learn more at https://phpstan.org/user-guide/discovering-symbols
    6 ------ --------------------------------------------------------------------
    7
    8 [ERROR] Found 1 error

    baselineファイルの作成

    現段階でのエラー改善が難しい場合、baselineファイルを作成することで、そのエラーを無視するようにすることができます。

    baseline ファイルは下記コマンドで作成可能です。

    1./vendor/bin/phpstan analyse --generate-baseline

    実行後、phpstan-baseline.neon というファイルが生成します。

    生成したファイルをphpstan.neon で読み込むように指定することで、エラーが無視できるようになります。

    1includes:
    2    - ./vendor/nunomaduro/larastan/extension.neon
    3    - phpstan-baseline.neon
    4parameters:
    5    paths:
    6        - app
    7        - bootstrap
    8        - config
    9        - database
    10        - resources/views
    11        - routes
    12    excludePaths:
    13        - ./routes/console.php
    14    level: 2

    静的解析の自動化

    ここまでやれば、静的解析を実行できる環境が整いましたが、人によってはチェック有無で漏れが発生する可能性があります。

    必ず実行されるように Github Actions に組み込んでいこうと思います。

    1name: larastan
    2
    3on:
    4  pull_request:
    5    paths:
    6      - "**.php"
    7
    8jobs:
    9  larastan:
    10    runs-on: ubuntu-latest
    11    steps:
    12    - uses: actions/checkout@v2
    13    - name: Setup PHP
    14      uses: shivammathur/setup-php@v2
    15      with:
    16        php-version: '8.0'
    17        tools: composer:v2
    18    - name: Resolve dependencies
    19      run: composer install --no-progress --prefer-dist --optimize-autoloader
    20    - name: Run larastan
    21      run: ./vendor/bin/phpstan analyse --memory-limit=2G --configuration=phpstan.neon

    もし、解析実行時にエラーが起きた場合、PR作成後の commit ファイル上にエラー箇所を吐き出してくれるようになります。

    (下記例では return での PHPDoc に記載されている型と実際に返ってくる型違いエラーの内容です)

    今の PHPDoc の書き方ですと、型 App\Http\Controllers\Illuminate\Pagination\AbstractPaginator で認識してしまっているので、正しく型を認識できるようにしてあげる必要があります。

    1# 現在
    2* @return Illuminate\Pagination\AbstractPaginator
    3
    4# 修正後
    5* @return \Illuminate\Pagination\AbstractPaginator

    細かいのですが、どういった型を返すべきなのかについても注意するようになります。

    まとめ

    PHPStan などの静的解析ツールを導入することで、思わぬバグを防ぐこともできるようになります。

    解析ツールによってバグが減り、トータルの開発工数も改善するはずですので、もし興味ある方は今からでも導入されてみてはいかがでしょうか?

    エンジニア記事

    エンジニア記事

    おすすめ記事