Laravelプロジェクトの静的解析をLarastanで始める入門
概要
PHP は実行するまでエラーに気づきにくい言語です。タイプミスした変数、存在しないメソッド呼び出し、null が返るのに前提にしていない処理――こうした問題はテストで踏まない限り本番まで届いてしまいます。
静的解析ツール PHPStan は、コードを実行せずにこうした欠陥を検出します。そして Larastan は、その PHPStan を Laravel 向けに強化する拡張です。Eloquent のマジックメソッドやファサードなど「Laravel 特有で静的解析が苦手とする書き方」を理解できるようにしてくれます。
この記事では、既存の Laravel プロジェクトに Larastan を導入し、エラーを少しずつ潰していくところまでを入門として扱います。
環境
- PHP 8.2 以上
- Laravel 11 / 12 / 13 系
- Composer 2 系
- Larastan 3 系(
larastan/larastan)
Larastan のパッケージ名は歴史的に nunomaduro/larastan でしたが、現在は larastan/larastan に移管されています。新規導入では後者を使ってください。
現行の Larastan 3 系は内部で PHPStan 2.x 系を要求します(Larastan 2 系は PHPStan 1.x 系前提でした)。composer require の際は PHPStan の系統が 2.x に揃うことになる点を押さえておいてください。あわせて、Larastan 3 系は Laravel 11.15 以降を対象としているため、Laravel 10 以前のプロジェクトでは 2 系を使うか、先に Laravel 側をアップグレードする必要があります。
発生した問題
静的解析を入れていないプロジェクトでは、次のような問題が「動かしてみるまで」わかりません。
- 変数名のタイプミス(
$usrと$userの取り違え) - 存在しないメソッド・プロパティへのアクセス
nullableな値を非 null 前提で扱っている箇所- 型が合わない引数の受け渡し
- 到達不能コードや無意味な条件分岐
レビューで人力検出するのは限界があり、プロジェクトが大きくなるほど見落としが増えます。
原因
PHP は動的型付けで、しかも Laravel は「書きやすさ」を優先してマジックメソッドを多用します。たとえば次のコードは、素の PHPStan から見ると where も first も「どこに定義されているのか不明」です。
$user = User::where('email', $email)->first();
User モデルに where という静的メソッドは実装されていません。これは __callStatic 経由で Eloquent のクエリビルダに委譲されているためです。素の静的解析ではここで誤検出が大量に出てしまい、実用になりません。
Larastan はこの「Laravel の魔法」を解決するルールと型定義を持ち込むことで、Eloquent・ファサード・コンテナを正しく追跡できるようにします。
対応方法
1. インストール
開発依存としてインストールします。
composer require --dev larastan/larastan
2. 設定ファイルを作る
プロジェクトルートに phpstan.neon(または phpstan.neon.dist)を作成します。
includes:
- vendor/larastan/larastan/extension.neon
parameters:
level: 5
paths:
- app
excludePaths:
- app/Legacy/*
ポイントは includes で Larastan の拡張を読み込むことです。これを忘れると、ただの PHPStan として動作してしまいます。
3. 実行する
./vendor/bin/phpstan analyse
初回はおそらく大量のエラーが出ます。ここで心が折れないことが重要です。後述のレベル調整とベースライン機能で、無理なく始められます。
実装例
レベルは 0 から段階的に上げる
PHPStan には 0(最も緩い)から 10 / max(最も厳しい)までの 11 段階のレベルがあります。最上位の 10 は PHPStan 2.0 で追加されたもので、Larastan 3 系(=PHPStan 2.x 系)なら利用できます。既存プロジェクトにいきなり max を当てると数千件のエラーに埋もれます。
おすすめは level: 0 から始めて、緑になったら 1 ずつ上げる 進め方です。
parameters:
level: 0
各レベルの大まかな守備範囲は次のとおりです。
| レベル | 主に検出するもの |
|---|---|
| 0 | 存在しないクラス・関数・基本的な構文の誤り |
| 1〜2 | 未定義変数、未知のメソッド・プロパティ |
| 3〜4 | 戻り値・代入の型の不一致、到達不能コード |
| 5〜6 | 引数の型チェック、型ヒント不足の検出 |
| 7〜8 | union 型の部分的な誤り、nullable な値の厳密な扱い |
| 9 | 明示的な mixed の厳密な扱い |
| 10 | 暗黙的な mixed(型が書かれていない箇所)の検出 |
9 と 10 の違いがわかりにくいところですが、9 は「開発者が意図的に書いた mixed」を厳密に扱うのに対し、10 は「型が書かれていないために暗黙的に mixed になっている箇所」まで洗い出します。最初から 10 を狙わず、まずは 5〜6 あたりで型ヒント不足を潰し、そのうえで段階的に引き上げるのが現実的です。
ベースラインで「既存の負債」を凍結する
いきなり全部直すのが難しいときは、現状のエラーをいったん「既知の問題」として退避できます。
./vendor/bin/phpstan analyse --generate-baseline
生成された phpstan-baseline.neon を設定で読み込みます。
includes:
- vendor/larastan/larastan/extension.neon
- phpstan-baseline.neon
こうすると 既存のエラーは無視され、新しく増えたエラーだけが検出される ようになります。新規コードから品質を担保しつつ、既存分は余裕があるときに少しずつ減らす、という現実的な運用ができます。
Composer スクリプトに登録する
{
"scripts": {
"analyse": "phpstan analyse --memory-limit=1G"
}
}
composer analyse
解析対象が多いとメモリ不足になりやすいので、--memory-limit を指定しておくと安定します。
PHPDoc で型を補ってヒット率を上げる
/**
* @return \Illuminate\Database\Eloquent\Collection<int, \App\Models\User>
*/
public function activeUsers(): Collection
{
return User::where('active', true)->get();
}
情報を与えるほど Larastan の解析精度が上がります。
確認方法
./vendor/bin/phpstan analyseが[OK] No errorsを返せば、そのレベルはクリアです- レベルを 1 上げて再実行し、増えたエラーを確認します
- CI に組み込む場合は、終了コードが 0 以外なら失敗する点を利用します
GitHub Actions に組み込む例です。
name: Static Analysis
on: [push, pull_request]
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- run: composer install --no-interaction --prefer-dist
- run: ./vendor/bin/phpstan analyse --no-progress
これで、解析でエラーが出る PR は CI が赤くなり、マージ前に気づけます。
注意点
includesの読み込み忘れ が最頻出のつまずきです。恩恵が出ないときはまずここを疑ってください- レベルを上げすぎて大量のエラーに埋もれると続きません。ベースライン+段階的レベル上げ が現実的です
- 解析対象に
vendorやマイグレーション、自動生成ファイルを含めると誤検出やノイズが増えます。paths/excludePathsで絞り込みます - メモリ不足は
--memory-limitで対処します - パッケージ名は新しい
larastan/larastanを使います。Larastan 3 系は PHPStan 2.x 系を前提とするため、PHPStan を個別に固定している場合はバージョン整合を確認してください
まとめ
- Larastan は PHPStan を Laravel 向けに拡張し、Eloquent やファサードの「魔法」を静的解析できるようにするツールです
- 導入は「
composer require --dev→phpstan.neon作成 →analyse実行」の3ステップです - 既存プロジェクトには レベル 0 から段階的に上げる+ベースラインで負債を凍結 が現実的な始め方です
- CI に組み込めば、型の崩れや未定義呼び出しをマージ前に検出でき、実行前に多くのバグを潰せます
静的解析は「動かす前にレビュワーがもう一人増える」ようなものです。まずはレベル 0 で緑を出すところから始めてみてください。
