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.
Writing a factory class for each and every service that has dependencies can be tedious, particularly in early development as you are still sorting out dependencies.
laminas-servicemanager ships with Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory,
which provides a reflection-based approach to instantiation, resolving
constructor dependencies to the relevant services. The factory may be used as
either an abstract factory, or mapped to specific service names as a factory:
use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory;
return [
/* ... */
'service_manager' => [
'abstract_factories' => [
ReflectionBasedAbstractFactory::class,
],
'factories' => [
'MyModule\Model\FooModel' => ReflectionBasedAbstractFactory::class,
],
],
/* ... */
];
Mapping services to the factory is more explicit and performant.
The factory operates with the following constraints/features:
$config typehinted as an array will receive the
application "config" service (i.e., the merged configuration).$config, will
be injected with an empty array.$options passed to the factory are ignored in all cases, as we cannot
make assumptions about which argument(s) they might replace.
Once your dependencies have stabilized, we recommend writing a dedicated factory, as reflection can introduce performance overhead; you may use the generate-factory-for-class console tool to do so.
Some services provided by Laminas components do not have entries based on their class name (for historical reasons). As examples:
Laminas\Console\Adapter\AdapterInterface maps to the service name ConsoleAdapter,Laminas\Filter\FilterPluginManager maps to the service name FilterManager,Laminas\Hydrator\HydratorPluginManager maps to the service name HydratorManager,Laminas\InputFilter\InputFilterPluginManager maps to the service name InputFilterManager,Laminas\Log\FilterPluginManager maps to the service name LogFilterManager,Laminas\Log\FormatterPluginManager maps to the service name LogFormatterManager,Laminas\Log\ProcessorPluginManager maps to the service name LogProcessorManager,Laminas\Log\WriterPluginManager maps to the service name LogWriterManager,Laminas\Serializer\AdapterPluginManager maps to the service name SerializerAdapterManager,Laminas\Validator\ValidatorPluginManager maps to the service name ValidatorManager,To allow the ReflectionBasedAbstractFactory to find these, you have two
options.
The first is to pass an array of mappings via the constructor:
$reflectionFactory = new ReflectionBasedAbstractFactory([
\Laminas\Console\Adapter\AdapterInterface::class => 'ConsoleAdapter',
\Laminas\Filter\FilterPluginManager::class => 'FilterManager',
\Laminas\Hydrator\HydratorPluginManager::class => 'HydratorManager',
\Laminas\InputFilter\InputFilterPluginManager::class => 'InputFilterManager',
\Laminas\Log\FilterPluginManager::class => 'LogFilterManager',
\Laminas\Log\FormatterPluginManager::class => 'LogFormatterManager',
\Laminas\Log\ProcessorPluginManager::class => 'LogProcessorManager',
\Laminas\Log\WriterPluginManager::class => 'LogWriterManager',
\Laminas\Serializer\AdapterPluginManager::class => 'SerializerAdapterManager',
\Laminas\Validator\ValidatorPluginManager::class => 'ValidatorManager',
]);
This can be done either in your configuration file (which could be problematic when considering serialization for caching), or during an early phase of application bootstrapping.
For instance, with laminas-mvc, this might be in your Application module's
bootstrap listener:
namespace Application
use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory;
class Module
{
public function onBootstrap($e)
{
$application = $e->getApplication();
$container = $application->getServiceManager();
$container->addAbstractFactory(new ReflectionBasedAbstractFactory([
/* ... */
]));
}
}
For Mezzio, it could be part of your config/container.php definition:
$container = new ServiceManager();
(new Config($config['dependencies']))->configureServiceManager($container);
// Add the following:
$container->addAbstractFactory(new ReflectionBasedAbstractFactory([
/* ... */
]));
The second approach is to extend the class, and define the map in the
$aliases property:
namespace Application;
use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory;
class ReflectionAbstractFactory extends ReflectionBasedAbstractFactory
{
protected $aliases = [
\Laminas\Console\Adapter\AdapterInterface::class => 'ConsoleAdapter',
\Laminas\Filter\FilterPluginManager::class => 'FilterManager',
\Laminas\Hydrator\HydratorPluginManager::class => 'HydratorManager',
\Laminas\InputFilter\InputFilterPluginManager::class => 'InputFilterManager',
\Laminas\Log\FilterPluginManager::class => 'LogFilterManager',
\Laminas\Log\FormatterPluginManager::class => 'LogFormatterManager',
\Laminas\Log\ProcessorPluginManager::class => 'LogProcessorManager',
\Laminas\Log\WriterPluginManager::class => 'LogWriterManager',
\Laminas\Serializer\AdapterPluginManager::class => 'SerializerAdapterManager',
\Laminas\Validator\ValidatorPluginManager::class => 'ValidatorManager',
];
}
You could then register it via class name in your service configuration.
You may also use the Config Abstract Factory, which gives slightly more flexibility in terms of mapping dependencies:
ConfigAbstractFactory.'config' service), choose the ConfigAbstractFactory.ConfigAbstractFactory or create a custom factory.This feature was inspired by a blog post by Alexandre Lemaire.
How can I help you explore Laravel packages today?