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
Basic Usage:
use League\Container\Container;
$container = new Container();
$container->add(MyService::class, MyService::class); // Register a concrete
$service = $container->get(MyService::class); // Resolve
First Use Case:
league/container for lightweight DI in a module or standalone script:
$container = new Container();
$container->add(\App\Services\UserService::class, \App\Services\UserService::class);
$userService = $container->get(\App\Services\UserService::class);
get(), has(), and set() methods.#[Inject] and #[Resolve] for dependency injection without manual registration.BeforeResolveEvent or ServiceResolvedEvent for custom logic.Manual Registration:
$container->add(
\App\Contracts\LoggerInterface::class,
\App\Services\FileLogger::class
);
Attribute-Based Resolution (Laravel-like auto-wiring):
#[Inject]
public function __construct(private UserRepository $users) {}
ReflectionContainer delegate:
$container->addDelegate(new \League\Container\ReflectionContainer());
Service Providers:
$container->addServiceProvider(new class implements \League\Container\ServiceProviderInterface {
public function provides(): array { return [UserRepository::class]; }
public function register(): void {
$this->container->add(UserRepository::class, UserRepository::class);
}
});
Shared vs. Non-Shared:
$container->addShared(MyService::class, MyService::class); // Singleton
$container->add(MyService::class, MyService::class); // New instance per request
Laravel Compatibility: Replace Laravel’s container in a module by binding to the same interfaces:
$container->add(\Illuminate\Contracts\Container\Container::class, $laravelContainer);
Use league/container for lightweight logic while leveraging Laravel’s ecosystem.
Event-Driven Extensions: Modify resolved services dynamically:
$container->listen(
\League\Container\Events\ServiceResolvedEvent::class,
fn($event) => $event->getService()->setConfig($event->getContainer()->get('config'))
);
Tagging for Collections:
$container->addTag(MyService::class, 'api');
$apiServices = $container->getTagged('api'); // Array of tagged services
Delegate Containers: Chain containers for modularity:
$delegate = new \League\Container\Container();
$delegate->add(\App\Services\Cache::class, \App\Services\RedisCache::class);
$container->addDelegate($delegate);
Circular Dependencies:
ContainerException with actionable guidance if a class has unresolved constructor dependencies.afterResolve().Overwriting Definitions:
$container->allowOverwrite(true);
Attribute Resolution Quirks:
string|int) are not auto-wired. Register manually or use #[Resolve] with explicit types.Event System Performance:
ReflectionContainer Caching:
$reflection = new \League\Container\ReflectionContainer();
$reflection->setCache(new \League\Container\Cache\FileCache(__DIR__.'/cache'));
$container->addDelegate($reflection);
$definition = $container->getDefinition(MyService::class);
var_dump($definition->getConcrete());
var_dump($container->getIds()); // All registered IDs
$container->setAutoResolve(false); // Disable auto-resolution of non-registered classes
Custom Argument Resolvers:
Override League\Container\Argument\ArgumentResolverInterface to handle custom types (e.g., DateTime from strings).
Definition Modifiers:
Use afterResolve() to post-process services:
$container->afterResolve(MyService::class, fn($service) => $service->configure());
Bootable Service Providers: Load providers eagerly:
$container->addServiceProvider(new class implements \League\Container\BootableServiceProviderInterface {
public function boot(): void { /* Setup */ }
public function register(): void { /* Bindings */ }
});
$container->boot();
Custom Exceptions:
Extend \League\Container\Exception\ContainerException for domain-specific errors.
bind() maps to add() in league/container. Use addShared() for singletons.afterResolve() or event listeners for dynamic binding.tag() maps to addTag() in league/container.ReflectionContainer for auto-wiring.ReflectionContainer for repeated resolutions.
```markdown
### Example: Migrating from Laravel to League/Container
```php
// Laravel-style binding
$container->add(
\App\Contracts\UserRepository::class,
\App\Repositories\EloquentUserRepository::class
);
// Laravel-style singleton
$container->addShared(
\App\Contracts\Cache::class,
\App\Services\RedisCache::class
);
// Laravel-style tagging
$container->addTag(\App\Services\ApiService::class, 'api');
// Laravel-style context binding (workaround)
$container->listen(
\League\Container\Events\BeforeResolveEvent::class,
fn($event) => match ($event->getId()) {
'user.repository' => $event->setConcrete(\App\Repositories\CacheUserRepository::class),
default => null,
}
);
How can I help you explore Laravel packages today?