Home arrow PHP arrow Page 2 - PHP Service Layers: Dependency Injection

The service’s object graph: basic DI containers - PHP

In this penultimate part of the series, I put the finishing touches on the previous service layer by adding a couple of basic dependency injection containers along with a static helper to it. These can be used by client code to construct a specified service in a valid state.

TABLE OF CONTENTS:
  1. PHP Service Layers: Dependency Injection
  2. The service’s object graph:
By: Alejandro Gervasio
Rating: starstarstarstarstar / 0
October 17, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

Since I want to keep things clear and easy to follow, the DI containers that I plan to build in the next few lines will be simple factories. They will implement the following interface:

(MyApplication/Injector/InjectorInterface.php)

<?php

namespace MyApplicationInjector;

interface InjectorInterface
{
    public function create();
}

Definitely, the contract defined by the “InjectorInterface” interface doesn’t bear any further discussion. So move on and take a close look at the implementer below. It is a factory that creates a Singleton instance of the previous MySQL adapter (you remember that one, right?):  

(MyApplication/Injector/MysqlAdapterInjector.php)

<?php

namespace MyApplicationInjector;
use MyApplicationDatabase;

class MysqlAdapterInjector implements InjectorInterface
{
    protected $_mysqlAdapter;

    /**
     * Create an instance of the MysqlAdapter class
     */
    public function create()
    {
        if ($this->_mysqlAdapter === null) {
            $this->_mysqlAdapter = new DatabaseMysqlAdapter(array('host', 'user', 'password', 'database'));
        }
        return $this->_mysqlAdapter;
    }
}

There’s no need to be a rocket scientist to grasp the logic behind the “MysqlAdapterInjector” class. As you can see, it simply implements the “create()” method defined by the earlier interface to return to client code a Singleton of the aforementioned MySQL adapter.

With this low-level factory already up and running, it’s really easy to create another one that spawns valid user service objects. Skeptical? Then take a peek at the following class, which should change your mind quickly: 

(MyApplication/Injector/UserServiceInjector.php)

<?php

namespace MyApplicationInjector;
use MyApplicationDatabase,
    MyApplicationCollection,
    MyApplicationMapper,
    MyApplicationService;

class UserServiceInjector implements InjectorInterface
{
    /**
     * Create a user service
     */
    public function create()
    {
        $mysqlInjector = new MysqlAdapterInjector;
        $adapter = $mysqlInjector->create();
        return new ServiceUserService(
            new MapperUserMapper($adapter,
                new CollectionUserCollection
            )
        );
    }
}

There you have it. In a few easy steps, I implemented a basic DI container, which is capable of creating instances of the previous user service class by injecting the corresponding dependencies at the appropriate places. Of course, similar results can be obtained with an “automated” container that uses  reflection internally to figure out what collaborators must be injected into a specific object. In this case, though, I decided to stick with the so-called poor man’s dependency injection, since the number of object graphs that need to be created is really small.

So far, so good. At this point, I’m sure that you've grasped how the pair of DI containers do their things. So, the next step is to build a helper that permits us to abstract away the creation of a specified service (including the one that you saw in the preceding section).

The definition of this brand new helper will be shown in the next segment, so keep reading. 

Giving the final touches to the service layer: creating a service helper

Frankly speaking, the creation of a helper class that allows you to retrieve (and also inject) a specified service is entirely optional. However, the convenience of having such a class at our disposal is that it can be used either for pulling in or out model data in an action controller or in a view, when working with an MVC stack.

Having clarified that point, here’s the code for this helper:

(MyApplication/Service/ServiceLocator.php)

<?php

namespace MyApplicationService;
use MyApplicationInjector;

class ServiceLocator
{
    protected static $_services = array();
    protected static $_injectors = array();

    /**
     * Protected constructor
     */
    protected function  __construct(){}

    /**
     * Add a single injector
     */
    public static function addInjector($name, InjectorInjectorInterface $injector)
    {
        if (!isset(self::$_injectors[$name])) {
            self::$_injectors[$name] = $injector;
            return true;
        }
        return false;
    }

    /**
     * Add multiple injectors
     */
    public static function addInjectors(array $injectors)
    {
        foreach ($injectors as $injector) {
            self::addInjector($injector);
        }
    }

    /**
     * Remove an existing injector
     */
    public static function removeInjector($name)
    {
        if (isset(self::$_injectors[$name])) {
            unset(self::$_injectors[$name]);
            return true;
        }
        return false;
    }
   
    /**
     * Get the specified injector
     */
    public static function getInjector($name)
    {
        return isset(self::$_injectors[$name]) ?
               self::$_injectors[$name] :
               null;
    }
   
    /**
     * Check if the specified injector exists
     */
    public static function injectorExists($name)
    {
        return isset(self::$_injectors[$name]);
    }

    /**
     * Add a service
     */
    public static function addService($name, AbstractService $service)
    {
        if (!isset(self::$_services[$name])) {
            self::$_services[$name] = $service;
            return true;
        }
        return false;
    }

    /**
     * Add multiple services
     */
    public static function addServices(array $services)
    {
        foreach($services as $service) {
            self::addService($service);
        }
    }

    /**
     * Remove an existing service
     */
    public static function removeService($name)
    {
        if (isset(self::$_services[$name])) {
            unset(self::$_services[$name]);
            return true;
        }
        return false;
    }

    /**
     * Get the specified service (if not created already, the associated injector builds the service)
     */
    public static function getService($name)
    {
        // if the service has been added and cached, get it from the cache
        if (isset(self::$_services[$name])) {
            return self::$_services[$name];
        }
        // otherwise, attempt to build the service via the associated injector and save it to the cache
        if (!$injector = self::getInjector($name)) {
            throw new ServiceLocatorException('Unable to get the injector associated to the specified service.');
        }
        $service = $injector->create();
        self::addService($name, $service);
        return $service;
    }

    /**
     * Check if the specified service exists
     */
    public static function serviceExists($name)
    {
        return isset(self::$_services[$name]);
    }
}

 

(MyApplication/Service/ServiceLocatorException.php)

<?php

namespace MyApplicationService;

class ServiceLocatorException extends Exception{}

From the above code fragment, it’s clear to see that the previous “ServiceLocator” class behaves like a static registry. This permits us to save and retrieve services and DI containers (called injectors in this case) via a set of convenient methods.

Nevertheless, the most interesting aspect of this class is the implementation of its “getService()” method. It follows a simple naming convention to retrieve a specified service. For instance, if the requested service is called “user,” the method will first look in the registry to see if the service has been created, and will return it to client code. Otherwise, it’ll attempt to find an associated injector called “UserServiceInjector,” which will be used (when possible) to create the pertinent service. Do you understand the inner workings of the helper? Great.

And now that you've grasped how this class functions, we're finally ready to put this sample user service to work for us. However, to see the service’s actual functionality, you’ll have to wait until the next tutorial.    

Final Thoughts

In this penultimate chapter of the series, I put the final touches on the previous service layer. I added to it a couple of basic dependency injection containers, along with a static helper. These can be used by client code -- and by client code, I mean anything that can consume the layer in question, including action controllers and views in an MVC schema -- to construct a specified service in a valid state.

With this additional step, we've finally set the scene to see the service's abilities in action. So, make sure to tighten your seatbelts and be ready to be confronted with a bunch of code samples, as this process will be conducted in the last tutorial.

Don’t miss it!



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: