Zend Framework – Service Manager

The Zend Framework includes a powerful service locator pattern implementation called zend-servicemanager. Zend framework extensively uses the service manager for all its functionalities. The Service Manager provides a high-level abstraction for the Zend Framework. It also integrates nicely with all the other components of the Zend Framework.

Install Service Manager

The Service Manager component can be installed using the composer tool.

composer require zendframework/zend-servicemanager

Example

First, all the services need to be registered into the service manager. Once the services are registered into the server manager system, it can be accessed at any time with minimal efforts. The service manager provides a lot of options to register the service. A simple example is as follows −

use Zend\ServiceManager\ServiceManager; 
use Zend\ServiceManager\Factory\InvokableFactory; 
use stdClass;  
$serviceManager = new ServiceManager([ 
   'factories' => [stdClass::class => InvokableFactory::class,], 
]);

The above code registers the stdClass into the system using the Factory option. Now, we can get an instance of the stdClass at any time using the get() method of the service manager as shown below.

use Zend\ServiceManager\ServiceManager;  
$object = $serviceManager->get(stdClass::class);

The get() method shares the retrieved object and so, the object returned by calling the get() method multiple times is one and the same instance. To get a different instance every time, the service manager provides another method, which is the build() method.

use Zend\ServiceManager\ServiceManager;  
$a = $serviceManager->build(stdClass::class); 
$b = $serviceManager->build(stdClass::class);

Service Manager Registration

The service manager provides a set of methods to register a component. Some of the most important methods are as given below −

  • Factory method
  • Abstract factory method
  • Initializer method
  • Delegator factory method

We will discuss each of these in detail in the upcoming chapters.

Factory Method

A factory is basically any callable or any class that implements the FactoryInterface (Zend\ServiceManager\Factory\FactoryInterface).

The FactoryInterface has a single method −

public function __invoke(ContainerInterface $container, $requestedName, array 
   $options = null)

The arguments details of the FactoryInterface is as follows −

  • container (ContainerInterface) − It is the base interface of the ServiceManager. It provides an option to get other services.
  • requestedName − It is the service name.
  • options − It gives additional options needed for the service.

Let us create a simple class implementing the FactoryInterface and see how to register the class.

Class Test – Object to be Retrieved

use stdClass;  
class Test { 
   public function __construct(stdClass $sc) { 
      // use $sc 
   } 
} 

The Test class depends on the stdClass.

Class TestFactory – Class to Initialize Test Object

class TestFactory implements FactoryInterface { 
   public function __invoke(ContainerInterface $container, $requestedName, 
      array $options = null) { 
      $dep = $container->get(stdClass::class); 
      return new Test($dep); 
   } 
}

The TestFactory uses a container to retrieve the stdClass, creates the instance of the Test class, and returns it.

Registration and Usage of the Zend Framework

Let us now understand how to register and use the Zend Framework.

serviceManager $sc = new ServiceManager([ 
   'factories' => [stdClass::class => InvokableFactory::class, 
      Test::class => TestFactory::class] 
]); 
$test = $sc->get(Test::class);

The service manager provides a special factory called InvokableFactory to retrieve any class which has no dependency. For example, the stdClass can be configured using the InvokableFactory since the stdClass does not depend on any other class.

serviceManager $sc = new ServiceManager([ 
   'factories' => [stdClass::class => InvokableFactory::class] 
]);  
$stdC = $sc->get(stdClass::class); 

Another way to retrieve an object without implementing the FactoryInterface or using the InvokableFactory is using the inline method as given below.

$serviceManager = new ServiceManager([ 
   'factories' => [ 
      stdClass::class => InvokableFactory::class, 
      Test::class => function(ContainerInterface $container, $requestedName) { 
         $dep = $container->get(stdClass::class); 
         return new Test($dep); 
      }, 
   ], 
]);

Abstract Factory Method

Sometimes, we may need to create objects, which we come to know only at runtime. This situation can be handled using the AbstractFactoryInterface, which is derived from the FactoryInterface.

The AbstractFactoryInterface defines a method to check whether the object can be created at the requested instance or not. If object creation is possible, it will create the object using the __invokemethod of the FactoryInterface and return it.

The signature of the AbstractFactoryInterface is as follows −

public function canCreate(ContainerInterface $container, $requestedName) 

Initializer Method

The Initializer Method is a special option to inject additional dependency for already created services. It implements the InitializerInterface and the signature of the sole method available is as follows −

public function(ContainerInterface $container, $instance)  
function(ContainerInterface $container, $instance) { 
   if (! $instance instanceof EventManagerAwareInterface) { 
      return; 
   } 
   $instance->setEventManager($container->get(EventManager::class)); 
} 

In the above example, the method checks whether the instance is of type EventManagerAwareInterface. If it is of type EventManagerAwareInterface, it sets the event manager object, otherwise not. Since, the method may or may not set the dependency, it is not reliable and produces many runtime issues.

Delegator Factory Method

Zend Framework supports delegators pattern through DelegatorFactoryInterface. It can be used to decorate the service.

The signature of this function is as follows −

public function __invoke(ContainerInterface $container, 
   $name, callable $callback, array $options = null 
); 

Here, the $callback is responsible for decorating the service instance.

Lazy Services

Lazy service is one of those services which will not be fully initialized at the time of creation. They are just referenced and only initialized when it is really needed. One of the best example is database connection, which may not be needed in all places. It is an expensive resource as well as have time-consuming process to create. Zend framework provides LazyServiceFactory derived from the DelegatorFactoryInterface, which can produce lazy service with the help of the Delegator concept and a 3rd party proxy manager, which is called as the ocramius proxy manager.

Plugin Manager

Plugin Manager extends the service manager and provides additional functionality like instance validation. Zend Framework extensively uses the plugin manager.

For example, all the validation services come under the ValidationPluginManager.

Configuration Option

The service manager provides some options to extend the feature of a service manager. They are shared, shared_by_default and aliases. As we discussed earlier, retrieved objects are shared among requested objects by default and we can use the build() method to get a distinct object. We can also use the shared option to specify which service to be shared. The shared_by_default is same as the shared feature, except that it applies for all services.

$serviceManager = new ServiceManager([ 
   'factories' => [ 
      stdClass::class => InvokableFactory::class 
   ], 
   'shared' => [ 
      stdClass::class => false // will not be shared 
   ], 
   'shared_by_default' => false, // will not be shared and applies to all service 
]);

The aliases option can be used to provide an alternative name to the registered services. This have both advantages and disadvantages. On the positive side, we can provide alternative short names for a service. But, at the same time, the name may become out of context and introduce bugs.

aliases' => ['std' => stdClass::class, 'standard' => 'std'] 

Leave a Reply