公開日 2026-03-13

PHPStan入門(最初のエラー1件を直す)

WSL2とDocker環境でPHPStanを最小導入し、最初のエラー1件を直して解析0件まで進める。

目次

  1. 前提環境
  2. 1. ゴールと非対象
  3. 2. 新規デモ環境を作成して起動する
  4. 3. PHPStanを導入してバージョンを確認する
  5. 4. 最小設定と失敗するサンプルを作る
  6. 5. 解析を実行して最初のエラー1件を読む
  7. 6. 引数型を修正して解析0件にする
  8. 7. 任意: composer analyse で実行を短縮する
  9. 8. まとめと次の一歩

対象読者: WSL2 + Docker で PHP 開発中で、静的解析を初めて導入する方

既存記事への依存を置かず、WSL2 + Docker 環境で PHPStan を最小導入する手順をまとめます。
ゴールは「最初のエラー1件を確認し、修正して解析0件まで通す」ことです。

主線コマンドは ./vendor/bin/phpstan analyse に固定します。
composer analyse は最後に任意ショートカットとして扱います。

前提環境

  • Windows 11
  • WSL2(Ubuntu)
  • VS Code(Remote - WSL)
  • Docker Desktop(WSL連携有効)

以降のコマンドは、特記がない限り WSL 側ターミナルで実行します。
サービス名は app に固定しています。

1. ゴールと非対象

この記事で到達する状態:

  • PHPStan を phpstan/phpstan:^2.1 で導入できる
  • 最初のエラー1件を確認し、修正して解析0件まで通せる
  • ./vendor/bin/phpstan analyse を主線に継続できる

この記事で扱わない内容:

  • baseline や ignoreErrors の本格運用
  • phpstan-strict-rules などの拡張導入
  • Laravel / Symfony / Slim 向けの専用設定
  • GitHub Actions での自動実行
  • Psalm など他ツールとの比較

2. 新規デモ環境を作成して起動する

WSL 側のシェルで作業します。
Windows 側から始める場合は wsl で Ubuntu に入り、以下を実行してください。

# Windows側から始める場合のみ実行
# wsl
mkdir -p ~/projects/phpstan-first-error-fix-demo
cd ~/projects/phpstan-first-error-fix-demo
mkdir -p docker/php src
code .

compose.yml を作成します。

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    working_dir: /workspace
    volumes:
      - ./:/workspace
    command: ["sleep", "infinity"]

docker/php/Dockerfile を作成します。
Composer が配布アーカイブを展開できるように、ここでは unzip も入れておきます。

FROM php:8.5-cli

RUN apt-get update \
    && apt-get install -y --no-install-recommends unzip \
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /workspace

composer.json を作成します。
この時点で autoload.psr-4src/ を登録しておくと、後続の GreetingBuilder を素直に読み込めます。

{
  "name": "example/phpstan-first-error-fix-demo",
  "type": "project",
  "require": {},
  "require-dev": {},
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}

起動して確認します。

docker compose up -d --build
docker compose exec app php -v
docker compose exec app composer --version
Docker と PHP / Composer の起動確認画面

詰まり時:

  • コンテナが起動しない: docker compose logs app
  • 依存導入で zip 展開エラーが出る: Dockerfile に unzip が入っているか確認し、docker compose up -d --build を再実行

3. PHPStanを導入してバージョンを確認する

PHPStan を導入します。
バージョンを固定しておくと、環境ごとの差分が少なくなり、再現しやすくなります。

docker compose exec app composer require --dev phpstan/phpstan:^2.1
docker compose exec app ./vendor/bin/phpstan --version

docker compose exec では実行パスを明示するほうが安定するため、以後は ./vendor/bin/phpstan を主線にします。
composer require --dev ... を実行すると、composer.jsonrequire-dev には依存が自動追記されます。

詰まり時:

  • phpstan が見つからない: docker compose exec app ls -la vendor/bin を確認
  • 依存解決に失敗する: docker compose exec app php -vdocker compose exec app composer --version を再確認

4. 最小設定と失敗するサンプルを作る

まずは最小設定だけで始めます。
phpstan.neon を作成します。

parameters:
  level: 5
  paths:
    - src

今回は、GreetingBuilder を「入力値から挨拶文を返す最小クラス」として置きます。
ただし最初は、わざと型が食い違う失敗版にします。

src/GreetingBuilder.php を作成します(失敗版)。

<?php
declare(strict_types=1);

namespace App;

final class GreetingBuilder
{
    public function build(int $name): string
    {
        $trimmed = $this->normalizeName($name);

        if ($trimmed === '') {
            return 'Hello, guest!';
        }

        return "Hello, {$trimmed}!";
    }

    private function normalizeName(string $name): string
    {
        return trim($name);
    }
}

ここでは build()int を受け取るのに、normalizeName()string を要求しています。
次章で、この食い違いを PHPStan に見つけてもらいます。

詰まり時:

  • src/ が解析対象に入らない: phpstan.neonpaths を確認
  • クラスの読込で迷う: composer.jsonautoload.psr-4App\\ => src/ になっているか確認

5. 解析を実行して最初のエラー1件を読む

解析を実行します。

docker compose exec app ./vendor/bin/phpstan analyse

実行結果例:

 ------ -------------------------------------------------------------------
  Line   GreetingBuilder.php
 ------ -------------------------------------------------------------------
  10     Parameter #1 $name of method App\GreetingBuilder::normalizeName()
         expects string, int given.
         argument.type
 ------ -------------------------------------------------------------------

 [ERROR] Found 1 error
PHPStan の最初のエラー表示

確認する点は4つです。

  • 問題が起きている行番号は 10
  • 対象メソッドは normalizeName()
  • 期待されている型は string
  • 実際に渡している型は int

つまり、PHPStan は「string が必要なメソッドへ int を渡している」と教えてくれています。
今回は build() の引数型が原因なので、次章でそこを直します。

詰まり時:

  • エラーが0件になる: 4章の失敗版を保存し直して再実行
  • 想定外に複数件出る: phpstan.neonpathssrc だけか確認

6. 引数型を修正して解析0件にする

今回は「名前を受け取って挨拶文を作る」クラスなので、build() の引数は string にそろえます。
src/GreetingBuilder.php を修正版に更新します。

<?php
declare(strict_types=1);

namespace App;

final class GreetingBuilder
{
    public function build(string $name): string
    {
        $trimmed = $this->normalizeName($name);

        if ($trimmed === '') {
            return 'Hello, guest!';
        }

        return "Hello, {$trimmed}!";
    }

    private function normalizeName(string $name): string
    {
        return trim($name);
    }
}

再実行して確認します。

docker compose exec app ./vendor/bin/phpstan analyse

成功時は次のように表示されます。

 [OK] No errors
PHPStan の解析成功表示

今回は intstring に変えるだけの最小修正で済みました。
静的解析は、実行前にこうした型の食い違いを見つけるための道具です。

詰まり時:

  • まだ失敗する: normalizeName() に渡す値の型を見直す
  • 変更が反映されない: 保存漏れと実行場所を確認する

7. 任意: composer analyse で実行を短縮する

主線は ./vendor/bin/phpstan analyse のままですが、任意でショートカットを追加できます。
2章の composer.json を次の最終版に更新します。

{
  "name": "example/phpstan-first-error-fix-demo",
  "type": "project",
  "require-dev": {
    "phpstan/phpstan": "^2.1"
  },
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  },
  "scripts": {
    "analyse": "phpstan analyse"
  }
}

実行例:

docker compose exec app composer analyse

composer analyse が動くのは、Composer scripts 実行時に vendor/bin が PATH に追加されるためです。

詰まり時:

  • composer analyse が失敗する: scripts.analyse のキー名とコマンド文字列を確認

8. まとめと次の一歩

PHPStan 2.1 を最小構成で導入し、型の食い違いを解析で検出・修正して 0 件まで通す一連を確認できました。

実行不能になった場合は3章へ、エラーの読み直しは5章へ、修正の見直しは6章へ戻って確認してください。

次の一歩:

  1. 解析対象を src から testsapp 全体へ広げる
  2. ルールレベルを1段ずつ引き上げて、増えた指摘を整理する
  3. GitHub Actions で composer analyse を自動実行する記事へ進む
最終ディレクトリ構成

シリーズ 2/4

このシリーズ

PHPのテストと品質改善

  1. 1. PHPUnit入門(最初のテスト1本)
  2. 2. PHPStan入門(最初のエラー1件を直す) 現在の記事
  3. 3. GitHub ActionsでPHPUnit / PHP CS Fixer / PHPStanを回す最小CI
  4. 4. PHPUnitでDBテストを始める(PostgreSQL + Docker)