PHP
PHPStan

[PHP] 静的解析ツールPHPStanの機能概要

はじめに

PHPのコードを書いているとコンパイルを必要とする言語のように検知できるエラーは動かす前に検知したいと思うことがあると思います。
PHPStanというツールを使えばコンパイラのようなエラーチェックができます。

本記事はPHPStanのversion0.9.2を対象にしています。

PHPStanとはどのようなツールなのか?

PHPStanは、PHPの静的解析ツールです。
PHPStan - PHP Static Analysis Tool

コードを実行せずに

  • 構文エラーはないか?
  • 関数に渡すパラメータの数が適切か?
  • 未定義のものにアクセスしようとしていないか?
  • 関数に渡す値が関数のパラメータの型宣言と一致するか?
  • PHPDocの内容と関数の戻り値は同じか?

などのエラーを見つけてくれます。もちろん他に沢山のチェック項目があります。詳しくは後述の"PHPStanはどういう観点でコードをチェックするのか?"をご覧ください。

必要なPHPVersionは?

PHPStanを実行するにはPHP7.1以上のVersionが必要。(v0.9.2時点)
PHPStanの実行にはPHP7.1以上が必要
ですが、PHPStanで解析する対象のコードはPHP7で書かれたコードでもPHP5時代に書かれたコードでも大丈夫みたいです。

2018-05-08 訂正
PHPStanの作者から指摘をいただきました。
v0.9.2ではPHP7.0以上
v0.10ではPHP7.1以上が必要らしいです。
masterブランチのREADME.mdを参考にしたので間違えてしまいました。tag0.9.2のREADME.mdには7.0になっていました。失礼しました。

インストール方法

スタンダードなインストール方法

Composerでインストールします。

実行コマンド
$ composer require --dev phpstan/phpstan

GitHub phpstan/phpstan Installation 参照。

依存関係がコンフリクトした場合

If you have conflicting dependencies or you want to install PHPStan globally, the best way is via a PHAR archive. You will always find the latest stable PHAR archive below the release notes. You can also use the phpstan/phpstan-shim package to install PHPStan via Composer without the risk of conflicting dependencies.

この場合の最善の方法はPHARアーカイブを使うことみたいです。
また別の方法としてphpstan/phpstan-shimパッケージをComposer経由でインストールする方法も紹介されています。

実行コマンド
$ composer require --dev phpstan/phpstan-shim
その他

Docker経由でも使えるらしいです
phpstan/docker-image

使い方

基本的な使い方は下記のようになります。
analyseコマンドを使用して解析したエラーがなければ下記のように表示され、エラーがあればエラーの内容が表示されます。

$ vendor/bin/phpstan analyse -l 7 path/to/xxx.php
 0/1 [>---------------------------]   0%
 1/1 [============================] 100%

 [OK] No errors

各オプションに関してはhelpを貼り付けましたので下記をご覧ください。

実行コマンド
$ vendor/bin/phpstan analyse -h
Usage:
  analyse [options] [--] <paths> (<paths>)...
  analyze

Arguments:
  paths                              Paths with source code to run analysis on

Options:
  -c, --configuration=CONFIGURATION  Path to project configuration file
  -l, --level=LEVEL                  Level of rule options - the higher the stricter
      --no-progress                  Do not show progress bar, only results
      --debug                        Show debug information - which file is analysed, do not catch internal errors
  -a, --autoload-file=AUTOLOAD-FILE  Project's additional autoload file path
      --errorFormat=ERRORFORMAT      Format in which to print the result of the analysis [default: "table"]
      --memory-limit=MEMORY-LIMIT    Memory limit for analysis
  -h, --help                         Display this help message
  -q, --quiet                        Do not output any message
  -V, --version                      Display this application version
      --ansi                         Force ANSI output
      --no-ansi                      Disable ANSI output
  -n, --no-interaction               Do not ask any interactive question
  -v|vv|vvv, --verbose               Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  Analyses source code

PHPStanはどういう観点でコードをチェックするのか?

現在、次のチェックを実行しています。

  • instanceofcatchtypehintsやannotationなどに記述されたクラスやインターフェースが存在するか?
  • 変数がスコープ内に存在するか?
  • callされたmethodやfunctionは存在しているか?またアクセス権があるか。
  • callされたプロパティやconstは存在しているか?またアクセス権があるか。
  • プロパティに割り当てられた型は正しいか?
  • コンストラクタやメソッドや関数に渡すパラメータの数や型は正しいか?
  • メソッドや関数の返り値は適切な型を返しているか?
  • sprintf,printfに渡す引数の数は正しいか?
  • (string) 'foo'のような無駄なキャストをしていないか?
  • コンストラクタのパラメーターで未使用なものはないか?
  • 親クラスにコンストラクタが定義されている場合はparent::__construct()しているか?
  • 連想配列のkeyには適切な型が使用されているか?(arrayとかobjectはダメよという意味).
  • 連想配列のkeyが重複していないか?
  • foreachに対して適切な値が渡されているか?
  • Classを参照するときは定義したクラス名と大文字小文字まで完全に一致するか?
  • instanceof, ===, !==, is_int(), is_null()などに対して不正な型を使用していないか?
  • phpDocsの検証
  • clone keywordに対してきちんとobjectが渡されているか?

phpstan/phpstan Consider supporting it on Patreon so I'm able to make it even more awesome! 参照
直訳ではありませんが大体あっていると思います。

levelオプションに関して

実行コマンド
$ vendor/bin/phpstan analyse -l 7 path/to/xxx.php

上記のようにlevelオプションが存在します。
現在は0から7までの8段階あり、0がチェックが最も緩く、7がチェックが最も厳しいです。(v0.9.2時点)
古いversionでは段階はもっと少なかったんですけど増えましたね。
phpstan/phpstan Rule levels 参照。

各レベルでどのようなチェックをするのかは
phpstan/confディレクトリにあるneonファイルに記述されているようです。
興味のある方はgithubの該当のディレクトリへのリンクを貼りつけておきますのでご確認ください。
https://github.com/phpstan/phpstan/tree/master/conf

新規に立ち上げるプロジェクトの場合は厳し目で設定しても良いと思います。
しかし、既存のプロジェクトに導入する場合はエラーが多発すると思いますので緩めの設定でスタートして徐々にレベルを上げていく感じになると思います。
適切な設定レベルを探ってください。

phpstan.neonを使用した設定値のカスタマイズ

phpstan.neonというファイルを作成し、パラメータを設定することでphpstanの設定値をカスタマイズできます。(neonファイルって初めて聞きました。。。)

実行コマンド
$ vendor/bin/phpstan analyse -c phpstan.neon -l 7 path/to/xxx.php

phpstan/phpstan Configurationには

  • autoload_directories
  • autoload_files
  • excludes_analyse
  • fileExtensions
  • universalObjectCratesClasses
  • earlyTerminatingMethodCalls
  • ignoreErrors
  • bootstrap

の設定例が記述されています。ここには書かれていませんが、おそらくphpstan/conf/config.neonに書かれているパラメータはphpstan.neonで変更が可能なのだと思います。

config.neonに書かれている設定値
parameters:
    bootstrap: null
    excludes_analyse: []
    autoload_directories: []
    autoload_files: []
    fileExtensions:
        - php
    checkAlwaysTrueCheckTypeFunctionCall: false
    checkAlwaysTrueInstanceof: false
    checkClassCaseSensitivity: false
    checkFunctionArgumentTypes: false
    checkArgumentsPassedByReference: false
    checkMaybeUndefinedVariables: false
    checkNullables: false
    checkThisOnly: true
    checkUnionTypes: false
    polluteScopeWithLoopInitialAssignments: true
    polluteCatchScopeWithTryAssignments: false
    ignoreErrors: []
    internalErrorsCountLimit: 50
    reportUnmatchedIgnoredErrors: true
    universalObjectCratesClasses:
        - stdClass
        - SimpleXMLElement
    earlyTerminatingMethodCalls: []
    memoryLimitFile: %tmpDir%/.memory_limit
...

色々書いたけど。。。

メルカリではこのツールを使っているらしいですね。こっちのスライドをみたほうがざっと理解できるかも。
PHPStanで始める継続的静的解析

  • 導入の背景
  • phanではなくPHPStanを選んだ理由
  • PHPStanの注意点
  • CIと組み合わせた活用事例

などがスライドにまとめられています。

2018-05-09 追加
PHPStanで始める継続的静的解析」を発表した時の動画をYouTubeで見つけました。
https://www.youtube.com/watch?v=1RQsHQJCMJ4

おわりに

まだまだ日本語の情報が少ないので英語のドキュメントを翻訳しながら記事を書きました。
英語は得意ではないので解釈が間違っていたらご指摘をお願いします。
また、PHPStanの活用事例があればコメントを頂けると嬉しいです。

ちなみにPHPStanの開発者のblogによると2018年6月に0.10がリリースされるらしいですよ。