Delegate containers
delegate() adds another PSR-11 container that the container asks for entries
it does not have itself. It is how a module that brings its own container, or a legacy container during a migration,
shares its entries without registering each of them again.
$container->delegate($legacyContainer);
$container->get(LegacyMailer::class); // from $legacyContainer, when nothing is registered for it here
When a delegate is asked
The container asks the delegates after its own registrations, and before attributes and autowiring:
- A scoped instance, an instance, or a binding registered for the identifier.
- The first delegate, in the order they were added, whose
has()returnstrue. - An attribute on the type, then autowiring.
A delegate is asked before autowiring because it can hold a configured instance of a class that autowiring would build
without its configuration. has() returns true for an identifier a delegate has.
A dependency of an autowired class is looked up the same way, so a class this container builds can receive an entry from a delegate.
What a delegate's entry is
What a delegate returns is its own value, which this container does not change or keep:
- it is not stored, so every
get()asks the delegate again, which decides whether it is shared; - extenders,
#[DecoratedBy]decorators, and callbacks do not run for it, because this container did not build it; make()never asks a delegate, because a PSR-11 container cannot build a new instance. For an identifier only a delegate has, it throws aResolutionException::notBuildable(), and for a class it builds a new instance itself.
Only its type is checked: for an identifier that names a class or interface, a value of another type throws a
ResolutionException::incompatibleType(), as it would for any other entry.
An exception the delegate throws while resolving the entry follows the same rules as
a factory that throws, and is wrapped in a
ResolutionException::delegateFailed() that names the delegate's class.
Two containers can delegate to each other. While the container asks its delegates for an identifier, a delegate that asks back for the same identifier is told this container does not have it, so the lookup ends instead of repeating.
After lock(), delegate() throws a
ContainerLockedException::cannotConfigure().