laminas/laminas-servicemanager
Powerful dependency injection and service container for PHP. Manage factories, abstract factories, delegators, aliases, and shared services, with PSR-11 interoperability and robust configuration for complex applications.
Installation:
composer require laminas/laminas-servicemanager
Ensure friendsofphp/proxy-manager-lts is installed for lazy services:
composer require friendsofphp/proxy-manager-lts
Basic Setup:
Create a ServiceManager instance with a minimal configuration:
use Laminas\ServiceManager\ServiceManager;
$serviceManager = new ServiceManager([
'factories' => [
'MyService' => \Laminas\ServiceManager\Factory\InvokableFactory::class,
],
]);
First Use Case: Retrieve a service:
$myService = $serviceManager->get('MyService');
$myService->doSomething();
Service Registration:
factories for explicit service-to-factory mappings (best for performance and clarity):
'factories' => [
MyService::class => MyServiceFactory::class,
],
abstract_factories for dynamic discovery (convenience over performance):
'abstract_factories' => [
\Laminas\ServiceManager\Factory\ReflectionBasedAbstractFactory::class,
],
Plugin Managers:
AbstractPluginManager for homogeneous services (e.g., validators, filters):
class MyPluginManager extends AbstractPluginManager {
protected $instanceOf = MyInterface::class;
}
ServiceManager:
'factories' => [
MyPluginManager::class => function($container) {
return new MyPluginManager($container, [
'factories' => [
MyService::class => InvokableFactory::class,
],
]);
},
],
Lazy Services:
'delegators' => [
ExpensiveService::class => [
\Laminas\ServiceManager\Proxy\LazyServiceFactory::class,
],
],
'lazy_services' => [
'class_map' => [
ExpensiveService::class => ExpensiveService::class,
],
],
Delegators:
'delegators' => [
MyService::class => [
\Laminas\ServiceManager\Delegator\ServiceDelegatorFactory::class,
\App\Delegator\LoggingDelegator::class,
],
],
Laravel Integration:
Leverage Laravel’s built-in ServiceProvider and bind()/singleton() methods to register services with laminas-servicemanager:
public function register()
{
$this->app->singleton(MyService::class, function ($app) {
return new MyService($app->make(Dependency::class));
});
}
For plugin managers, use Laravel’s extend() or macro() to integrate with Laravel’s container:
$this->app->extend('validator', function ($validator) {
return new MyPluginManager($this->app, $validator->getConfig());
});
Configuration:
Use Laravel’s config() helper to load service configurations from config/services.php:
$serviceManager = new ServiceManager(config('services'));
Testing:
Mock the ServiceManager in tests:
$mockServiceManager = $this->createMock(ServiceManager::class);
$mockServiceManager->method('get')->willReturn(new MyService());
Circular Dependencies:
LazyServiceFactory or Proxy\LazyServiceFactory to defer initialization:
'delegators' => [
ServiceA::class => [LazyServiceFactory::class],
ServiceB::class => [LazyServiceFactory::class],
],
Plugin Manager Validation:
$instanceOf or override validate() in a plugin manager can lead to runtime errors when invalid services are injected:
// Wrong: No validation
class MyPluginManager extends AbstractPluginManager {}
// Correct: Enforce interface validation
class MyPluginManager extends AbstractPluginManager {
protected $instanceOf = MyInterface::class;
}
Abstract Factory Performance:
Lazy Service Limitations:
ProxyManager. Ensure it’s installed and configured correctly. Lazy services cannot be serialized/deserialized directly (use ProxyManager's serialize()/unserialize() methods if needed).Delegator Order:
// Wrong: Logging runs after modification
'delegators' => [
MyService::class => [
LoggingDelegator::class,
ModifyingDelegator::class,
],
];
// Correct: Modifying runs first
'delegators' => [
MyService::class => [
ModifyingDelegator::class,
LoggingDelegator::class,
],
];
Service Not Found:
$serviceManager->setService('debug', true);
Factory Errors:
function ($container, $requestedName, array $options = []) {
try {
return new MyService($container->get(Dependency::class));
} catch (Exception $e) {
throw new Exception("Failed to create {$requestedName}: " . $e->getMessage());
}
}
Plugin Manager Issues:
getRegisteredFactories() to inspect registered services:
$pluginManager = $serviceManager->get(MyPluginManager::class);
print_r($pluginManager->getRegisteredFactories());
Lazy Service Debugging:
$service = $serviceManager->get(MyService::class);
var_dump(class_exists('ProxyManager\Proxy\LazyLoadingInterface') &&
$service instanceof ProxyManager\Proxy\LazyLoadingInterface);
Custom Factories:
class DatabaseConnectionFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = []) {
return new PDO(
$options['dsn'],
$options['username'],
$options['password']
);
}
}
Custom Delegators:
AbstractDelegatorFactory to add cross-cutting concerns (e.g., caching, metrics):
class CacheDelegatorFactory extends AbstractDelegatorFactory {
public function __invoke(ContainerInterface $container, $name, $requestedName, array $options = []) {
$service = $this->getService($container, $name);
return new CacheProxy($service, $container->get('cache'));
}
}
Custom Plugin Managers:
AbstractPluginManager for domain-specific validation (e.g., API clients, payment gateways):
class ApiClientPluginManager extends AbstractPluginManager {
protected $instanceOf = ApiClientInterface::class;
public function validate($instance) {
if (!$instance instanceof ApiClientInterface) {
throw new InvalidServiceException('Invalid API client');
}
if (!$instance->isConnected()) {
throw new InvalidServiceException('Disconnected API client');
}
}
}
Service Manager Extensions:
ServiceManager methods to add custom behavior (e.g., auto-registration, lifecycle hooks):
class CustomServiceManager extends ServiceManager {
public function get($name, $plugin = null) {
if
How can I help you explore Laravel packages today?