league/container
league/container is a lightweight PSR-11 dependency injection container for PHP. Define entries, factories, and autowiring-friendly services to manage application dependencies cleanly, with modern PHP support and solid tooling for testing and analysis.
Installation:
composer require league/container
Requires PHP 8.3+ (as per latest release).
Basic Setup:
use League\Container\Container;
$container = new Container();
First Use Case: Register a service and resolve it:
$container->add(\App\Services\MyService::class);
$service = $container->get(\App\Services\MyService::class);
Constructor Injection:
$container->add(\App\Services\UserService::class)
->addArgument(\App\Repositories\UserRepository::class);
Alias-Based Resolution (for interfaces/contracts):
$container->add(\App\Contracts\UserRepository::class, \App\Repositories\MySqlUserRepository::class);
$container->add(\App\Services\UserService::class)
->addArgument(\App\Contracts\UserRepository::class);
Service Providers (for modular registration):
$container->addServiceProvider(new \App\Providers\AppServiceProvider());
Define a provider with register() method to batch-add services.
Delegate Containers (fallback resolution):
$fallback = new \League\Container\Container();
$container->delegate($fallback);
$container = new \League\Container\Container();
$container->addServiceProvider(new \Illuminate\Foundation\ApplicationServiceProvider(app()));
$container->add(\App\Services\PaymentService::class, new \App\Mocks\MockPaymentService());
$container->add(\App\Services\CacheService::class, fn() => new \App\Services\RedisCacheService());
Circular Dependencies:
League’s container throws League\Container\Exception\NotFoundException for unresolvable services. Debug by checking registration order or using ->has() to verify availability.
Argument Resolution Overhead:
Avoid ambiguous strings (e.g., "User"). Use explicit types or aliases:
// Bad: Ambiguous
$container->add(\App\Services\UserService::class)->addArgument("User");
// Good: Explicit
$container->add(\App\Services\UserService::class)->addArgument(\App\Models\User::class);
Service Provider Lifecycle:
Providers are registered but not automatically invoked. Call register() manually or use addServiceProvider() with a provider that auto-registers.
Closure Caching:
Closure-based services (e.g., ->add(\App\Service::class, fn() => new \App\Service())) are not cached by default. Use ->share() to enable singleton behavior:
$container->share(\App\Services\ConfigService::class, fn() => new \App\Services\ConfigService());
if (!$container->has(\App\Service::class)) {
throw new \RuntimeException("Service not registered!");
}
->getArguments() to see what’s being injected:
$definition = $container->getDefinition(\App\Service::class);
print_r($definition->getArguments());
->getDelegates(). Delegates are checked in registration order.Custom Argument Types:
Extend League\Container\Argument\ArgumentInterface for domain-specific logic (e.g., environment variables):
$container->addArgument(new \App\Arguments\EnvVarArgument('APP_DEBUG'));
Inflectors: Modify resolved objects post-instantiation:
$container->addInflector(new \League\Container\Inflector\Inflector(
fn($obj) => $obj->setLogger($container->get(\Psr\Log\LoggerInterface::class))
));
Event System:
Listen for League\Container\Events\Building to intercept resolution:
$container->on('building', fn($event) => $event->setValue(new \App\Mocks\MockService()));
How can I help you explore Laravel packages today?