このページについて
パッケージ開発の基礎ではcomposer.json の extra.laravel セクションを書けばサービスプロバイダーとファサードが自動登録されることを紹介しました。このページではその裏側、Illuminate\Foundation\PackageManifest クラスがどのように動作しているかをソースコードレベルで解説します。
このページはパッケージ開発の基礎の姉妹ページです。自動検出の基本的な使い方を先に読むことをおすすめします。
自動検出の仕組み全体像
PackageManifest クラス
自動検出の中核は Illuminate\Foundation\PackageManifest です。以下がフレームワーク13.x時点の実装(要約)です。
- マニフェストは一度読み込むとメモリにキャッシュされる(
$this->manifestプロパティ)。1リクエスト内で複数回providers()を呼んでもファイルI/Oは1回だけ。 - マニフェストファイルが存在しない場合のみ
build()が実行される。通常運用では毎回ビルドされるわけではない。 - 実体は素朴なPHP配列を
returnするファイル(bootstrap/cache/packages.php)で、requireするだけで読み込める最速の形式。
マニフェストのビルド処理
build() メソッドが実際に composer.json の情報を集約する部分です。
composer.json を直接パースするのではなく vendor/composer/installed.json を読んでいることです。これはComposerが composer install / composer update 実行時に生成する、インストール済み全パッケージのメタデータファイルです。各パッケージの composer.json に書かれた extra セクションがここに集約されているため、Laravel側はComposerの管理下にある情報だけを信頼して読み取ります。
installed.json は通常 .gitignore されているため、初回セットアップ時(vendor/ がない状態)では自動検出は機能しません。composer install の完了後に初めてキャッシュが構築されます。dont-discover の2つの書き方
dont-discover はパッケージ側・アプリケーション側どちらの composer.json にも書けますが、意味が異なります。
パッケージ側のcomposer.json
アプリケーション側のcomposer.json
build() の実装を見ると、$ignore 配列は各パッケージの configuration['dont-discover'] を array_merge して蓄積しています。つまりパッケージ自身が「自分の依存パッケージの自動検出を無効化する」ことも技術的には可能です(例:内部で使っているサブパッケージのプロバイダーを重複登録させたくない場合)。ただし実務でよく使うのはアプリケーション側での無効化です。
dont-discover に * を指定する
packagesToIgnore() が返す配列に * が含まれていると $ignoreAll = true となり、すべてのパッケージの自動検出を丸ごと無効化します。CIやテスト環境で自動検出のオーバーヘッドを避けたい場合や、bootstrap/providers.php で完全に手動管理したい場合に使います。
キャッシュファイルの実体
getCachedPackagesPath() は環境変数 APP_PACKAGES_CACHE があればそれを、なければ bootstrap/cache/packages.php を返します。
return しているだけだとわかります。
php artisan package:discover の出力で「どのパッケージが検出されたか」を確認できます。
キャッシュが再構築されるタイミング
Illuminate\Foundation\ComposerScripts は3つのComposerイベントにフックしており、いずれも同じ clearCompiled() を呼びます。
composer install / composer update / composer dump-autoload のいずれを実行しても、設定キャッシュ・サービスキャッシュ・パッケージキャッシュがまとめて削除されます。次にLaravelが起動したタイミングで PackageManifest::build() が走り、installed.json の最新状態から再構築されます。
これは composer.json の scripts に登録されている、標準的なLaravelプロジェクトの挙動です。
Laravelアプリのcomposer.json(抜粋)
php artisan package:discover で手動再構築
キャッシュが古いままになっている、あるいはComposerを経由せずに vendor/ を直接変更したような場合は、package:discover コマンドで手動再構築できます。
$manifest->build() を呼んで結果を出力するだけで、コマンド固有のロジックはほぼありません。CIパイプラインで composer install --no-scripts を使っている場合など、Composerイベントが発火しない状況ではこのコマンドを明示的に呼ぶ必要があります。
デプロイ時の注意点
本番デプロイではcomposer install --no-dev --optimize-autoloader を実行するのが一般的ですが、--no-scripts を付けている場合はパッケージキャッシュが更新されません。デプロイスクリプトに明示的な再構築ステップを入れておくと安全です。
package:discover は config:cache より前に実行してください。設定キャッシュが先に作られると、後から追加されたパッケージ設定(mergeConfigFrom で登録される設定など)が反映されない場合があります。
まとめ
- 自動検出は
composer.jsonの静的な内容ではなく、Composerが生成するvendor/composer/installed.jsonを読み取って動作する。 - 結果は
bootstrap/cache/packages.phpにプレーンなPHP配列としてキャッシュされ、requireだけで高速に読み込まれる。 - キャッシュは
composer install/update/dump-autoloadの各イベントで自動的に削除され、次回起動時に再構築される。 --no-scriptsでComposerを実行する環境ではphp artisan package:discoverを明示的に呼ぶ必要がある。dont-discoverに*を指定すると自動検出全体を無効化できる。
関連ページ
- パッケージ開発の基礎 — サービスプロバイダーと
extra.laravelの基本的な使い方 - 遅延サービスプロバイダー — 検出されたプロバイダーの読み込みタイミングを最適化する
- Orchestra Testbenchでのパッケージテスト — Testbench独自の
package:discoverコマンド