静的解析
PHPStan は、コードを実行せずにバグを見つけます。未定義のメソッドの 呼び出し、間違った型の引数、戻り値の不足などを検出できます。テスト を補うもので、 テストが少ない古いアプリケーションでは特に役立ちます。
CakePHP 2 は多くのオブジェクトを動的に作成します。コントローラーではモデルを $this->Post、 コンポーネントを $this->Auth として使え、ビヘイビアのメソッドをモデルから呼び出せます。 PHPStan だけではこれらを認識できません。 pieceofcake2/phpstan-cakephp2 拡張を使うと、PHPStan が CakePHP 2 の仕組みを理解できるようになります。
インストール
PHPStan と拡張を開発用の依存パッケージとしてインストールします。
composer require --dev phpstan/phpstan pieceofcake2/phpstan-cakephp2設定
PHPStan はプロジェクトルートの phpstan.neon から設定を読み込みます。設定では、拡張、 ルールレベル、解析するディレクトリ、ブートストラップファイルを指定します。 ブートストラップファイルは、webroot/index.php と同じように CakePHP の定数を定義して フレームワークを読み込みます。
モダン構成
pieceofcake2/app スケルトンの設定は次のとおりです。
# phpstan.neon
includes:
- vendor/pieceofcake2/phpstan-cakephp2/extension.neon
parameters:
level: 5
phpVersion: 80000
paths:
- src
- config
bootstrapFiles:
- tests/phpstan-bootstrap.phpphpVersion には、アプリケーションがサポートする最も古い PHP のバージョンを指定します (80000 は PHP 8.0 です)。ブートストラップファイルは、リクエストのディスパッチやデータベースへの 接続を行わずに、config/define.php と CakePHP のブートストラップを読み込みます。
<?php
// tests/phpstan-bootstrap.php
use Composer\InstalledVersions;
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
require_once dirname(__DIR__) . DS . 'config' . DS . 'define.php';
require_once VENDORS . 'autoload.php';
require_once InstalledVersions::getInstallPath('pieceofcake2/cakephp') . DS . 'src' . DS . 'Cake' . DS . 'bootstrap.php';レガシー構成
レガシーな app/ 構成のアプリケーションでは、app/ 内のディレクトリを解析し、 ブートストラップファイルでパスの定数を定義します。
# phpstan.neon
includes:
- vendor/pieceofcake2/phpstan-cakephp2/extension.neon
parameters:
level: 5
phpVersion: 80000
paths:
- app/Config
- app/Console/Command
- app/Controller
- app/Lib
- app/Model
- app/View
excludePaths:
- app/Config/Schema
bootstrapFiles:
- app/Test/phpstan-bootstrap.php<?php
// app/Test/phpstan-bootstrap.php
use Composer\InstalledVersions;
define('DS', DIRECTORY_SEPARATOR);
define('ROOT', dirname(__DIR__, 2));
define('APP_DIR', 'app');
define('WEBROOT_DIR', 'webroot');
define('WWW_ROOT', ROOT . DS . APP_DIR . DS . WEBROOT_DIR . DS);
require_once InstalledVersions::getInstallPath('pieceofcake2/cakephp') . '/src/Cake/bootstrap.php';アプリケーションに無いディレクトリは paths から削除してください。
プラグイン
プラグインは、プラグイン自身のディレクトリから、テスト用のブートストラップファイルを使って 解析します。paths にプラグインのディレクトリを並べ、プラグインのビヘイビアを behaviorPaths に追加します (ビヘイビアとスキーマファイル を参照してください)。
# phpstan.neon
includes:
- vendor/pieceofcake2/phpstan-cakephp2/extension.neon
parameters:
level: 5
phpVersion: 80000
paths:
- Console
- Controller
- Lib
- Model
- View
bootstrapFiles:
- tests/bootstrap.php
ModelBehaviorsExtension:
behaviorPaths:
- Model/Behavior/*.phpPHPStan の実行
vendor/bin/phpstan analysePHPStan は、エラーごとにファイルと行を表示します。CI のジョブでは、--no-progress を付けると ログが短くなります。
CI のジョブサマリー
GitHub Actions では、pieceofcake2/.github の ci-summary アクションを使うと、結果を ジョブサマリー として実行結果のページに表示できます。エラーの数と、エラーごとのファイル、行、識別子、メッセージの表が 表示されます。エラーを checkstyle 形式(または JSON 形式)で書き出し、そのファイルをアクションに渡します。
- name: Run PHPStan
run: vendor/bin/phpstan analyse --no-progress --error-format=checkstyle > phpstan-report.xml
- name: PHPStan summary
if: ${{ !cancelled() }}
uses: pieceofcake2/.github/actions/ci-summary@v1
with:
type: phpstan
file: phpstan-report.xml同じアクションで、PHP_CodeSniffer のレポートと PHPUnit の JUnit レポート(必要ならカバレッジも)を まとめることもできます。E2E テストでは、アップロードした失敗時の スクリーンショットへのリンクも表示します。ジョブ全体の例は e2e-test のドキュメントの CI の節 を参照してください。アクションの実行にはランナーに PHP が必要です。アクションがジョブを失敗させることは ありません。
拡張が追加する機能
拡張は PHPStan に以下の機能を追加します。
- ビヘイビアのメソッドを、すべてのモデルのメソッドとして扱います (
$this->Post->behaviorMethod())。 - コントローラー、モデル、シェルの、モデルクラスと同じ名前のプロパティを、そのクラスの インスタンスとして扱います (コントローラーの
$this->Post)。 - コントローラーとコンポーネントの、
$componentsに指定したコンポーネントと同じ名前の プロパティを、そのクラスのインスタンスとして扱います ($this->Auth)。 - シェルの、タスククラスと同じ名前のプロパティを、そのクラスのインスタンスとして扱います。
ClassRegistry::init('Post')が、可能な場合はPostを返し、スキーマファイルにテーブルだけが 定義されている場合はModelを返すものとして扱います。$this->Components->load('Name')がNameComponentを返すものとして扱います。
それ以外のマジックプロパティには、CakePHP のコアと同じようにクラスに @property タグを 追加します。たとえば、ほかのヘルパーを使うヘルパーでは次のようにします。
// src/View/Helper/LinkHelper.php
/**
* @property HtmlHelper $Html
*/
class LinkHelper extends AppHelper
{
public $helpers = ['Html'];
public function home(): string
{
return $this->Html->link('Home', '/');
}
}ビヘイビアとスキーマファイル
拡張は、PHPStan を実行するディレクトリからの相対的な glob パターンで、ビヘイビアと スキーマファイルを探します。デフォルトでは、コアのビヘイビア、モダン構成の src/Model/Behavior、plugins/*/Model/Behavior、config/Schema、レガシー構成の app/Model/Behavior、app/Plugin/*/Model/Behavior、app/Config/Schema が対象です。 ほかの場所は phpstan.neon に追加します。追加した場所はデフォルトにマージされます。
parameters:
ModelBehaviorsExtension:
behaviorPaths:
- vendor/my-vendor/my-plugin/src/Model/Behavior/*.php
SchemaService:
schemaPaths:
- vendor/my-vendor/my-plugin/config/Schema/*.phpレベルの選び方
PHPStan には 0 (最も緩い) から 10 までの ルールレベル があります。各レベルは、 前のレベルにチェックを追加します。たとえば、レベル 0 は未知のクラスやメソッドを報告し、 レベル 3 は戻り値の型を、レベル 5 はメソッドや関数に渡す引数の型をチェックし、 レベル 6 はあらゆる場所に型宣言を求めます。
CakePHP 2 のアプリケーションでは、レベル 5 を目標にするのがよいでしょう。実際のバグを見つけられ、 必要な修正も小さく済みます。pieceofcake2/app スケルトンと メンテナンスしているプラグイン は、CI で PHPStan を レベル 5 で実行しています。レベル 6 以上では主に型宣言や配列の形の指定が求められますが、 CakePHP 2 のコードにはそれらがほとんどありません。
既存のアプリケーションでは、レベル 0 から始めてエラーを修正し、レベルを 1 つずつ上げていきます。 一度に修正しきれないほどエラーが多い場合は、現在のエラーを ベースライン に記録すると、新しいエラーだけが 報告されるようになります。
vendor/bin/phpstan analyse --generate-baseline生成された phpstan-baseline.neon を phpstan.neon の includes に追加し、記録された エラーを少しずつ修正していきます。
