skip to content

In a Laravel app, how do you stop package discovery from registering a particular package, and when would you want to?

level: middleimportance: should knowfreq 30%

answer

  1. the app's composer.json, not the package's
  2. extra.laravel.dont-discover
  3. package name, or * for all
  4. then register the provider yourself
  5. rebuild with package:discover

basics

~10 s

List the package's Composer name under extra.laravel.dont-discover in the app's composer.json (or * to disable discovery entirely), rebuild the manifest with package:discover, and register the provider yourself where and when you want it.

solid answer

~40 s

Add the package name to `"extra": {"laravel": {"dont-discover": ["vendor/package"]}}` in the **application's** `composer.json`; `["*"]` turns discovery off for every package. `PackageManifest::build()` reads that list and leaves those packages out of `bootstrap/cache/packages.php`, so run `php artisan package:discover` (or any Composer command that triggers it) afterwards. You then register the provider yourself, for example only in some environments from `AppServiceProvider::register()` with `$this->app->register(...)`, or unconditionally in `bootstrap/providers.php`. Typical reasons: a development-only tool that must never boot in production, a package whose provider you want to replace with your own subclass, or controlling order and visibility of what an app loads. A package's own `extra.laravel.dont-discover` can also exclude other packages.

code

json · 9 lines
json
{
    "extra": {
        "laravel": {
            "dont-discover": [
                "acme/debug-panel"
            ]
        }
    }
}

go deeper

for a junior

Know that the app's composer.json can list a package under extra.laravel.dont-discover to stop it registering automatically.

for a middle

Explain that the manifest must be rebuilt, that * disables all discovery, and how to register the provider manually or conditionally.

for a senior

Use opt-out deliberately: development tools, provider replacement, and a clear record of why each exclusion exists.

for a principal

Choose a team default: discovery on for convenience, or * with an explicit provider list for auditability across many apps.

## Where the switch lives Package discovery registers every installed package that declares providers under `extra.laravel`. The **consuming application** can opt out in its own `composer.json`: ```json "extra": { "laravel": { "dont-discover": ["acme/debug-panel"] } } ``` - Entries are **Composer package names** (`vendor/package`), not class names. - `"*"` disables discovery for **all** packages; the app then registers every package provider itself. - Nothing changes until the manifest is rebuilt: run `php artisan package:discover`, or let a Composer command that fires the skeleton's `post-autoload-dump` script do it. `PackageManifest::build()` collects the ignore list from the app's `composer.json`, adds any `dont-discover` entries declared by installed packages themselves, and skips those packages when it writes `bootstrap/cache/packages.php`. So a package can also exclude another package, for example one it replaces. ## How the list is read The manifest builder treats the list literally: each entry must match the package's Composer name exactly as it appears in `vendor/composer/installed.json`, such as `acme/debug-panel`. Provider class names, partial names and patterns other than the single `*` have no effect, and a misspelt name silently leaves discovery on for that package. Checking the rebuilt manifest is the fastest way to confirm an exclusion took effect. ## Why opt out | Reason | Example | |---|---| | Keep a development tool out of production | a local profiling panel that must never boot on a live server | | Replace a provider | register your own subclass of the package's provider with different bindings | | Control what boots | a large app that wants every provider listed in one place for review | | Temporary isolation | switch off a misbehaving package while investigating, without uninstalling it | For the first case, installing the tool as a development dependency already keeps it off production servers that install without dev packages. `dont-discover` adds control on machines where it is installed but should not always run. ## Registering the provider yourself After opting out, choose where registration happens: 1. **Always:** add the class to `bootstrap/providers.php`, the skeleton's provider list. 2. **Conditionally:** call `$this->app->register(DebugPanelServiceProvider::class)` from `AppServiceProvider::register()`, guarded by an environment check or a config flag. ```php public function register(): void { if ($this->app->environment('local')) { $this->app->register(\Acme\DebugPanel\DebugPanelServiceProvider::class); } } ``` Remember facade aliases too: an excluded package's `aliases` are skipped along with its providers, so add any alias you still need yourself or import the facade class directly. ## A shared audit-trail package Three internal apps use `acme/audit-trail`. One of them, a reporting app, needs a customised provider that binds a read-only recorder. That app lists `acme/audit-trail` under `dont-discover` and registers its own `ReportingAuditTrailServiceProvider`, which extends the package's provider and overrides one binding. The other two apps keep discovery on and never touch provider lists. ## Pitfalls - **Editing the wrong `composer.json`**: a change to a package's `composer.json` inside `vendor/` is not what the manifest reads and is lost on the next install; the consumer's opt-out belongs in the app's own file. - **Forgetting to rebuild the manifest**, so the old provider keeps loading from `bootstrap/cache/packages.php`. - **Using `*` casually**: every package provider, including ones the team never noticed, now has to be registered by hand, and a missed one fails at runtime. - **Excluding a package but keeping its facade alias usage** without registering the alias. ## Development-only packages A frequent interview follow-up concerns packages installed only for development. When production installs without development packages, those packages are absent, so a manifest rebuilt there does not list their providers; nothing needs excluding. The trap is a **stale manifest**: if `bootstrap/cache/packages.php` built on a machine with development packages is shipped to production, it still lists their providers, and booting fails with a class-not-found error because the classes are absent. Rebuilding the manifest where the app runs, which the skeleton's Composer scripts do automatically, avoids it. `dont-discover` is for the cases Composer's dependency split cannot express, such as a tool that is installed but should only run on some machines. ## Checking the result After a change, look at `bootstrap/cache/packages.php`: it is a plain PHP array keyed by package name. An excluded package should not appear in it. Running `php artisan package:discover` also prints each package it discovered.

  • After adding a package to dont-discover, its provider still boots. Why?
    The manifest in `bootstrap/cache/packages.php` was not rebuilt, so the old list is still used. Run `php artisan package:discover`, or a Composer command that triggers the skeleton's `post-autoload-dump` script, and confirm the package no longer appears in the manifest.
  • What does dont-discover set to * change for a team?
    Every package provider and facade alias must be registered by hand, usually in `bootstrap/providers.php`. It gives one reviewed list of what boots, at the cost of extra steps on every install and a runtime failure if a provider is forgotten.

saying these in an interview costs you the question

  • dont-discover takes provider class names rather than package names
  • Adding dont-discover takes effect without rebuilding the manifest
  • dont-discover also removes the package's classes from the autoloader
  • dont-discover uninstalls the package from vendor/
  • Excluded packages still get their facade aliases registered