PHPStan の使い方

NEW
2021/09/06

PHPのコードを実行する前に、バグがあるかどうか調べられると便利だとは思いませんか? PHPはスクリプト言語ですので、いくら文法的に正しいコードであっても、実際に実行させるまでバグか発生するかどうかわからないという、スクリプト言語であるが故の本質的な問題を抱えています。 C や Java など他のコンパイル言語ではコンパイル時にエラーになるようなコードであっても、スクリプト言語であるPHPでは、実行させるまでエラーになるかどうかわからないのです。

PHPStan とは?

PHPのコードをチェックする似たようなツールとして PHP_CodeSniffer や、PHPの lint コマンド php -l が思いつきますが、それらはあくまで文法レベルでのチェックにすぎません。 いくら文法が正しくともエラーは発生します。

では、PHPUnit などの単体テストを実行して見つければ良いじゃないかと思うかもしれません。 しかし、テストコードを書くのは、かなり大変なことです。 コードカバレッジ 100% のテストコードを書くなどというのは、目標ではあるのかもしれませんが、非効率的であり、非現実的な行為となります。

文法が正しくとも、テストを実行しなくとも、実際に動作させなくても、明らかにバグだとわかるコードというものは存在します。

PHPStan は、PHPコードを実行する前に(つまり静的に)、PHPコードを解析し、明らかにバグだとわかるコードを探して指摘してくれる、PHPの静的解析ツールです。

Stan という名前は、static analysis (つまり静的解析)の略のようです。

PHPStan のインストール

さっそくですがインストールしていきましょう。

Composer でインストールする

PHPStan は Composer を使ってインストールします。下記のコマンドを実行するだけです。 基本的には開発環境で使うツールだと思うので、--dev オプションを付けておきましょう。 Composer の使い方がわからないという方は、まずは「Composer 再入門」を参考にしてください。

php composer.phar require --dev "phpstan/phpstan"

インストールできたか確認する

PHPStan をインストールすると、phpstan コマンドが使えるようになっていますので、使用できるか確認してみましょう。 実行ファイルは ".\vendor\bin\" ディレクトリにあります。

.\vendor\bin\phpstan -h

基本的な使い方

インストールできたので、さっそく使ってみます。

ファイルを解析するには phpstan analyse コマンドを使います。

単一のファイルを解析する

1つのファイルを解析する場合は、解析したいファイル名を指定します。 解析したいファイル名はスペース区切りで複数指定できます。

".\src\hoge.php" ファイルを解析する場合
.\vendor\bin\phpstan analyse .\src\hoge.php
".\src\hoge.php" ファイルと ".\src\fuga.php" を解析する場合
.\vendor\bin\phpstan analyse .\src\hoge.php .\src\fuga.php

複数のファイルを解析する

複数のファイルを解析する場合は、解析したいディレクトリ名を指定します。 解析したいディレクトリ名はスペース区切りで複数指定できます。

".\src" ディレクトリを解析する場合
.\vendor\bin\phpstan analyse .\src
.\src ディレクトリと .\tests ディレクトリを解析する場合
.\vendor\bin\phpstan analyse .\src .\tests

ルールレベル

PHPStan では、どこまで厳密に解析を行うかのレベルを設定することができます。 レベルは 0 ~ 8 の 9 段階あり、レベル 0(デフォルト)が最も緩く、レベル 8 が最も厳密になります。 解析する際、設定されたレベル以下の全てのチェックが実行されます。 例えば、レベル 5 に設定すると、レベル 0 ~ 5 の全てのチェックが実行されます。

レベルを上げると、より厳密な解析になってはいきますが、その分だけ解析にかかる時間も長くなり、検出個所も多くなります。 検出個所が多くなるということは、その検出個所に対応する時間も比例して多くなってしまうということです。 修正する必要のない個所もたくさん検出されることになるでしょう。 些細な問題に対して時間をかけてしまい、実際のバグを見逃してしまっていては、本末転倒にもなりかねません。

最初はレベル 0(デフォルト)から始めて、検出個所が無くなったら、1つずつ徐々にレベルを上げていきましょう。 一気にレベルを上げてしまうと、どれから手を付けて良いかわからなくなってしまいます。

どのレベルで、どんな内容の解析が行われるかは、ここでは記載しません。 今後 PHPStan がバージョンアップして、レベルが新しく増える可能性もあるようなので、 詳しく知りたい方は、英語ですが公式のドキュメントを見てもらった方が良いと思います。

レベルを変更する

レベルを変更するには --level オプションを使用します。 --level オプション には、エイリアス(別名) -l オプションがあります。

レベルを 6 に変更する場合
.\vendor\bin\phpstan analyse -l 6 .\src

最大のレベルに設定する

最大のレベルを設定する際には、エイリアス(別名)max が使用できます。 現状ではレベル 8 が最大ですが、今後 PHPStan がバージョンアップして、9 以上のレベルが新しく増えた場合でも、常に最大のレベルで動作させることができます。

.\vendor\bin\phpstan analyse -l max .\src

構成ファイル

PHPStan では、特定のエラーを無視したり細かな設定ができるのですが、コマンドのパラメーターだけでは、さすがに全てを指定しきれません。 そのため、PHPStan には構成ファイルを読み込む機能があります。

構成ファイルのフォーマット

構成ファイルのフォーマットは、NEON です。 NEON は、YAMLと非常によく似ているため、YAML に慣れている方は簡単に NEON を作成することができると思います。

ここでは簡単な例だけ記載しておきます。

phpstan.neon
parameters:
	level: 6
	paths:
		- src
		- tests

どんな設定項目があるかは、ここでは記載しません。 あまりにも大量にありますので、英語ですが公式のドキュメントを見てもらった方が良いと思います。

構成ファイルを使用する

構成ファイルを使用する方法は2つあります。

--configuration オプションで指定する

1つ目の方法は、--configuration オプションで使用したい構成ファイルを指定することです。 複数の構成ファイルを使用して、設定を切り替えたい場合などには便利かもしれません。 --configuration オプション には、エイリアス(別名) -c オプションがあります。

.\vendor\bin\phpstan analyse -c .\custom_phpstan.neon

デフォルトの構成ファイルを使用する

2つ目の方法は --configuration オプションは指定せず、デフォルトの構成ファイルを使用する方法です。

--configuration オプションを指定しない場合(つまりデフォルト)、 PHPStan は現在のディレクトリから構成ファイルを探します。 この時に探されるファイルの名前は、「phpstan.neon」「phpstan.neon.dist」の2つです。 この2つのファイル名のどちらかと一致するファイルが見つかった場合に、そのファイルが自動的に読み込まれます。 もし複数のファイルが見つかった場合は、前述の順番で優先的に読み込まれます。

.\vendor\bin\phpstan analyse

ルールレベルは必須項目

構成ファイルを使用する場合は、ルールレベルの指定が必須になります。 デフォルト値の 0 も、構成ファイルを使用している場合は反映されません。 コマンドの --level オプションで指定するか、構成ファイル内に level 項目で指定するか、 どちらかは必須となるので注意してください。

複数の構成ファイル

PHPStan で自動的に読み込まれる構成ファイルは、phpstan.neon と phpstan.neon.dist の2つです。 通常は、phpstan.neon.dist の方を Git などのバージョン管理下に置き、phpstan.neon の方を .gitignore に追加します。 各ユーザーが phpstan.neon.dist を元にして、phpstan.neon を作成することで、 各環境内(各ユーザーのPCや、CIサーバーなど)で特定の設定を上書きできるようにします。 phpstan.neon と phpstan.neon.dist の両方のファイルがある場合に、phpstan.neon の方が優先して読み込まれるのはそのためです。

PHPStan の構成ファイルでは、複数のファイルから構成するための includes の項目があります。 そのため、phpstan.neon ファイルから、phpstan.neon.dist ファイルを includes の項目で読み込んで使用できます。

phpstan.neon.dist
parameters:
	level: 6
	paths:
		- src
		- tests
phpstan.neon
includes:
	- phpstan.neon.dist

parameters:
	level: 8

	ignoreErrors:
		- '#Function pcntl_open not found\.#'

エラーを無視する

PHPStan で検出されたエラーの中には、あきらかに対応できないものや、対応する必要のないものなどがあったりします。 そういったものが常に検出されていると、作業の邪魔になってしまいます。 そこで、特定のエラーを検出の対象外として、無視させることができます。

方法としては主に2つあります。

PHPDocsでエラーを無視させる

1つ目の方法は、PHPのコード上にPHPDocsとして記述する方法です。 この方法は、ピンポイントで指定できるというメリットはありますが、大量に使用した場合はPHPのコードがコメントで汚れてしまうというデメリットがあります。 できるなら、後述の構成ファイルに記載する方法の方が良いでしょう。

使用するには、PHPのコメント(///* *//** */のいずれでも可)の中で、下記のタグを使用します。

  • @phpstan-ignore-line は、それが書かれたその行を無視します。
  • @phpstan-ignore-next-line は、それが書かれたその次の行を無視します。
PHPファイル
<?php

echo $foo; // @phpstan-ignore-line

/* @phpstan-ignore-next-line */
echo $foo;

構成ファイルに記載してエラーを無視させる

2つ目の方法は、エラーを無視する個所を、構成ファイルに記載することです。 構成ファイルの ignoreErrors 項目の下に正規表現を追加することで、そのエラーを無視できます。 正規表現ですので、「|」「$」「.」「(」「)」などの特殊文字はエスケープする必要があるので注意です。

プロジェクト全体でエラーを無視する

プロジェクト全体でエラーを無視するには、ignoreErrors の項目に、正規表現によるエラーメッセージを指定します。

phpstan.neon
parameters:
	ignoreErrors:
		- '#Call to an undefined method [a-zA-Z0-9\\_]+::doFoo\(\)#'
		- '#Call to an undefined method [a-zA-Z0-9\\_]+::doBar\(\)#'

特定のファイル/ディレクトリでエラーを無視する

特定のファイル/ディレクトリでエラーを無視するには、message と、path もしくは paths の項目を含むエントリーを追加します。

  • message の項目には、正規表現によるエラーメッセージを指定します。
  • path もしくは paths の項目には、特定のファイル/ディレクトリのパスを指定します。 PHPの fnmatch() 関数と互換性のある形式のワイルドカードが使用できます。
  • また、必須ではありませんが、path を使用した場合のみ、count の項目で、エラーが予想される回数を指定することもできます。
phpstan.neon
parameters:
	ignoreErrors:
		-
			message: '#Access to an undefined property [a-zA-Z0-9\\_]+::\$foo#'
			path: some/dir/SomeFile.php
		-
			message: '#Call to an undefined method [a-zA-Z0-9\\_]+::doFoo\(\)#'
			path: other/dir/DifferentFile.php
			count: 2 # optional
		-
			message: '#Call to an undefined method [a-zA-Z0-9\\_]+::doBar\(\)#'
			paths:
				- some/dir/*
				- other/dir/*
		- '#Other error to ignore everywhere#'

ベースライン機能

PHPStan をプロジェクトの開始の時点で導入できていれば良いですが、現実ではプロジェクトの途中で導入することもあると思います。 大量のコードに対して PHPStan を実行すると、大量の検出個所が報告されることになります。

もちろん、その全ての検出個所の修正を先に終わらせるのが理想ではあります。 しかし、修正箇所があまりに多く、対処する十分な時間やエネルギーがない状況では、 現時点で見つかった検出個所の修正を一旦後回しにし、新しく発生した個所に注力していく戦略をとるというのも、決して間違いとは言えません。

ベースライン機能を使用すると、現時点を作業を開始する基準とし、現在発生している全てのエラー無視させることができるようになります。 これにより、新しく変更されたコードのみに注力することができるようになります。

ベースライン機能を使用するには、まず現在発生している全てのエラーの一覧をベースラインファイルとして保存します。 次に、その保存したベースラインファイルを、構成ファイルから読み込みます。 すると、次回実行した際に、そのベースラインファイルに記載されているエラーは無視されるようになります。

上述の構成ファイルに記載してエラーを無視する機能の応用といった形でしょうか。

ベースラインファイルを保存する

現在発生している全てのエラーの一覧をベースラインファイルに保存するには、--generate-baseline オプションを使用します。 ファイル名は --generate-baseline オプションのパラメーターとして指定することができます。 ファイル名が未指定の場合、ファイル名は phpstan-baseline.neon になります。 このベースラインファイルは、構成ファイルと同じ形式の NEON ファイルですので、手動で編集することも、再度生成しなおすこともできます。

ファイル名を指定しない場合
.\vendor\bin\phpstan analyse --generate-baseline
ファイル名を指定する場合
.\vendor\bin\phpstan analyse --generate-baseline=basline.neon

構成ファイルからベースラインファイルを読み込む

ベースラインファイルを読み込むには、構成ファイルの includes の項目で、読み込むファイルを指定します。

phpstan.neon
includes:
	- phpstan-baseline.neon

parameters:
	# your usual configuration options

コードとベースラインファイルの食い違い

ベースラインファイルに記載されているエラーの個所のコードが修正されエラーが発生しなくなった場合、 ベースラインファイルにはエラーを無視する記載が残ったままになってしまい、コードと食い違ってしまうという状況が発生します。 ベースラインファイルから、修正された箇所のエラーを無視する記載を削除する必要があります。

この状況が発生した場合、PHPStan が通知してくれます。 もしこの通知が不要な場合は、構成ファイルで reportUnmatchedIgnoredErrorsfalse に設定することで、オフにすることができます。

phpstan.neon
includes:
	- phpstan-baseline.neon

parameters:
	reportUnmatchedIgnoredErrors: false

エラーがあってもより高いルールレベルを実行する

ルールレベルを上げるには、そのレベルでのエラーが 0 件になっているのが理想です。 しかしながら、大量に修正箇所がある場合、レベルが上がるまでかなりの時間を費やすことになります。 それはつまり、より高いレベルで見つかるようなエラーはずっと見逃している状態になることを意味します。

ベースラインを使用すると、その時点のエラーを全て無視することができますから、 低いレベルでの修正が全て終わるのを待たずに、一気にレベルを上げることが可能になります。 これにより、新しく変更されたコードには、より高いレベルでの厳密なチェックを適用することが可能です。

Composer スクリプトとして登録する

構成ファイルがあるとはいえ、コマンドを毎回入力するのは面倒に感じてくると思います。 特に、ここまでの説明で散々繰り返し記載してきた ".\vendor\bin" を記述するのが面倒に感じてくると思います。 そこで、コマンドを Composer スクリプトとして登録しておくと、入力の手間が少なくなって便利です。 Composer スクリプトでは、 ".\vendor\bin" に PATH が通っているので、".\vendor\bin" を入力する必要はありません。 Composer スクリプトの使い方がわからないという方は、「Composer 再入門(その2:スクリプト)」を参考にしてください。

composer.json
{
	"name": "hoge/hoge",
	"require-dev": {
		"phpstan/phpstan": "^0.12.98"
	},
	"scripts": {
		"stan": "phpstan analyse"
	}
}
登録したスクリプト stan を実行する
php composer.phar stan

色がちゃんとつかない場合

Composer スクリプトから実行した場合に、出力内容に色がちゃんとつかない場合があります。 その場合は、--ansi オプションを付けてみましょう。

composer.json
{
	"name": "hoge/hoge",
	"require-dev": {
		"phpstan/phpstan": "^0.12.98"
	},
	"scripts": {
		"stan": "phpstan analyse --ansi"
	}
}

参考サイト

関連記事

プログラムを書いていると気になるのがコードの読みやすさです。複数人で開発している場合などは特にコードの読みやすさは重要です。PHP_CodeSniffer とは?PHP_CodeSniffer は、PHPのコードがちゃんとコーディング規約に沿って記述されているか検査したり、コーディング規約に違反している個所を自動的に修正してくれるツールです。コードをクリーン ...
PHP_CodeSniffer や PHPStan などで、コードの文法的な正しさは確認できますが、そのコードが本当に正しい動作を行っているかどうかを確認するためには、やはり最終的には動作させてみるしかありません。PHPUnit とは?PHPUnit は、PHPのテストフレームワークです。その名前からわかる通り、基本的には単体テスト(UnitTest)に使用 ...
PHPでの開発効率を向上させるために、PHP自体に機能を追加するという方法があります。Xdebug とは?Xdebug は、PHPでの開発効率を向上させるためのさまざまな機能を提供してくれるPHPの拡張機能(エクステンション)です。Xdebug でできることは下記の6つです。ステップ実行:スクリプトの実行中にIDEまたはエディターでコードをステップ実行開発ヘ ...
もはやPHPで開発を行う際に、使用していないプロジェクトは探すのが大変なぐらいスタンダードな存在となった Composer ですが、昨年めでたく 2.0 になったということで、改めて少しまとめてみます。今更とか言っちゃダメです。Composer とは?Composer は、PHPの依存関係管理のためのツールです。世界中のエンジニアが作成してくれたライブラリ( ...

記事検索

最新記事

RSSフィード