0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Symfony アプリの PHPStan を CircleCI で自動実行する

0
Posted at

PHPアプリの構文に問題がないかやバグの予兆・コーディングルールの違反がないかなどをチェックしたい場合、PHPStanなどの静的解析ツールを利用します。

この記事では、Symfonyで作られたアプリにPHPStanを導入し、CircleCIで自動実行する方法を紹介します。

本記事の手順は以下の環境で検証しています。

  • Symfony 7.4 / PHP 8.3
  • PHPStan 2.1

Symfony プロジェクトのセットアップ

Symfony と PHPStan を動かすには PHP 本体に加えて複数の拡張(intlzip など)が必要です。ホスト 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 と必要最小限の拡張を入れています。

Dockerfile.dev
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 キャッシュを永続化します。

docker-compose.yml
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.neonphpstan-symfonyextension.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.jsonextra.symfony.allow-contrib: true が書き込まれ、以降このプロジェクトでは確認なしに contrib レシピが実行されるようになります。

インストールが完了すると、Symfony Flex のレシピにより phpstan.dist.neon がプロジェクトルートに自動生成されます。

phpstan.dist.neon
parameters:
    level: 6
    paths:
        - bin/
        - config/
        - public/
        - src/
        - tests/

このうち、- test/の行だけ削除してください。これは今回の記事ではテストファイルを作成しないため、PHPStan実行時に「ディレクトリが見つからない」というエラーが出ないようにするためです。

phpstan.dist.neon
parameters:
    level: 6
    paths:
        - bin/
        - config/
        - public/
        - src/
-        - tests/

PHPUnit などを導入してテストコードを書き始めたら、pathstests を追加してください。

これで静的解析の準備が完了しました。

ローカルで 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を次のように変更します。

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 の解析が成功すると、以下のように全ステップが緑色で表示されます。

CircleCI の PHPStan ジョブ成功画面

PHPStan がエラーを検出した場合は「PHPStan」ステップが赤色に変わり、ジョブが失敗します。

スクリーンショット 2026-04-23 14.54.15.png

エラーの詳細は該当ステップのログで確認できます。

スクリーンショット 2026-04-23 14.57.54.png

エラーログをTest Insightsで見やすくする

PHPStanのエラー内容をより見やすくする方法があります。それが CircleCI Test insightsを利用する方法です。

PHPStanを実行するコマンドを、次のように変更しましょう。

.circleci/config.yml
      - 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]タブでエラー内容がチェックできるようになります。

スクリーンショット 2026-04-23 15.03.12.png

また、パイプライン一覧ページでも問題の起きたファイルがリスト表示されます。これらを使ってCIが失敗した時のログ分析をより簡単に行えます。

スクリーンショット 2026-04-23 15.04.00.png

Chunkを使って解析エラーを修正する

エラーの修正方法は様々です。ここではその1例として、AIを利用した修正方法を紹介します。

CircleCIはChunkという開発サイクルにおける検証と修正にフォーカスしたAIエージェントをベータ提供(2026/04現在)しています。

このAIを利用し、PHPStanが報告したエラーを修正するPRを作成させることができます。

失敗しているPHPStanのジョブを見ると、[Fix error]ボタンが表示されています。これをクリックすると、Chunkエージェントが起動し、エラーログを元に修正作業を開始します。

スクリーンショット 2026-04-23 15.07.27.png

CIの実行結果もチェックした上でPRを作成してくれますので、ぜひお試しください。

スクリーンショット 2026-04-23 15.20.39.png

PHPStan 単体の実行では使いませんが、PHPUnit など実行時に機密情報が必要になった場合は、CircleCI の Project Settings > Environment Variables で設定します。

CircleCI Project Settings の環境変数設定画面

ここで設定した値は OS レベルの環境変数としてジョブのコンテナに注入されます。Symfony の DotEnv コンポーネント は OS レベルの環境変数を .env で上書きしないため、.env のデフォルト値より優先されます。

まとめ

Symfony プロジェクトの作成から PHPStan の導入、CircleCI での自動実行までの手順を紹介しました。今回は PHPStan の実行のみを扱いましたが、同じ config.yml に PHPUnit のジョブを追加してワークフロー内で並列実行することも可能です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?