About this page
Laravel Package Development introduced how service providers and facades are automatically registered when you define theextra.laravel section in composer.json. This page explains what happens behind the scenes at the source-code level, focusing on the Illuminate\Foundation\PackageManifest class.
This page is a companion to Laravel Package Development. Read that guide first if you want to learn the basic usage of auto-discovery before the internals.
Auto-discovery flow
The PackageManifest class
The core of auto-discovery is Illuminate\Foundation\PackageManifest. The following is a summarized implementation as of the framework 13.x branch.
- The manifest is cached in memory after the first load through the
$this->manifestproperty. Even ifproviders()is called multiple times in one request, file I/O happens only once. build()runs only when the manifest file does not exist. In normal operation, the manifest is not rebuilt on every request.- The manifest itself is a plain PHP file that returns an array (
bootstrap/cache/packages.php), which is the fastest shape for loading it withrequire.
Building the manifest
Thebuild() method is where Laravel aggregates information from Composer metadata.
composer.json directly. Instead, it reads vendor/composer/installed.json. Composer generates this file during composer install and composer update, and it contains metadata for every installed package. Because each package’s extra section is consolidated there, Laravel reads only Composer-managed information.
installed.json is normally ignored by Git. Auto-discovery does not work during an initial setup before vendor/ exists; the cache can be built only after composer install finishes.Two places to define dont-discover
You can define dont-discover in either a package’s composer.json or the application’s composer.json, but the meaning differs.
Package composer.json
Application composer.json
build(), the $ignore array is accumulated by merging each package’s configuration['dont-discover']. In other words, a package can technically disable auto-discovery for one of its dependencies. That can be useful when a package internally uses a subpackage and wants to avoid duplicate provider registration. In day-to-day application development, however, dont-discover is most commonly used from the application side.
Setting dont-discover to *
When the array returned by packagesToIgnore() contains *, $ignoreAll becomes true, and auto-discovery is disabled for every package. This is useful when you want to avoid auto-discovery overhead in CI or tests, or when you want to manage providers completely manually in bootstrap/providers.php.
The cache file
getCachedPackagesPath() returns the value of the APP_PACKAGES_CACHE environment variable when it exists; otherwise, it returns bootstrap/cache/packages.php.
php artisan package:discover shows which packages were discovered.
When the cache is rebuilt
Illuminate\Foundation\ComposerScripts hooks into three Composer events, and all of them call the same clearCompiled() method.
composer install, composer update, and composer dump-autoload all delete the config cache, service cache, and package cache together. The next time Laravel boots, PackageManifest::build() runs and rebuilds the cache from the current installed.json.
This is the standard behavior of a Laravel project registered in the scripts section of composer.json.
Excerpt from a Laravel application's composer.json
Manual rebuilds with php artisan package:discover
If the cache is stale, or if vendor/ was changed directly without going through Composer, manually rebuild it with the package:discover command.
$manifest->build() and prints the result; there is almost no command-specific logic. If your CI pipeline runs composer install --no-scripts, Composer events will not fire, so you need to call this command explicitly.
Deployment notes
Production deployments commonly runcomposer install --no-dev --optimize-autoloader. If you also pass --no-scripts, the package cache will not be updated. Add an explicit rebuild step to the deployment script to stay safe.
package:discover before config:cache. If the config cache is created first, configuration registered by newly added packages, such as configuration merged with mergeConfigFrom, may not be reflected.
Summary
- Auto-discovery does not read static
composer.jsonfiles directly; it reads thevendor/composer/installed.jsonfile generated by Composer. - Results are cached as a plain PHP array in
bootstrap/cache/packages.phpand loaded quickly withrequire. - The cache is automatically deleted by
composer install,composer update, andcomposer dump-autoloadevents, then rebuilt on the next application boot. - Environments that run Composer with
--no-scriptsneed to explicitly callphp artisan package:discover. - Setting
dont-discoverto*disables auto-discovery entirely.
Related pages
- Laravel Package Development — Basic usage of service providers and
extra.laravel - Deferred service providers — Optimize when discovered providers are loaded
- Package testing with Orchestra Testbench — Testbench’s dedicated
package:discovercommand