league/container
PSR-11–compliant dependency injection container from The PHP League. Register services, factories and shared instances, then resolve dependencies with autowiring support. Modern PHP (8.3+) with full docs, tests, and MIT license.
Here we will attempt to provide as clear a guide as possible for upgrading to the latest version of the package.
If you notice anything missing from this page, please create an issue, or pull request.
PHP 8.3+ is now required. PHP 8.1 and 8.2 have reached end of life and are no longer supported.
The entire inflector subsystem has been removed. Container::inflector() was deprecated in 5.2 and is now gone, along with InflectorInterface, InflectorAggregate, and InflectorAggregateInterface.
The inflector() method has also been removed from DefinitionContainerInterface. If you have custom implementations of this interface, remove the inflector() method.
Use Container::afterResolve() as a drop-in replacement:
// Before (5.x)
$container->inflector(LoggerAwareInterface::class, fn($obj) => $obj->setLogger($logger));
// After (6.x)
$container->afterResolve(LoggerAwareInterface::class, fn($obj) => $obj->setLogger($logger));
For method invocation and property setting patterns, the callback form covers all use cases:
// Before (5.x) - method invocation
$container->inflector(LoggerAwareInterface::class)
->invokeMethod('setLogger', [Logger::class]);
// After (6.x)
$container->afterResolve(LoggerAwareInterface::class, function (object $service) use ($container) {
$service->setLogger($container->get(Logger::class));
});
// Before (5.x) - property setting
$container->inflector(DatabaseAwareInterface::class)
->setProperty('connection', Database::class);
// After (6.x)
$container->afterResolve(DatabaseAwareInterface::class, function (object $service) use ($container) {
$service->connection = $container->get(Database::class);
});
Note: there is no direct event system equivalent for the setProperty() inflector pattern. Use constructor injection or the callback form shown above.
See the events documentation for the full event API.
The Container constructor no longer accepts an InflectorAggregateInterface parameter. If you were passing arguments positionally to the constructor, update your call:
// Before (5.x)
$container = new Container($definitions, $providers, $inflectors);
// After (6.x)
$container = new Container($definitions, $providers);
ContainerAwareInterface::setContainer() now returns static instead of ContainerAwareInterface. If you have custom implementations, update the return type:
// Before (5.x)
public function setContainer(DefinitionContainerInterface $container): ContainerAwareInterface
// After (6.x)
public function setContainer(DefinitionContainerInterface $container): static
Several new features have been added in 6.0:
addContextualArgument() on definitions.NotFoundException, runtime circular dependency detection, and resolution chain reporting in nested failures.getDefinitionIds() and getServiceProviderIds() on the Container class, plus a --dump flag on the compile CLI.#[Shared] attribute for declaring singleton intent at the class level when auto-wired via ReflectionContainer.DefinitionInterface has two new methods in 6.0:
addContextualArgument(string $abstract, string|object $concrete): DefinitionInterfacegetContextualArguments(): arrayIf you have custom implementations of DefinitionInterface, add these methods.
ServiceProviderInterface has a new method in 6.0:
getProvidedIds(): arrayAbstractServiceProvider provides a default implementation returning []. Override this method if you want your provider's services to appear in Container::getServiceProviderIds().
The project coding standard has changed from PSR-12 to PER Coding Style 2.0. Contributors should run composer test:style to check compliance or vendor/bin/php-cs-fixer fix to auto-fix.
Tests have migrated from PHPUnit to Pest v4. Run tests with composer test:unit or vendor/bin/pest.
A new event system replaces inflectors. The event system hooks into the container lifecycle at four points: definition registration, pre-resolution, post-definition-resolution, and post-service-resolution.
Container::inflector() now triggers E_USER_DEPRECATED. Use Container::afterResolve() as a drop-in replacement:
// Before
$container->inflector(LoggerAwareInterface::class, fn($obj) => $obj->setLogger($logger));
// After
$container->afterResolve(LoggerAwareInterface::class, fn($obj) => $obj->setLogger($logger));
For method invocation and property setting patterns, the callback form covers all use cases:
// Before
$container->inflector(LoggerAwareInterface::class)
->invokeMethod('setLogger', [Logger::class]);
// After
$container->afterResolve(LoggerAwareInterface::class, function (object $service) use ($container) {
$service->setLogger($container->get(Logger::class));
});
DefinitionInterface now includes getTags(): array. If you have custom implementations of DefinitionInterface, add this method.
EventAwareContainerInterface has been removed. DefinitionContainerInterface no longer extends it. The event system is available on the concrete Container class via EventAwareTrait. If you were type-hinting against EventAwareContainerInterface, use the concrete Container class instead.
Shared definitions (registered via addShared()) now automatically receive a 'shared' tag. This means $definition->hasTag('shared') returns true for shared definitions. If you were using a custom 'shared' tag, this may conflict.
See the events documentation for the full API.
PHP 7.2+ is now required.
Container::add no longer accepts a 3rd argument $shared. To define a shared item, use Container::addShared.
// old
$container->add($id, $concrete, true);
// new
$container->addShared($id, $concrete);
If a container is set to be shared by default, to define a non-shared item, use Definition::setShared(false).
$container->defaultToShared();
// old
$container->add($id, $concrete, false);
// new
$container->add($id, $concrete)->setShared(false);
The convenience method Container::share has been replaced with Container::addShared.
// old
$container->share($id, $concrete);
// new
$container->addShared($id, $concrete);
Container::get no longer accepts a 2nd argument $new. To force resolution of a new item, use Container::getNew.
// old
$container->get($id, true);
// new
$container->getNew($id);
ServiceProviderInterface methods now declare a return type of void.
// old
public function boot()
{
// ...
}
public function register()
{
// ...
}
// new
public function boot(): void
{
// ...
}
public function register(): void
{
// ...
}
The distinction between AbstractServiceProvider::getContainer and AbstractServiceProvider::getLeagueContainer has been removed. Always just use AbstractServiceProvider::getContainer.
public function register(): void
{
// old
$this->getLeagueContainer()->add($id, $concrete);
// new
$this->getContainer()->add($id, $concrete);
}
Service providers are now expected to implement bool ServiceProviderInterface::provides rather than defining a array $provides property.
class MyServiceProvider extends League\Container\ServiceProvider\AbstractServiceProvider
{
// old
protected $provides = [
MyService::class,
AnotherService::class,
];
// new example, should return true if the service provider provides
// a service for the given alias $id
public function provides(string $id): bool
{
$services = [
MyService::class,
AnotherService::class,
];
return in_array($id, $services);
}
}
Any use of ClassName/ClassNameInterface or RawArgument/RawArgumentInterface should be removed in favour of ResolvableArgument and LiteralArgument.
// old
$container->add('string', new League\Container\Argument\RawArgument('a string'));
// new
$container->add('string', new League\Container\Argument\Literal\StringArgument('a string'));
See full documentation to determine the best changes for you.
PHP 7.2+ is now required.
Container::add no longer accepts a 3rd argument $shared. To define a shared item, use Container::addShared.
// old
$container->add($id, $concrete, true);
// new
$container->addShared($id, $concrete);
The convenience method Container::share has been replaced with Container::addShared.
// old
$container->share($id, $concrete);
// new
$container->addShared($id, $concrete);
Container::call has been removed, use ReflectionContainer::call instead.
// old
$container->call($callable, $args);
// new
(new League\Container\ReflectionContainer())->call($callable, $args);
Container::hasInDelegate has been removed, use Container::has, or specifically, DelegateContainer::has if you need to determine where the service is coming from.
$delegateContainer = new DelegateContainer();
$container->delegate($delegateContainer);
// old
$container->hasInDelegate($id);
// new
$container->has($id); // checks main container and then all delegates
$delegateContainer->has($id); // check specifically in the delegate
Container::get no longer accepts a 2nd argument $args with an array of runtime arguments to pass to the service on resolution.
Arguments should be defined at the same time the service is defined. If you are relying on defining arguments at runtime, you are likely using service location, this is not what the container is designed for and you shouldn't.
If you absolutely need to do this, a better way would be to define your service with no arguments, extend it, and add the arguments to the definition.
// old
$container->get($id, $args);
// new
$container->extend($id)->addArguments($args);
$container->get($id);
Be aware though that these arguments will be used for subsequent resolutions of that service, if you need to resolve the service multiple times with different arguments, either define multiple aliases for the same service, or first extend the service, and clone it before adding the arguments.
$originalDefinition = $container->extend($id);
$clonedDefinition = clone $originalDefinition;
$clonedDefinition->addArguments($args);
$clonedDefinition->resolve();
All withX methods have been replaced with addX to better describe the behaviour.
// old
$container->add($id, $concrete)->withArgument($arg);
$container->add($id, $concrete)->withArguments($args);
$container->add($id, $concrete)->withMethodCall('method', [$args]);
$container->add($id, $concrete)->withMethodCalls(['method' => [$args]]);
// new
$container->add($id, $concrete)->addArgument($arg);
$container->add($id, $concrete)->addArguments($args);
$container->add($id, $concrete)->addMethodCall('method', [$args]);
$container->add($id, $concrete)->addMethodCalls(['method' => [$args]]);
ServiceProviderInterface methods now declare a return type of void.
// old
public function boot()
{
// ...
}
public function register()
{
// ...
}
// new
public function boot(): void
{
// ...
}
public function register(): void
{
// ...
}
The distinction between AbstractServiceProvider::getContainer and AbstractServiceProvider::getLeagueContainer has been removed. Always just use AbstractServiceProvider::getContainer.
public function register(): void
{
// old
$this->getLeagueContainer()->add($id, $concrete);
// new
$this->getContainer()->add($id, $concrete);
}
Service providers are now expected to implement bool ServiceProviderInterface::provides rather than defining a array $provides property.
class MyServiceProvider extends League\Container\ServiceProvider\AbstractServiceProvider
{
// old
protected $provides = [
MyService::class,
AnotherService::class,
];
// new example, should return true if the service provider provides
// a service for the given alias $id
public function provides(string $id): bool
{
$services = [
MyService::class,
AnotherService::class,
];
return in_array($id, $services);
}
}
Sub-type definition classes CallableDefinition and ClassDefinition have been removed and are now just League\Container\Definition\Definition, this will only require action if you build definitions manually or have created your own definition classes that extend these.
How can I help you explore Laravel packages today?