skip to content

In Laravel, when do the application's booting() and booted() callbacks and a provider's own booted() callback run, and what are they used for?

level: middleimportance: nice to knowfreq 15%

answer

  1. app booting: before any provider boots
  2. provider booting/booted wrap its boot()
  3. app booted: after every provider booted
  4. a late booted() callback runs at once
  5. routes load in a provider booted callback

basics

~10 s

$app->booting() callbacks run before any provider's boot(); a provider's own booting() and booted() callbacks wrap that provider's boot(); $app->booted() callbacks run after every provider has booted, or immediately if added after boot.

solid answer

~40 s

The application's boot pass runs in a fixed order: all `$app->booting()` callbacks, then for each provider its own `booting()` callbacks, its `boot()` and its own `booted()` callbacks, then all `$app->booted()` callbacks. Application callbacks receive the application; provider callbacks are invoked through the container's `call()`, so they can type-hint services. `$app->booted()` is also safe to call late: if the app has already booted, the callback runs immediately, whereas a `booting()` callback added after boot has started never runs. Laravel uses these hooks itself: `withRouting()` registers the route provider in an app `booting` callback, that provider loads your route files in its own `booted` callback, and `withBroadcasting()` registers broadcast routes in an app `booted` callback. In `bootstrap/app.php`, the builder's `booting()`, `booted()` and `registered()` methods forward to the same hooks.

code

php · 20 lines
php
<?php

namespace App\Providers;

use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;

class NewsletterServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Runs after every provider, including the route provider, has booted.
        $this->app->booted(function ($app) {
            $count = count($app->make(Router::class)->getRoutes());

            Log::debug("Newsletter routes checked: {$count} routes registered.");
        });
    }
}

go deeper

for a junior

Recall that booted() callbacks run after the app has started, and that most setup still belongs in a provider's boot().

for a middle

Explain the full order: app booting, each provider's booting, boot and booted, then app booted, and which of them runs late registrations.

for a senior

Use the hooks to fix ordering bugs, such as code that needs routes or another provider's configuration, without depending on provider order.

for a principal

Keep startup ordering explicit and reviewable, limiting callback chains so new engineers can predict what runs when.

## Two levels of callbacks Laravel offers boot-phase hooks at two levels: - **Application level**: `$app->booting($cb)`, `$app->booted($cb)` and `$app->registered($cb)`, also reachable from `bootstrap/app.php` as `->booting()`, `->booted()` and `->registered()` on the application builder. - **Provider level**: `$this->booting($cb)` and `$this->booted($cb)` inside a service provider, attached to that provider only. They exist so code can run at a precise point relative to the rest of the startup, without depending on provider order. ## The exact order 1. Every configured provider's `register()` runs; then all **`registered`** callbacks fire. 2. `Application::boot()` fires all **app `booting`** callbacks. 3. For each registered provider, in order: 1. that provider's **`booting`** callbacks; 2. its `boot()` method, called through the container; 3. its **`booted`** callbacks. 4. The application marks itself booted and fires all **app `booted`** callbacks. | Hook | Runs | Receives | |---|---|---| | `$app->registered()` | after all configured providers registered | the application | | `$app->booting()` | before any provider boots | the application | | provider `booting()` | just before that provider's `boot()` | injected parameters | | provider `booted()` | just after that provider's `boot()` | injected parameters | | `$app->booted()` | after every provider has booted | the application | ## Late registration - `$app->booted()` checks whether the application has already booted; if so, it runs the callback **immediately**. Code can therefore call it at any time and be sure it runs once the app is ready. - `$app->booting()` has no such check. A callback added after the boot pass has started is stored but **never called**. - A provider registered after boot, for example a deferred provider loaded on demand, is booted straight away, including its own `booting` and `booted` callbacks. ## How the framework uses them - **Routing.** `withRouting()` in `bootstrap/app.php` registers the application's route provider from an app `booting` callback, so it is added after the configured providers and boots last. Without a route cache, that provider loads `routes/web.php` and the other route files in its own `booted` callback, after your `AppServiceProvider::boot()` has run, so route macros, patterns and model bindings defined there are ready in time. An app `booted` callback then refreshes the route name and action lookups. - **Broadcasting.** `withBroadcasting()` registers the broadcast auth routes and requires `routes/channels.php` in an app `booted` callback. - **JSON preference.** `prefersJsonResponses()` prepends its middleware to the HTTP kernel in an app `booted` callback. ## When to reach for them - Use a provider's plain **`boot()`** for almost everything; the callbacks are for ordering problems. - Use **`$app->booted()`** for work that must see the result of every provider's boot, such as inspecting registered routes or adjusting something another provider configured. - Use a provider's own **`booted()`** when a provider must finish its own setup first, as the route provider does. - Use **`$app->booting()`** to register something that must itself take part in the boot pass, typically another provider. - Avoid chains of callbacks that depend on one another's order; if two pieces of setup depend on each other, put them in the same method. ## Common mistakes - **Using `$app->booting()` from `boot()`.** The booting callbacks have already fired, so the callback silently never runs; `$app->booted()` is the late-safe choice. - **Expecting a provider's `booted()` to see every provider.** It runs right after that one provider's `boot()`, while later providers have not booted yet. - **Assuming routes exist in `boot()`.** Route files load after the configured providers boot, so code in `AppServiceProvider::boot()` that inspects routes does not yet see the application's own routes; move it to `$app->booted()`. - **Heavy work in callbacks.** Like `boot()` itself, every callback runs on every request and every command, so expensive work belongs in lazily resolved services. - **Forgetting cached routes.** With `route:cache`, route files are not loaded at all; the cached file is required in an app `booted` callback instead.

  • In Laravel, what happens to a callback passed to $app->booting() from inside a provider's boot() method?
    It is stored but never called. The application fires its booting callbacks once, before the first provider boots, and `booting()` has no check for an application that is already booting or booted. Use `$app->booted()` instead, which runs late callbacks immediately.
  • In Laravel, why can routes/web.php use a Route::pattern() defined in AppServiceProvider::boot()?
    The route provider that `withRouting()` registers is added in an app `booting` callback, after the configured providers, so it boots after `AppServiceProvider`. Without a route cache, it loads the route files in its own `booted` callback, by which point `AppServiceProvider::boot()` has already defined the pattern.

saying these in an interview costs you the question

  • $app->booted() callbacks added after boot are silently ignored.
  • A provider's booted() callback runs only after every provider has booted.
  • $app->booting() callbacks run between register() and boot() of each provider.
  • Route files load before AppServiceProvider::boot(), so route macros must go in register().
  • Provider-level booted() callbacks cannot receive injected services.