yiisoft/di
PSR-11 compatible dependency injection container for PHP 8.1+. Supports autowiring plus constructor, method and property injection, aliasing, service providers, delegated/composite containers, circular reference detection, and state reset for long-running workers.
Installation:
composer require yiisoft/di
Add to composer.json under autoload-dev if using for testing:
"yiisoft/di": "^1.0"
Basic Container Initialization:
use Yiisoft\Di\Container;
$container = new Container();
First Use Case: Define a service and resolve it:
// Define a service
$container->set('service', fn() => new MyService());
// Resolve and use
$service = $container->get('service');
$service->doSomething();
Where to Look First:
src/Container.php for core implementation details.Service Registration:
$container->set(MyService::class, fn() => new MyService());
$container->set(MyService::class, MyService::class);
$container->set(MySingleton::class, MySingleton::class, ['shared' => true]);
Dependency Resolution:
$container->set(MyService::class, fn($container) => new MyService($container->get(AnotherService::class)));
call):
$container->set('handler', fn($container) => [
'method' => 'handle',
'object' => $container->get(RequestHandler::class),
'params' => [$container->get(Request::class)],
]);
Configuration Injection:
config parameter in set():
$container->set(MyConfigurableService::class, MyConfigurableService::class, [
'config' => ['timeout' => 30, 'retries' => 3],
]);
Grouping Services:
$container->setGroups([
'auth' => [
UserRepository::class,
AuthService::class,
],
]);
Integration with Laravel:
$yiisoftContainer = new Container();
$yiisoftContainer->set('app', fn() => new App($yiisoftContainer->get('config')));
$container->set(FactoryService::class, fn($container) => new FactoryService(
$container->get(Database::class),
$container->get(Cache::class)
));
has() to check for service existence before resolving:
if ($container->has('optional-service')) {
$service = $container->get('optional-service');
}
$container->set(MyService::class, TestMyService::class, ['shared' => false]);
Circular Dependencies:
CircularReferenceException during resolution.$container->set(CircularService::class, fn($container) => new CircularService(
$container->get(OtherService::class) // Resolved lazily
));
Shared vs. Non-Shared Instances:
shared: true can lead to duplicate instances.false; explicitly set for singletons.Configuration Overrides:
set() is merged, not replaced. Use config: [] to clear:
$container->set(MyService::class, MyService::class, ['config' => []]);
PSR-11 Compliance:
make()) may not work with all PSR-11 consumers.get(), has(), and set().Namespace Collisions:
'app.services.user-repo') for clarity.$services = $container->getDefinitions();
dump($services);
$container->setDebug(true); // Logs resolution steps
ContainerException for missing or invalid services:
try {
$container->get('nonexistent-service');
} catch (ContainerException $e) {
Log::error($e->getMessage());
}
$container->set(LoggerInterface::class, MonologLogger::class);
$container->get(MyService::class); // Works if registered as class key
class AppContainer extends Container {
public function __construct() {
parent::__construct();
$this->setDefaults([
'app.name' => 'MyApp',
]);
}
}
shared: true for expensive-to-initialize services.$container->set(MyService::class, TestMyService::class);
ContainerInterface for mocking in unit tests.Definition class to add metadata or validation:
class CustomDefinition extends Definition {
public function __construct(array $config = []) {
parent::__construct($config);
$this->set('custom-meta', $config['meta'] ?? null);
}
}
Compiler to pre-process definitions (e.g., for Yii modules):
$compiler = new Compiler($container);
$compiler->compile();
Container::RESOLVING and Container::RESOLVED events for interception:
$container->on(Container::RESOLVING, fn($event) => {
if ($event->getService() === MyService::class) {
$event->setService(new InterceptedMyService());
}
});
How can I help you explore Laravel packages today?