skip to content

In a Laravel package's service provider, how does optimizes() hook the package's own cache commands into php artisan optimize and optimize:clear?

level: middleimportance: nice to knowfreq 14%

answer

  1. a protected ServiceProvider method
  2. named arguments optimize: and clear:
  3. stored in static optimizeCommands
  4. key derived from the provider class
  5. call it inside runningInConsole()

basics

~10 s

Calling $this->optimizes(optimize: 'package:optimize', clear: 'package:clear-optimizations') in a provider's boot() adds those Artisan commands to the task lists that php artisan optimize and optimize:clear run after Laravel's own caches.

solid answer

~40 s

`optimizes(?string $optimize = null, ?string $clear = null, ?string $key = null)` is a protected method on `Illuminate\Support\ServiceProvider`. It stores the command names in the static arrays `ServiceProvider::$optimizeCommands` and `$optimizeClearCommands`, keyed by a short name derived from the provider class (for `AcmeRotaServiceProvider`, `acme-rota`) unless you pass `key`. `OptimizeCommand` spreads those arrays after its four built-in tasks, and `OptimizeClearCommand` does the same after its six. The docs call it from `boot()` inside `if ($this->app->runningInConsole())`. Because entries are keyed, `php artisan optimize --except=acme-rota` skips the package's step. It lets a package with its own compiled artifacts, such as generated manifests, take part in the standard deploy command instead of needing a separate line in every deploy script.

go deeper

for a junior

Recall that packages can add their own steps to php artisan optimize, so the deploy still needs only one command.

for a middle

Explain the optimizes() arguments, where the static command arrays are consumed, and how --except skips a package step.

for a senior

When writing a package with compiled artifacts, pair cache and clear commands so optimize:clear never leaves a stale file behind.

for a principal

Prefer packages that integrate with the standard optimize and reload hooks, keeping deploy scripts uniform across many applications.

## The problem it solves Laravel's `php artisan optimize` caches configuration, events, routes and views. Packages sometimes have their own expensive bootstrap work: scanning directories for components, compiling icon sets, building a manifest of discovered classes. Before a hook existed, each package documented its own command and every team had to remember to add it to their deploy script. `optimizes()` lets the package plug into the command teams already run. ## The API `Illuminate\Support\ServiceProvider` declares: ```php protected function optimizes(?string $optimize = null, ?string $clear = null, ?string $key = null) ``` - `optimize` — the Artisan command to run during `php artisan optimize`; - `clear` — the Artisan command to run during `php artisan optimize:clear`; - `key` — the label for this entry; when omitted it is derived from the provider class name. The method writes into two **static** arrays on the base provider class: - `ServiceProvider::$optimizeCommands[$key] = $optimize;` - `ServiceProvider::$optimizeClearCommands[$key] = $clear;` The default key comes from `getProviderKey()`: take the class basename, keep the part before `ServiceProvider`, convert it to kebab case and lower-case it. `AcmeRotaServiceProvider` becomes `acme-rota`. ## How the commands consume it `OptimizeCommand::getOptimizeTasks()` returns: 1. `config` → `config:cache` 2. `events` → `event:cache` 3. `routes` → `route:cache` 4. `views` → `view:cache` 5. `...ServiceProvider::$optimizeCommands` `OptimizeClearCommand::getOptimizeClearTasks()` does the same after `config:clear`, `cache:clear`, `clear-compiled`, `event:clear`, `route:clear` and `view:clear`. Each task runs through `callSilently()`, and each shows a `DONE` or `FAIL` line in the output. Because package entries are keyed like the built-in ones, `--except` works for them too: `php artisan optimize --except=acme-rota` skips the package's command, and so does passing the command name. ## Registering it The docs register it in `boot()` and only in the console, since the arrays are only read by Artisan commands: ```php <?php namespace Acme\Rota; use Illuminate\Support\ServiceProvider; class AcmeRotaServiceProvider extends ServiceProvider { public function boot(): void { if ($this->app->runningInConsole()) { $this->optimizes( optimize: 'rota:cache', clear: 'rota:clear', ); } } } ``` The package must also register `rota:cache` and `rota:clear` as Artisan commands; `optimizes()` only records their names. ## Design notes | Aspect | Behaviour | |---|---| | Scope | static arrays shared by all providers in the process | | Ordering | package commands run after Laravel's built-in tasks | | Skipping | `--except` matches the entry's key or command name | | Either side optional | pass only `optimize:` or only `clear:` if one is enough | A few practical points: - **Pair the two commands.** If `rota:cache` writes a file that is used whenever it exists, `rota:clear` must delete it, or `optimize:clear` will leave a stale artifact behind. - **Make the cache self-clearing.** Like Laravel's own `:cache` commands, a package's cache command should remove its previous output first, so `optimize` alone is enough on a deploy. - **The sibling hook for long-running services** is `reloads()`, used by `php artisan reload`; that belongs to deploy steps rather than to caching. ## When not to use it - **Cheap or lazy artifacts.** If the package already computes something quickly on demand, a cache step adds a file that can go stale for little gain. - **Environment-dependent output.** The command runs wherever `optimize` runs. If the artifact embeds environment values, it inherits the same risk as `config:cache` built in the wrong place, so document where it must run. - **Work that belongs to long-running processes.** Restarting workers or servers is not a cache; that is what `reloads()` and `php artisan reload` are for. ## In an application Application code can call `optimizes()` from its own `AppServiceProvider` too, for an app-specific manifest. The effect is the same: one `php artisan optimize` in the deploy builds everything.

  • What default key does optimizes() use for a provider named AcmeRotaServiceProvider, and why does the key matter?
    `getProviderKey()` takes the class basename before `ServiceProvider`, kebab-cases it and lower-cases it, giving `acme-rota`. The key indexes the static command arrays, so it is what `php artisan optimize --except=acme-rota` matches, and two providers with the same key would overwrite each other's entry unless one passes an explicit `key`.

saying these in an interview costs you the question

  • Packages must add their cache command to the app's deploy script by hand
  • optimizes() runs the package command immediately when the provider boots
  • Package optimize commands run before config:cache
  • optimizes() is a Composer script hook in composer.json