【入門編】ComposerとPHP 8.3以降の『アトリビュート』を活かした自作プラグインによる静的解析の拡張 – ビルド・パッケージ管理ツール生産性向上バイブル

こんにちは!日々の開発、本当にお疲れ様です。

PHPのプロジェクトを進める中で、`composer install` を実行してライブラリを落としてくるのは、もう毎日のルーティンになっていますよね。でも、「Composerって、ただのzip解凍屋さんなんでしょ?」と思っていませんか?

実はそれ、もったいないアプローチです。
世界最高峰の開発環境を知る私から言わせれば、Composerは「PHPプロジェクトのライフサイクルそのものを司る最強のオーケストレータ」です。

特にPHP 8.3以降の現代において、ネイティブの強力な武器となった「アトリビュート(Attribute)」とComposerのプラグイン機構を組み合わせれば、単なるパッケージ管理の枠を大きく飛び越え、「自分だけの静的解析・自動コード検証エンジン」をIDEやCIの枠外で自作できるようになります。

「難しそう…」と思いましたか? 安心してついてきてください。
今回は、初心者の方でも直感的に理解できるように、PHP 8.3の最新機能をフル活用した「アトリビュート読み取り型・自作Composerプラグイン」の作り方を、一歩ずつ優しく、しかし妥協のない本物のアーキテクチャで解説していきます。

これをマスターすれば、チーム全員のコーディングミスや設定漏れが「ビルド(Composer実行)の瞬間」に自動で検知できるようになり、毎日の開発が劇的に楽になりますよ!

—

1. なぜ「Composerプラグイン × PHP 8 アトリビュート」なのか?

私たちが普段書くコードには、「このコントローラーのメソッドは必ず認可チェックを通していなければならない」「このDoma/Entityのプロパティは特定のバリデーションルールを満たすべきだ」といった、コードの構造だけでは静的解析しきれないビジネス上の制約が数多く存在します。

これらを毎回人力でコードレビューするのは限界があります。かといって、巨大な静的解析ツールにカスタムルールを追加するのは学習コストが高すぎます。

そこで登場するのが、Composerプラグインです。
Composerがパッケージの依存関係を解決・ロードする瞬間(Event Dispatcher機構)、私たちの自作プラグインを割り込ませ、PHP 8.3の反射(Reflection)APIを使ってソースコード上のアトリビュートをスキャンします。そして「ルール違反があれば、ビルド(Composerコマンド)をその場で安全にエラー終了させる」という仕組みを作ります。

このアプローチの最大のメリットは以下の通りです。

  • 追加の外部ツールが不要: 開発者は `composer install` や `composer update` を叩くだけで、勝手に静的解析が走る。
  • PHP 8の恩恵: 独自のアトリビュートクラスを作ることで、型安全かつ直感的にメタデータをコードに埋め込める。

—

2. 開発環境のセットアップ

まずは、今回の実験場となるプロジェクトディレクトリを作成しましょう。
今回は、「プロジェクト内の特定のクラスに、専用のアトリビュートが付与されているかを検証するプラグイン」を作ります。

ターミナルを開き、以下のコマンドを順に実行してください。

作業用ディレクトリを作成して移動
mkdir composer-attribute-guard
cd composer-attribute-guard

PHP 8.3以上がインストールされていることを確認
php -v

プロジェクトの `composer.json` の初期化

Composerプラグイン専用のパッケージとしてルートを設定します。

対話形式をスキップしてcomposer.jsonを生成
composer init –name=”my-org/attribute-guard-plugin” \
–description=”A custom Composer plugin to enforce PHP 8.3 attributes validation.” \
–type=”composer-plugin” \
–license=”MIT” \
–require=”php:>=8.3″

生成された `composer.json` を開いて、プラグインとしての心臓部である `extra` セキュリティ設定を追加します。ここが非常に重要です。

{
“name”: “my-org/attribute-guard-plugin”,
“description”: “A custom Composer plugin to enforce PHP 8.3 attributes validation.”,
“type”: “composer-plugin”,
“license”: “MIT”,
“require”: {
“php”: “>=8.3”,
“composer-plugin-api”: “^2.0”
},
“autoload”: {
“psr-4”: {
“MyOrg\\AttributeGuard\\”: “src/”
}
},
“extra”: {
“class”: “MyOrg\\AttributeGuard\\GuardPlugin”
}
}

【ここがポイント】
`extra.class` に指定したクラスが、Composerが起動した瞬間に最初に呼び出される「プラグインのエントリーポイント」となります。

—

3. PHP 8.3 アトリビュートとプラグインロジックの実装

それでは、プラグインの本体を実装していきましょう。
ディレクトリ構造を以下のように整えます。

composer-attribute-guard/
├── composer.json
└── src/
├── Attribute/
│ └── Audited.php # 検証用のアトリビュート定義
├── GuardPlugin.php # Composerプラグインのエントリーポイント
└── PluginSubscriber.php # イベントを検知して解析を実行するロジック

① アトリビュートの定義 (`src/Attribute/Audited.php`)

まずは、開発者がコードに付与するための目印となるアトリビュートを作ります。PHP 8.3ならではのスマートな記述です。

  • 解説: `readonly class`(PHP 8.2以降)を使用し、不変のメタデータを安全に保持します。`riskLevel` などのパラメータを型安全に渡すことができます。
  • —

    ② イベント購読クラスの実装 (`src/PluginSubscriber.php`)

    次に、Composerがパッケージのインストールやダンプオートロードを行った際(今回は `POST_AUTOLOAD_DUMP` イベントを利用します)に、実際にプロジェクト内のファイルを走査してアトリビュートをチェックするクラスを作ります。

    getIO();
    $io->write(“[AttributeGuard] Running static attribute validation…“);

    // デモ用として、プロジェクト内の特定の対象クラスをモックで検証
    // 実務ではここにロジックを書き、Iterator等で全PHPファイルを走査します
    $targetClasses = [
    // 例: \App\Controller\SecretController::class
    ];

    foreach ($targetClasses as $className) {
    if (!class_exists($className)) {
    continue;
    }

    $reflection = new ReflectionClass($className);
    $attributes = $reflection->getAttributes(Audited::class);

    if (empty($attributes)) {
    // ルール違反を検知した場合、例外を投げることでComposerの処理全体を安全に中断させる
    throw new RuntimeException(
    sprintf(“Architecture Violation: Class [%s] must have #[Audited] attribute!”, $className)
    );
    }

    // アトリビュートのインスタンスを取得して詳細を検証
    / @var Audited $instance /
    $instance = $attributes[0]->newInstance();
    $io->write(sprintf(” -> Checked: %s (Owner: %s, Risk: %s)”, $className, $instance->owner, $instance->riskLevel));
    }

    $io->write(“[AttributeGuard] All checks passed successfully!“);
    }
    }

    —

    ③ プラグインのエントリーポイント (`src/GuardPlugin.php`)

    Composerが認識するプラグインのメインクラスです。`PluginInterface` を実装します。

    ‘onPostAutoloadDump’,
    ];
    }

    public function onPostAutoloadDump(Event $event): void
    {
    // オートロード生成後に検証ロジックを呼び出す
    PluginSubscriber::enforceRules($event);
    }
    }

    —

    4. 精度高い HelloWorld 的な動作確認

    さあ、私たちが血潮を注いで書いたこのプラグインが実際にどう動くのか、ローカルでテストしてみましょう。

    Composerプラグインは、通常のローカルパッケージとして別のプロジェクトから読み込ませるか、開発元プロジェクトにパスリポジトリとして登録してテストするのが最も効率的です。

    ルートディレクトリにテスト用のアプリケーションコード(またはダミーファイル)を配置し、`composer.json` にローカルリポジトリの設定を追加します。

    動作確認用スクリプトの作成 (`tests/SampleService.php`)

    テスト実行!

    ターミナルで以下のコマンドを実行します。

    オートロードを再生成し、プラグインのフックを誘発する
    composer dump-autoload

    【期待される実行ログ】
    うまく設定されていれば、コンソールに以下のような美しい出力が現れるはずです。

    Generating autoload files
    Generated autoload files
    > MyOrg\AttributeGuard\GuardPlugin::onPostAutoloadDump
    [AttributeGuard] Running static attribute validation…
    -> Checked: App\Service\SampleService (Owner: DevOps Team, Risk: HIGH)
    [AttributeGuard] All checks passed successfully!

    もし、対象のクラスから `#[Audited]` アトリビュートを剥がして再度 `composer dump-autoload` を実行すると……?

    [AttributeGuard] Running static attribute validation…
    Script MyOrg\AttributeGuard\GuardPlugin::onPostAutoloadDump handling the post-autoload-dump event terminated with an exception:
    Architecture Violation: Class [App\Service\SampleService] must have #[Audited] attribute!

    このように、アトリビュートの付け忘れをビルド(Composer実行)の段階で強制的にブロックすることに成功しました!

    —

    5. 現場でこの知見を活かすために

    いかがでしたでしょうか?
    今回は「Composerプラグイン」という一見敷居が高く見える技術を、PHP 8.3のアトリビュートと組み合わせることで、「チームの設計規約を強制する強力なガードレール」へと昇華させる手法を解説しました。

    これをマスターすれば、CI/CDパイプラインに複雑な静的解析ツールを導入するまでもなく、開発者の手元(Local Environment)の `composer install` や `update` のステップで、リポジトリの品質を担保し続けることが可能です。

    「これを毎日どう活かすか?」——例えば、データベースのマイグレーションが必要なエンティティクラスにアトリビュートが付いているかチェックさせたり、APIのエンドポイントに対応する権限アトリビュートが必ず付与されているかをビルド時に静的検証させたりと、応用範囲は無限大です。

    あなたの開発環境を、一歩先の「知的なアーキテクチャ」へ。ぜひ今日の業務から取り入れてみてください。あなたのコードベースが、より美しく、より堅牢になることを確信しています。

    タイトルとURLをコピーしました