PHPアプリの構文に問題がないかやバグの予兆・コーディングルールの違反がないかなどをチェックしたい場合、PHPStanなどの静的解析ツールを利用します。
この記事では、Symfonyで作られたアプリにPHPStanを導入し、CircleCIで自動実行する方法を紹介します。
本記事の手順は以下の環境で検証しています。
- Symfony 7.4 / PHP 8.3
- PHPStan 2.1
Symfony プロジェクトのセットアップ
Symfony と PHPStan を動かすには PHP 本体に加えて複数の拡張(intl、zip など)が必要です。ホスト OS に PHP と拡張を直接インストールすると、他のプロジェクトとのバージョン衝突や、OS(macOS / Linux / Windows)ごとのインストール手順の差異が発生します。本記事では、こうした手間を避けるためにローカル開発環境を Docker コンテナで構築します。
CI 側では同じ Dockerfile.dev は使わず、CircleCI が提供する cimg/php:8.3 を使います。cimg/php は PHP 本体と Composer v2、よく使われる拡張がインストール済みで、CircleCI の実行環境で高速に起動するように最適化されているため、CI 上では追加のビルドなしで Composer と PHPStan を実行できます。
Dockerfile.devを作成する
まずはローカル開発用のイメージを用意しましょう。PHP 8.3 CLI に Composer と必要最小限の拡張を入れています。
FROM php:8.3-cli
RUN apt-get update && apt-get install -y \
git \
unzip \
libzip-dev \
libicu-dev \
&& docker-php-ext-install \
intl \
zip \
&& rm -rf /var/lib/apt/lists/*
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /workspace
docker-compose.ymlを追加する
続いてdocker-compose.ymlを作ります。ここではソースコードをマウントし、Composer キャッシュを永続化します。
services:
php:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/workspace
- composer-cache:/root/.composer/cache
working_dir: /workspace
tty: true
volumes:
composer-cache:
これでコンテナ環境が用意できました。
コンテナの起動と Symfony プロジェクトの初期化
コンテナをビルド・起動し、Symfony を Symfony Skeleton からセットアップします。
# コンテナ起動
docker compose up -d
# Symfony Skeleton を一時ディレクトリ経由でセットアップ
docker compose exec php bash -c "\
composer create-project symfony/skeleton /tmp/symfony-init --no-interaction && \
cp -a /tmp/symfony-init/. /workspace/ && \
rm -rf /tmp/symfony-init"
PHP 8.3 の場合、Symfony 8.0(PHP 8.4 以上が必要)は Composer の依存解決で自動的にスキップされ、Symfony 7.4 がインストールされます。
Docker向けのファイルを先に作成する関係上、composer create-projectをプロジェクトルートで実行するとエラーが出ます。
% docker compose exec php composer create-project symfony/skeleton . --no-interaction
Creating a "symfony/skeleton" project at "./"
In CreateProjectCommand.php line 368:
Project directory "/workspace/." is not empty.
そのため少し複雑になりますが、一時ディレクトリに展開してから移動する形でセットアップしています。
PHPStan の導入
続いてPHPStan 本体と Symfony 向け拡張、拡張の自動登録を行う Composer プラグインをインストールします。
docker compose exec php composer require --dev \
phpstan/phpstan \
phpstan/phpstan-symfony \
phpstan/extension-installer
各パッケージの役割は以下の通りです。
-
phpstan/phpstan— PHPStan 本体 -
phpstan/phpstan-symfony— Symfony 固有のパターン(サービスコンテナ、Request::get()の戻り値、コンソールコマンド等)を理解させる拡張 -
phpstan/extension-installer— PHPStan 拡張のincludes設定を自動登録する Composer プラグイン。これをインストールしておくと、phpstan.dist.neonにphpstan-symfonyのextension.neonを手動でincludesに書く必要がなくなります(公式ドキュメント)
インストール中に 2 種類の確認プロンプトが表示されます。
1 つ目は Composer 2.2.0 以降の allow-plugins 機能によるものです。
Do you trust "phpstan/extension-installer" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?]
phpstan/extension-installer` が Composer プラグインとしてコードを実行することを許可するかを確認します。y で許可すると composer.json の config.allow-plugins に記録され、以降は確認なしで実行されます。
2 つ目は Symfony Flex の contrib レシピ確認で、phpstan/phpstan のレシピが symfony/recipes-contrib(コミュニティ貢献のレシピリポジトリ)にあるため、実行するかどうかを確認されます。
- WARNING phpstan/phpstan (>=1.0): From github.com/symfony/recipes-contrib:main
The recipe for this package comes from the "contrib" repository, which is open to community contributions.
Review the recipe at https://github.com/symfony/recipes-contrib/tree/main/phpstan/phpstan/1.0
Do you want to execute this recipe?
[y] Yes
[n] No
[a] Yes for all packages, only for the current installation session
[p] Yes permanently, never ask again for this project
(defaults to n):
y で今回のみ実行、p を選ぶと composer.json に extra.symfony.allow-contrib: true が書き込まれ、以降このプロジェクトでは確認なしに contrib レシピが実行されるようになります。
インストールが完了すると、Symfony Flex のレシピにより phpstan.dist.neon がプロジェクトルートに自動生成されます。
parameters:
level: 6
paths:
- bin/
- config/
- public/
- src/
- tests/
このうち、- test/の行だけ削除してください。これは今回の記事ではテストファイルを作成しないため、PHPStan実行時に「ディレクトリが見つからない」というエラーが出ないようにするためです。
parameters:
level: 6
paths:
- bin/
- config/
- public/
- src/
- - tests/
PHPUnit などを導入してテストコードを書き始めたら、paths に tests を追加してください。
これで静的解析の準備が完了しました。
ローカルで PHPStan を実行して確認
まずはローカルで動かしてみましょう。解析のレベルや対照などはphpstan.dist.neonファイルに定義されていますので、オプションを設定せずに実行できます。
docker compose exec php vendor/bin/phpstan analyse
初期状態であれば [OK] No errors と表示されます。
% docker compose exec php vendor/bin/phpstan analyse
Note: Using configuration file /workspace/phpstan.dist.neon.
5/5 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
[OK] No errors
PHPStanで構文エラーをチェックする
簡単にPHPStanがどのようなエラーを出すかチェックしてみましょう。src/Kernel.phpを次のように変更します。
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
- use MicroKernelTrait;
+ use MicroKernelTrait
}
Node.jsとPHPを同時にコーディングしている際などにやりがちなミスですね。PHPStanをもう一度実行してみましょう。
% docker compose exec php vendor/bin/phpstan analyse
Note: Using configuration file /workspace/phpstan.dist.neon.
5/5 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ ---------------------------------------------------------------
Line src/Kernel.php
------ ---------------------------------------------------------------
11 Syntax error, unexpected '}', expecting ';' or '{' on line 11
🪪 phpstan.parse (non-ignorable)
------ ---------------------------------------------------------------
[ERROR] Found 1 error
⚠️ Result is incomplete because of severe errors. ⚠️
Fix these errors first and then re-run PHPStan
to get all reported errors.
構文エラーの発生を検知できました。このようにしてコードやアプリを動かす前にミスをチェックできる点も静的解析がもつメリットです。
.circleci/config.yml の作成
ローカルでの解析準備が完了しました。しかしローカルでの手動実行だけでは、レビュアーが「本当に静的解析をパスしたコードやPRであるかどうか」を判断できません。そこでCircleCIのようなCIサービスを利用し、pushやPR作成をトリガーにチェックが自動実行されるようにしましょう。
まずはプロジェクトルートに .circleci/config.yml を作成し、以下の内容を記述します。
version: 2.1
jobs:
phpstan:
docker:
- image: cimg/php:8.3
resource_class: medium
environment:
APP_ENV: test
steps:
- checkout
- restore_cache:
keys:
- composer-v1-{{ checksum "composer.lock" }}
- composer-v1-
- run:
name: Composer install
command: composer install --no-interaction --prefer-dist
- save_cache:
key: composer-v1-{{ checksum "composer.lock" }}
paths:
- vendor
- run:
name: PHPStan
command: vendor/bin/phpstan analyse --no-progress
workflows:
ci:
jobs:
- phpstan
こちらもPHPStanはDockerコンテナ内で実行します。CircleCI が提供するPHP コンビニエンスイメージであるimage: cimg/php:8.3を利用し、PHPやComposerのインストールなどを省略しています。
今回は静的解析が目的なのでセットアップ手順などを簡略化するため、コンビニエンスイメージを利用しました。しかしもしPHPUnitやE2Eテストなどで実際の環境と同じ状況でテストを実行したい場合は、今回作成したDockerfile.devを利用する形でCIを構築できます。
ワークフローファイルを少し見てみましょう。
まずenvironment セクションにて APP_ENV=test を指定しています。これはSymfonyの実行環境をtestモードにするための設定です。
また、Composerのインストールステップを高速化するため、restore_cache / save_cacheを使ってインストール結果のキャッシュを行います。
composer.lock のチェックサムをキーとして vendor/ ディレクトリをキャッシュします。composer.lock が変更されない限りキャッシュから復元されるため、2回目以降の composer install は高速で完了します。
GitHub に push して CircleCI で実行する
あとは変更をGitでコミットし、pushしておきましょう。
git add .
git commit -m "Symfony + PHPStan + CircleCI"
git push origin main
CircleCI側でトリガーの設定などが完了していれば、プッシュを検知してパイプラインが実行されます。
GitHubとCircleCIの連携方法などは、以下の記事も参考にしてください。
PHPStan の解析が成功すると、以下のように全ステップが緑色で表示されます。
PHPStan がエラーを検出した場合は「PHPStan」ステップが赤色に変わり、ジョブが失敗します。
エラーの詳細は該当ステップのログで確認できます。
エラーログをTest Insightsで見やすくする
PHPStanのエラー内容をより見やすくする方法があります。それが CircleCI Test insightsを利用する方法です。
PHPStanを実行するコマンドを、次のように変更しましょう。
- run:
name: PHPStan
- command: vendor/bin/phpstan analyse --no-progress
+ command: |
+ mkdir -p phpstan-junit-results
+ vendor/bin/phpstan analyse --no-progress \
+ --error-format=junit > phpstan-junit-results/phpstan.xml
+ - store_test_results:
+ path: phpstan-junit-results
PHPStanのテスト実行結果をJUnit形式で出力するようにPHPStanの設定を変更しました。そしてその後にCircleCI上に保存するステップを追加し、CircleCIのTest Insightsで利用できるようにしています。
この状態でCIを実行すると、失敗したPHPStanジョブの[Tests]タブでエラー内容がチェックできるようになります。
また、パイプライン一覧ページでも問題の起きたファイルがリスト表示されます。これらを使ってCIが失敗した時のログ分析をより簡単に行えます。
Chunkを使って解析エラーを修正する
エラーの修正方法は様々です。ここではその1例として、AIを利用した修正方法を紹介します。
CircleCIはChunkという開発サイクルにおける検証と修正にフォーカスしたAIエージェントをベータ提供(2026/04現在)しています。
このAIを利用し、PHPStanが報告したエラーを修正するPRを作成させることができます。
失敗しているPHPStanのジョブを見ると、[Fix error]ボタンが表示されています。これをクリックすると、Chunkエージェントが起動し、エラーログを元に修正作業を開始します。
CIの実行結果もチェックした上でPRを作成してくれますので、ぜひお試しください。
PHPStan 単体の実行では使いませんが、PHPUnit など実行時に機密情報が必要になった場合は、CircleCI の Project Settings > Environment Variables で設定します。
ここで設定した値は OS レベルの環境変数としてジョブのコンテナに注入されます。Symfony の DotEnv コンポーネント は OS レベルの環境変数を .env で上書きしないため、.env のデフォルト値より優先されます。
まとめ
Symfony プロジェクトの作成から PHPStan の導入、CircleCI での自動実行までの手順を紹介しました。今回は PHPStan の実行のみを扱いましたが、同じ config.yml に PHPUnit のジョブを追加してワークフロー内で並列実行することも可能です。
- CircleCI PHP コンビニエンスイメージ — 利用可能なタグ一覧
- PHPStan 公式ドキュメント — ルール設定やベースライン機能の詳細
-
PHPStan Extension Library —
phpstan/extension-installerと各拡張の解説 -
Symfony Configuration — Environment Variables —
.envファイル運用の公式ガイド - CircleCI 環境変数の設定 — Context を使った環境変数の共有







