laminas/laminas-di
Dependency injection container for Laminas apps. Supports autowiring, configuration-driven definitions, factories, and runtime instantiation to manage object creation and wiring with minimal boilerplate. Integrates with Laminas ServiceManager patterns and PSR-friendly practices.
Installation:
composer require laminas/laminas-di
Add to composer.json:
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
Basic Usage:
use Laminas\Di\Di;
use Laminas\Di\Injector;
$injector = new Injector();
$di = new Di($injector);
// Resolve a class with constructor dependencies
$service = $di->get('App\Service\MyService');
First Use Case: Replace manual instantiation of a service with autowired dependencies:
// Before (manual)
$userRepository = new UserRepository(new DatabaseConnection());
$userService = new UserService($userRepository);
// After (autowired)
$userService = $di->get(UserService::class);
src/Injector.php: Core autowiring logic.src/Di.php: PSR-11 container facade.src/ConfigProvider.php: Configuration utilities.// Service with constructor dependencies
class UserService {
public function __construct(
private UserRepository $repository,
private LoggerInterface $logger
) {}
}
// Resolve via DI container
$service = $di->get(UserService::class);
Injector::setTypePreference() to resolve interfaces vs. concrete classes.// Override a dependency for a specific class
$injector->setService(UserRepository::class, new CustomUserRepository());
$service = $di->get(UserService::class); // Uses CustomUserRepository
// Pass arguments during creation
$service = $di->get(UserService::class, [
'repository' => new CustomUserRepository(),
'logger' => new NullLogger()
]);
use Laminas\Di\Injector;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
public function register() {
$injector = new Injector();
$this->app->bind('laminas-di', function () use ($injector) {
return new Di($injector);
});
}
}
$di = app('laminas-di');
$service = $di->get(MyService::class);
use Laminas\Di\Generator\InjectorGenerator;
$generator = new InjectorGenerator();
$factoryCode = $generator->generate('App\Service\MyService');
file_put_contents('MyServiceFactory.php', $factoryCode);
$injector->setService(
UserService::class,
function (Injector $injector) {
return new UserService(
$injector->get(UserRepository::class),
config('app.debug') ? new DebugLogger() : new ProductionLogger()
);
}
);
$injector->setTypePreference(LoggerInterface::class, MonologLogger::class);
$service = $di->get(UserService::class); // Uses MonologLogger
$injector->setClassDefinitionCache(new FilesystemCache('/path/to/cache'));
$injector->preloadDefinitions([UserService::class, UserRepository::class]);
No Shared Instances by Default
get() call. To share instances, use a wrapper like Laminas\ServiceManager or manually cache resolved objects.$sharedService = $di->get(UserService::class);
$di->setService(UserService::class, $sharedService); // Force sharing
No Setter/Property Injection
$injector->setService(UserService::class, function (Injector $injector) {
$service = new UserService();
$service->setRepository($injector->get(UserRepository::class));
return $service;
});
PHP 8+ Type System Quirks
// Resolves to the first type in `int|string`
public function __construct(private int|string $id) {}
RuntimeException).Circular Dependencies
CircularDependencyException. Break cycles by:
PSR-11 Compliance
Di implements PSR\Container\ContainerInterface, it does not fully comply with PSR\Container\ContainerInterface::get() requirements (e.g., throws exceptions on missing services instead of returning null).Laminas\ServiceManager for strict PSR-11 compliance.Enable Verbose Logging
$injector->setDebug(true);
$di->get(UserService::class); // Logs resolution steps
Inspect Class Definitions
$definition = $injector->getClassDefinition(UserService::class);
print_r($definition->getParameters());
Common Errors & Fixes
| Error | Cause | Solution |
|---|---|---|
ClassNotFoundException |
Missing class or namespace | Check autoloading (composer dump-autoload). |
CircularDependencyException |
Circular dependency | Refactor to break the cycle. |
InvalidArgumentException |
Unsupported type hint | Use supported types (no variadic, etc.). |
RuntimeException |
Factory return type mismatch | Ensure factory returns the expected type. |
Custom Parameter Resolvers
$injector->addParameterResolver(function ($parameter, $classDefinition) {
if ($parameter->getType() === 'config') {
return config($parameter->getName());
}
return null;
});
Post-Resolution Callbacks
$injector->addPostResolver(UserService::class, function ($instance) {
$instance->initialize();
});
Integrate with Laravel Events
use Illuminate\Support\Facades\Event;
Event::listen('laminas-di.resolved', function ($service, $class) {
logger()->debug("Resolved {$class}: " . get_class($service));
});
Generate Custom Factories
Extend Laminas\Di\Generator\InjectorGenerator to create domain-specific factory logic.
Service Provider Binding
$this->app->singleton('laminas-di', function () {
$injector = new Injector();
$injector->setTypePreference(LoggerInterface::class, MonologLogger::class);
return new Di($injector);
});
Conflict with Laravel’s Container
// Bad: Resolves via Laravel's container first, then Laminas-DI
$di = app('laminas-di')->get(UserService::class);
// Good: Use Laminas-DI exclusively for a namespace
$di = app('laminas-di');
$service = $di->get(UserService::class);
Configuration Integration
config/di.php:
$injector = new Injector();
$injector->configure(config
How can I help you explore Laravel packages today?