Jul 182012
 

This post is intended to familiarize you with the various features of the new Zend Framework 2 ServiceManager component along with some simple examples.

So, what is the ServiceManager? Basically it’s a registry, or container (the proper term is service locator) to hold various objects needed by your application, allowing you to easily practice Inversion of Control. The service manager holds just the information needed to lazily instantiate these objects as they’re needed. So if you were thinking ‘services’ such as those composing a service layer, you might be better off thinking of the service manager more as an “object manager” or “instance manager”.

Service Manager Features

At first glance, the Zend\ServiceManager component might seem like a simple registry or service locator, and it is. You can simply set and get objects under a given name:

$serviceManager->setService('some_service', new SomeService());
$someService = $serviceManager->get('some_service');

Note: All service names are case-insensitive and the following characters are stripped from the names internally: -, _, \, /, and spaces.

However, the ServiceManager component provides some extra goodies for us to make our lives easier. An instance of the ServiceManager (hereby referred to as SM) can contain the following things:

Invokables

An invokable is simply a fully qualified class name, provided to the SM as a string. When requested, it will simply be instantiated with new $invokableClass();.

$serviceManager->setInvokableClass('index', 'MyModule\Controller\IndexController');
$indexController = $serviceManager->get('index');
Factories

A factory is either a PHP callable, an object, or the fully qualified class name of a class implementing Zend\ServiceManager\FactoryInterface. Factories are used to perform any setup or dependency injection required for the object being requested.

$serviceManager->setFactory('user_mapper', function($serviceManager) {
    $mapper    = new \MyModule\Mapper\User;
    $dbAdapter = $serviceManager->get('db_adapter');
    $mapper->setDbAdapter($dbAdapter);
    return $mapper;
});
$userMapper = $serviceManager->get('user_mapper');
Aliases

An alias simply points one service name to another and they can be recursive. This may seem pointless at first, but aliases actually play a very important role in a modular environment.

$serviceManager->setAlias('usermodule_db_adapter', 'db_adapter');
$db = $serviceManager->get('usermodule_db_adapter'); // Will actually retrieve 'db_adapter'
Initializers

An initializer is either a closure, an object, or the fully qualified class name of a class implementing Zend\ServiceManager\InitilizerInterface. Any object pulled from a service manager is ran through the registered initializers which can perform additional initialization tasks.

$serviceManager->addInitializer(function($instance, $serviceManager) {
    if ($instance instanceof SomethingAwareInterface) {
        $instance->setSomething($serviceManager->get('something'));
    }
});
Configuration Classes

A configuration class that implements Zend\ServiceManager\ConfigInterface. Config classes simply know how to configure an instance of the SM, potentially setting several factories, invokables, initializers, etc.

$serviceConfig = new ServiceConfig;
$serviceConfig->configureServiceManager($serviceManager);
Shared Services

Anything placed in the SM can be either shared or not shared. If shared, the SM will create an instance of the requested service object the first time it is requested, and on subsequent requests, return that same exact instance. If shared is set to false for a service, the service manager will create a new service. By default, all services are set to shared.

$serviceManager->setInvokable('my_service', 'My\Service');
$serviceA = $serviceManager->get('my_service');
$serviceB = $serviceManager->get('my_service'); // Same exact instance as $serviceA
 
$serviceManager->setShared('my_service', false);
 
$serviceC = $serviceManager->get('my_service'); // New instance of My\Service
 
var_dump((spl_object_hash($serviceA) === spl_object_hash($serviceB))); // bool(true)
var_dump((spl_object_hash($serviceB) === spl_object_hash($serviceC))); // bool(false)
Abstract Factories

If a SM is asked for a service which it cannot locate, it will then query the registered abstract factories to see if any of them are able to create the requested object. An abstract factory is either a fully qualified class name as a string or instance of an object that implements Zend\ServiceManager\AbstractFactoryInterface.

Note: In the standard ZF2 MVC configuration, if you have any DI definitions in your application’s configuration, a DI abstract factory is registered on the main service manager. This means that if a requested service is not in the SM, it will try to retrieve it from your Zend\Di\Di container.

Peering Service Managers

The service manger introduces a concept of peering. Each service manager can have “peers”, or rather, a stack of one or more other service managers which can also be used when a service is pulled from the SM.

The ServiceManager class also has an option to tell it to try the registered peering service managers before trying to resolve a service itself which can be set with $serviceManager->setRerieveFromPeeringManagerFirst(true);. Apart from this distinction, peering service managers behave much like abstract factories.

$serviceManagerA = new ServiceManager();
$serviceManagerA->setInvokableClass('index', 'MyModule\Controller\IndexController');
 
// Create a new service manager which has $serviceManagerA as a peer.
$serviceManagerB = $serviceManagerA->createScopedServiceManager(ServiceManager::SCOPE_PARENT);
$indexController = $serviceManagerB->get('index'); // Works!
 
// Create a new service manager, and add it as a peer to $servicemanagerA
$serviceManagerC = $serviceManagerA->createScopedServiceManager(ServiceManager::SCOPE_CHILD);
$indexController = $serviceManagerC->get('index'); // ServiceNotFoundException!
 
$serviceManagerC->setInvokableClass('foo', 'MyModule\Foo');
$foo = $serviceManagerA->get('foo'); // Works!

Note: By default the “ControllerLoader” SM (the one configured via the ‘controllers’ key in the config or Module::getControllerConfiguration() in your modules) is set up with the main service manager as a peer.


For further reading, see the Service Manager Quick-Start.

  18 Responses to “Introduction to the Zend Framework 2 ServiceManager”

  1. You make a note saying

    Note: All service names are case-insensitive and the following characters are stripped from the names internally: -, _, \, /, and spaces

    But then use an underscore in the following examples.

    Just a typo.

    • I still use underscores in my service names for two reasons: 1) it improves legibility, and 2) in ZF, the suggested convention for configuration keys is lowercase_underscore.

  2. I think a nice addition would be a listing of the available services by default, from

    ServiceListenerFactory::$defaultServiceConfig

    .

  3. Could you please tell me when to register service and where?

    for example should i register service from a controller? or from a module.php file? from view?

  4. This seems a good explanation of the ServiceManager but I am still having trouble trying to visualize the separate sections of the MVC and the Applicationand how different elements relate.

    ie. When & how is the service manager used in a typical MVC Application along with when and how are the other ZF2 components used.

    I feel an article on this (Maybe with some nice diagrams) would be an absolute gem to all of us confused php programmers trying to figure out how ZF2 actually works as a whole for an MVC Application

  5. Hi!

    I translate this post to Russian. You can find it here: http://zftutorials.ru/blog/introduction-to-the-zend-framework-2-servicemanager.html

    Tnanks for usefull post

  6. How am I to understand the difference between this sentence: “Create a new service manager which has $serviceManagerA as a peer,” and this one: “Create a new service manager, and add it as a peer to $servicemanagerA”?

  7. Hi, you do not explain where to put all fragment of code in witch file inside a zend 2 project. Into the boostrap.php file? Where is the first and only first method called into the boostrapping phase that initialize the service? How to use this service into a Controller? How to use this service into a model? The examples is not complete with all the part necessary to understand the complete process. Thanks

  8. Helped me understand ServiceManager. Nice article. Appreciated

  9. i was looking for ” the Zend\ServiceManager component might seem like a simple registry or service locator, and it is.” and got it. tnx alot.

  10. Hi Evan,

    Thank you for the explanation. I think that a practical example could clarify some of the questions above and a lot of mine. Think about it, don’t fly an airplane this weekend and help us please ;)

    Regards.

  11. Nice read. I think the whole idea of peering service managers can be regarded as simple inheritance. Might be easier to understand that way

  12. Hi Evan, thanks for this tutorial.

    I have a question about your Shared Services example. I have your exact code in a controller of one of my module, I think this test should work also from there, correct me if I mistake.

    First problem is the call to ‘setInvokable’ that is an undefined method, should be setInvokableClass?

    After edited setInvokable to setInvokableClass it works but both the var_dump give true.

    Then I tried to put the setShared call before the first call of the service and I got both false: it is like if the service is already called at least once it is not possible to modify the shared flag.

    Any suggestions?

    Thank you

  13. Hi Evan

    And get service controller not getting dependencies, and my Fatal error Call to a member function setQuery() on a non-object, the request is null

    setEvent is nesseray?

    thanks!

  14. Great stuff! As always very clear and straight.

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre user="" computer="" color="" escaped="">