In a Laravel service provider, what belongs in register() and what belongs in boot(), and why does the order matter?
answer
- two passes over every provider
- register: container bindings only
- boot: after all providers registered
- boot() parameters are injected
- $bindings and $singletons properties
basics
~20 sregister() should only bind services into the container; boot() configures the app with them, such as macros, view composers, gates and event listeners. Laravel runs every provider's register() before any boot(), so boot() can rely on every registered service.
solid answer
~40 sLaravel loads providers in two passes. First it calls `register()` on each provider in list order and then applies that provider's `$bindings` and `$singletons` properties; at that point later providers have not registered, so their services may not be bound. Once every provider is registered, the application calls each provider's `boot()` through the container, so `boot()` can type-hint dependencies and rely on every binding existing. That is why the documentation says `register()` should only bind things into the container, while event listeners, routes, macros, view composers, gates and model-wide settings belong in `boot()`. The skeleton's `App\Providers\AppServiceProvider` ships with both methods empty, ready for that split.
code
php · 20 lines<?php
namespace App\Providers;
use App\Services\NewsletterRenderer;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(NewsletterRenderer::class);
}
public function boot(): void
{
Gate::define('publish-issue', fn ($user) => $user->is_editor);
}
}go deeper
Recall the rule: bindings go in register(), everything that uses services goes in boot(), and every register() runs before any boot().
Explain the two passes, when $bindings and $singletons are applied, and why boot() can type-hint dependencies while the constructor cannot.
Spot register() code that resolves services or has side effects, and explain the order-dependent failures and duplicate instances it causes.
Decide how a large app splits providers by module so each one's register() stays pure and its boot() stays small and discoverable.
## What a service provider is A **service provider** is a class extending `Illuminate\Support\ServiceProvider` that tells Laravel how to set up part of the application. The framework's own features (database, mail, queue, views) each come from providers, packages ship their own, and your app starts with one: `App\Providers\AppServiceProvider`, listed in `bootstrap/providers.php`. New ones come from `php artisan make:provider`. A provider has two main methods, `register()` and `boot()`, and the whole point is **when** each runs. ## The two passes At startup, after environment variables and configuration are loaded, the application works through its provider list like this: 1. **Register pass.** For each provider in order: create it with `new Provider($app)`, call `register()`, then bind every entry of its `$bindings` property and register every entry of its `$singletons` property. 2. **Boot pass**, which begins only after the register pass has finished: - run the application's `booting` callbacks; - for each provider: its own `booting` callbacks, then `boot()` called through the container, then its own `booted` callbacks; - run the application's `booted` callbacks. During the register pass, a provider further down the list has not run yet. During the boot pass, **every** provider has registered, so every binding exists. ## What goes where | Task | Method | |---|---| | `bind()`, `singleton()`, `scoped()`, `instance()`, contextual `when()` rules | `register()` | | Registering another provider conditionally with `$this->app->register()` | `register()` | | `View::composer()`, Blade directives and components | `boot()` | | `Event::listen()`, model observers | `boot()` | | `Gate::define()`, `Gate::policy()` | `boot()` | | Macros on `Response`, `Str`, `Collection` and friends | `boot()` | | `Model::preventLazyLoading()` and other model-wide settings | `boot()` | ## The $bindings and $singletons properties For simple mappings, a provider can declare them instead of writing `register()` code: ```php public $bindings = [ShippingQuote::class => FlatRateQuote::class]; public $singletons = [NewsletterRenderer::class, SubscriberCache::class => RedisSubscriberCache::class]; ``` - They are applied **after** `register()` returns, so an entry here overrides a binding for the same abstract made inside `register()`. - In `$singletons`, an entry without a key registers the class as a singleton of itself; `$bindings` has no such shorthand. ## Boot method injection Providers are instantiated with `new`, so their constructor only receives the application. `boot()` is different: the application invokes it with the container's `call()`, so any type-hinted parameter is resolved: ```php public function boot(ResponseFactory $response): void { $response->macro('csv', fn (string $body) => $response->make($body, 200, ['Content-Type' => 'text/csv'])); } ``` ## Why the rule exists - **Order independence.** Code in `register()` that uses a service can fail or silently get the wrong object if the provider that binds it comes later in the list. - **Cheap registration.** Registration should only record recipes; building objects there wastes time on every request and can freeze an instance before other providers have extended or replaced it. - **Deferral.** A provider that only binds can later be made deferred; one that does real work in `register()` cannot. - **Predictable boot.** Everything that configures behaviour runs in one well-defined phase, after the container is complete. ## Registering providers at run time A provider can register another provider with `$this->app->register(OtherProvider::class)`. This is how development-only tools are kept out of production: `AppServiceProvider::register()` checks the environment and registers the tool's provider only locally. The call registers the provider immediately; if the application has already booted, it also boots it straight away, so a late registration still ends up fully set up. Registering the same class twice returns the existing instance instead of running it again. ## A quick self-check for any provider - Does `register()` contain anything other than bindings, contextual rules or `$this->app->register()`? Move it to `boot()`. - Does `register()` call `make()`, `app()` or a facade? That is resolution, not registration. - Does `boot()` build services that only some requests need? Consider binding them lazily instead. - Could this provider be deferred? Only if it registers bindings and nothing else. - Is the provider listed in `bootstrap/providers.php`, or registered from another provider?
- In a Laravel provider, what does an entry without a key in the $singletons array do?An integer key means the value is used as both abstract and concrete, so `public $singletons = [NewsletterRenderer::class];` registers that class as a singleton of itself. `$bindings` has no such shorthand; each entry must map an abstract to a concrete.
- In Laravel, why can a service provider's boot() type-hint services while its constructor cannot?Providers are created with `new $provider($app)`, so the constructor receives only the application. `boot()` is invoked through the container's `call()`, which resolves each type-hinted parameter, and by then every provider has registered, so the services exist.
register() is every supplier delivering stock to the shop's back room; boot() is opening the doors once all deliveries are in. A shop that starts selling while deliveries are still arriving will find some shelves empty.
saying these in an interview costs you the question
- At startup each provider's boot() runs right after its own register(), before the next provider registers.
- Event listeners and routes should be registered in register() so they exist early.
- The container injects type-hinted services into a service provider's constructor.
- Services bound by a provider later in the list are available inside register().
- In Laravel 13, AppServiceProvider must be listed in config/app.php's providers array.