In Laravel's container, what do beforeResolving(), resolving() and afterResolving() callbacks do, and why does a resolving() callback fire only once for a singleton?
answer
- hooks on the container, not the class
- beforeResolving: abstract, parameters, container
- resolving, then afterResolving: object, container
- matched by type name or instanceof
- cached singleton returns before callbacks fire
basics
~20 sresolving() and afterResolving() run callbacks on each newly built object, matched by type; beforeResolving() runs before any build. A cached singleton is returned before the resolving callbacks fire, so they run only at its first build.
solid answer
~40 s`beforeResolving($abstract, $callback)` fires at the start of every resolution of that type, cached or not, with the abstract name, the parameter array and the container. `resolving($abstract, $callback)` and `afterResolving($abstract, $callback)` fire after an object is built, with the object and the container, `resolving` callbacks first; passing only a closure registers a global hook. Type-specific hooks match when the abstract equals the registered type or the object is an `instanceof` it, so a hook on an interface catches every implementation. Because `make()` returns a cached shared instance before firing these callbacks, a `resolving()` hook on a singleton runs once per application instance. Laravel's form requests rely on the pair: a `resolving(FormRequest::class)` hook fills the request with the current input, then an `afterResolving(ValidatesWhenResolved::class)` hook validates it.
code
php · 19 lines<?php
namespace App\Providers;
use App\Contracts\ClockAware;
use App\Support\SystemClock;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
// Runs for every newly built object implementing ClockAware.
$this->app->resolving(ClockAware::class, function (ClockAware $object, Application $app) {
$object->setClock($app->make(SystemClock::class));
});
}
}go deeper
Recall that resolving() lets you run code on an object when the container builds it, and that form request validation runs through such a hook.
Explain the firing order, type matching by instanceof, and why a cached singleton skips the resolving callbacks after its first build.
Diagnose hooks registered too late, keep global hooks cheap, and choose between resolving(), extend() and constructor injection for configuring services.
Limit how much behaviour hides in container hooks, since they act at a distance, and require that each hook be discoverable from the class it affects.
## Three hooks around resolution Laravel's container lets you run code **around** the moment it produces an object, without changing the class or its binding. The hooks are registered on the container, usually in a service provider: | Hook | Fires | Callback receives | |---|---|---| | `beforeResolving($abstract, $cb)` | at the start of every resolution of that type | the abstract, the parameter array, the container | | `resolving($abstract, $cb)` | after a new object is built | the object, the container | | `afterResolving($abstract, $cb)` | right after the `resolving` callbacks | the object, the container | | `afterResolvingAttribute($attr, $cb)` | after building something carrying that attribute | the attribute instance, the object, the container | Passing a closure alone, `resolving(fn ($object, $app) => ...)`, registers a **global** hook that runs for every object the container builds. ## Where they fire inside make() For each resolution the container follows this order: 1. Fire **`beforeResolving`** callbacks for the abstract. 2. Check for a contextual binding. 3. If a **cached shared instance** exists and no parameters or contextual binding apply, **return it now**. 4. Build the object: closure, auto-wiring or aliased concrete. 5. Apply `extend()` decorators. 6. Cache the object if the binding is shared. 7. Fire global and type-specific **`resolving`** callbacks, then **`afterResolving`** callbacks. Step 3 returns before step 7, which is why a `resolving()` hook on a singleton runs **once**: only the first resolution builds the object. `beforeResolving` sits before the cache check and runs on every call. ## How type matching works - A type-specific `resolving` or `afterResolving` hook fires when the resolved abstract equals the registered type **or** the object is an `instanceof` it. A hook on an interface therefore catches every implementation, whatever name it was resolved by. - A `beforeResolving` hook fires when the abstract equals the registered type or is a subclass of it; there is no object yet to test. - Global hooks fire for everything, so they run on every build in the application and should stay cheap. ## How Laravel itself uses them - **Form requests.** `FormRequestServiceProvider::boot()` registers `resolving(FormRequest::class, ...)`, which copies the current HTTP request's data into the form request, and `afterResolving(ValidatesWhenResolved::class, ...)`, which calls `validateResolved()`. The ordering guarantees the data is in place before validation runs. - **Exception handler.** `withExceptions()` in `bootstrap/app.php` hooks `afterResolving` on the handler class so your callback configures it when it is first built. - **Scheduler.** `withSchedule()` hooks `afterResolving(Schedule::class, ...)` to register your scheduled tasks. - **Packages.** `ServiceProvider::callAfterResolving()` registers an `afterResolving` hook and, if the service is already resolved, calls it immediately, which fixes the "hook registered too late" problem. ## Traps - A hook registered **after** a singleton was built never fires for it; use the `callAfterResolving()` pattern or register earlier. - `resolving` mutates the object it is handed; it cannot replace it. Replacing or wrapping a service is `extend()`'s job. - Hooks do not run for objects created with `new`. - Heavy work in a global hook multiplies across every resolution in the request. ## Choosing a hook - Use **`resolving`** for the main configuration of a freshly built object, such as calling setters or registering it somewhere. - Use **`afterResolving`** for work that must see the object after every `resolving` callback has run, as form request validation does. - Use **`beforeResolving`** when you only need to know that a type is being asked for, for example to load something lazily or record the request; it runs even for cached singletons. - Use **`afterResolvingAttribute`** to act on classes or parameters marked with your own attribute, without listing the classes anywhere. - Prefer plain **constructor injection** when the object's own class can simply declare what it needs; hooks are for cross-cutting configuration that the class should not know about.
- In Laravel, when would you use a resolving() hook instead of extend()?Use `resolving()` to configure the object the container built, such as calling a setter, because the callback's return value is ignored. Use `extend()` when you need to replace or wrap the object, because its closure's return value is what consumers receive.
- In a Laravel package provider, why use callAfterResolving() rather than calling $this->app->afterResolving() directly?If the service was already resolved before the provider ran, a plain `afterResolving()` hook would never fire for it. `callAfterResolving()` registers the hook and, when the service is already resolved, calls the callback immediately with the existing instance, so the configuration is applied either way.
saying these in an interview costs you the question
- A resolving() callback runs every time a singleton is fetched from the container.
- Returning a new object from a resolving() callback replaces the resolved service.
- A resolving() hook on an interface fires only when that exact interface name is resolved.
- beforeResolving() callbacks receive the built object.
- Container hooks also run for objects created with new.