Skip to main content
When a package adds functionality to a Laravel service, you often want to avoid creating the instance until it is actually used, while still applying your configuration if it has already been created. The service provider’s callAfterResolving() is a protected method that combines these two behaviors. This page examines the implementation in Laravel Framework v13.35.0 and digs deeper into extending services from boot(), as introduced in Laravel package development. Note that this is not a hook that waits for the entire application to finish booting; it is a hook on the resolution of a specific service.

Wait if unresolved, run now if already resolved

ServiceProvider::callAfterResolving() works in two steps:
  1. It registers the callback with the container’s afterResolving().
  2. If resolved() returns true, it retrieves the service with make() and also invokes the callback immediately.
If the service has not been resolved yet, the method itself does not call make() on the target. During normal resolution, the container builds the object, applies extenders, calls the resolving callbacks, and then calls the afterResolving callbacks. When retrieving a singleton normally, the container returns the stored instance early, so afterResolving does not fire on every retrieval. The key point is that simply registering afterResolving() misses configuration for singletons created before the registration.

Add a rule to the validation factory

As an example of providing a package-specific string rule, register the rule after validator is resolved. The official ValidationServiceProvider registers this key as a singleton, and the provider itself supports deferred loading.
The consuming application uses it alongside regular rules.
The extend() in this example is the rule registration API of Illuminate\Validation\Factory. It is a different method from the container’s extend() described later. Regular custom rules may not run for empty values, so specify that the field is required with required. For details on designing the rule itself, see Custom validation rules. Factory::extend() overwrites the array entry with the same rule name, so registering the same logic again, as in this example, does not add more entries. However, to avoid overwriting rules from other packages, prefix the rule names you expose with a package-specific prefix.
The callback runs when the factory is resolved. The rule’s validation closure runs later, when the Validator validates the target value. Input values are not validated when the hook is registered.

Match the target key with the resolved check

For the container’s normal resolution events, callbacks are selected not only when they match the registered key but also when they match the type of the retrieved object. On the other hand, resolved($name), which callAfterResolving() uses for the immediate check, looks at the alias-normalized key to see whether it has a resolved flag or a stored instance. It does not search through all objects that have been created. Therefore, just because an interface or class has been resolved does not mean that resolved() will also return true for a different key. Check the actual binding and aliases of the target service, and test the already-resolved case with the same key. The example above specifies validator, which is registered by the official provider. Also note that resolved() includes the condition “has been resolved in the past.” It does not mean that the current instance is guaranteed to still exist.

Not an API that guarantees “run only once”

callAfterResolving() leaves the callback registered. Even after it runs immediately, the registered callback runs again whenever the object is newly resolved in the future. In addition, calling the method again adds another callback. Take particular care when a regular non-shared binding has already been resolved. In the implementation we examined, the following happens:
  1. A new callback is registered.
  2. Because resolved() is true, make() is called.
  3. make() creates a new object, and the callback runs during its resolution event.
  4. The immediate callback also runs on the same object returned from make().
On this path, the newly registered callback is applied to the same object twice. Do not reuse a design that assumes only singleton retrieval for regular bindings as-is.
Do not send emails, call external APIs, charge payments, or unconditionally add listeners inside the callback. Limit its use to applying configuration to the service idempotently, and design it so that side effects are not duplicated by repeated registration or resolution.

Use a different API to replace objects

The return value of an afterResolving callback is not used to replace the object returned by the container. If you want to return a decorator and swap out the service itself, consider the container’s extend(). The closure for this API is contractually required to return the modified service. Similarly, callAfterResolving() is not a mechanism for updating every dependency that has already been stored in other objects. If your goal is to update dependencies when a binding is rebound, check rebinding() in the official documentation and the design of the target class. For the requirements of providers that truly defer loading services, see DeferrableProvider. Using a hook and making the provider that registers the hook itself deferred are two separate things.

Combinations to check when updating

Before releasing a package, verify not only the normal boot order but also the case where another provider uses the service first. If you record “used once across the entire application” with a static flag, configuration may be missed for services rebuilt in a new container or in tests. Ensure idempotency at the level of the target object or the registered key whenever possible.

Overriding and updating package views

See an example of how loadViewsFrom() uses a resolution hook to register a view namespace.

Package translation overrides and updates

Review how Translator namespaces are registered and how already loaded translations are handled.

Primary sources consulted

Last modified on October 10, 2026