Home arrow PHP arrow Dependency Injection and Service Locators in PHP

Dependency Injection and Service Locators in PHP

In this second tutorial of the series, I show how the combined functionality provided by dependency injection and a service locator can be quite helpful in the construction of testable object graphs.

TABLE OF CONTENTS:
  1. Dependency Injection and Service Locators in PHP
  2. Modifying the library by injecting the registry locator
By: Alejandro Gervasio
Rating: starstarstarstarstar / 3
February 15, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

Unless you build your object graphs by poisoning your constructors with tons of "new" operators (which is a cardinal sin), you know that dependency injection and service locators are currently the two most popular approaches to connecting sets of related objects.

In the PHP world, dependency injection (AKA inversion of control) is quickly being adopted, due to its relatively simple implementation and the benefits that it brings to the table, such as the construction of easily testable classes. But in truth, the utilization of a service locator deserves a close analysis as well. This approach can be quite helpful in those use cases where the injection of collaborators is not a viable option.

In line with this idea, in the first part of this series I went through the development of a basic registry library. It used a single service locator to provide a client class with its dependency -- in this case, a naive array-based registry. Moreover, to help you see the differences between using dependency injection and a service locator, I deployed the library by using each approach individually (more hard work for me, but hopefully in pursuit of a just cause).

It's probable that you're thinking that these approaches are mutually exclusive. They really aren't. In fact, it's possible to inject a service locator either through a constructor or a setter and get the dependencies of a given object. In most cases, this method is used when those dependencies have a shorter life cycle than the object that needs them. In a situation like this, the locator is a plain factory or a builder, which spawns collaborators on request.

Since the scenario just described is pretty common in modern web application development, in this second tutorial I'm going to modify the sample registry library previously developed. This time, it will combine the functionality of dependency injection and a service locator within the same client class.

Are you ready to see how this will be accomplished? Then start reading!

Recap: building a simple service locator

Just in case you haven't read the previous article, where I explained how to implement a basic service locator, below I included the source classes (and the interface) corresponding to this example. As noted in the introduction, the locator was utilized as part of a registry library, which permitted you to save and fetch data from a plain array.

Having said that, here's the interface that defines the methods of an abstract registry:  

(Registry/Registrable.php)

<?php

namespace Registry;

interface Registrable
{
    public function set($key, $value);
   
    public function get($key);
   
    public function clear(); 
}

Understanding the contract that the "Registrable" interface establishes is a process that doesn't bear any further discussion. So, the next step is to show the definition of a concrete registry, which in this case stores and retrieves data from a protected array. This one is an implementer of the previous interface, and its source code is as follows: 

(Registry/ArrayRegistry.php)

<?php

namespace Registry;

class ArrayRegistry implements Registrable
{
    protected $_data = array();
   
    /**
     * Save the specified value to the array registry
     */
    public function set($key, $value)
    {
        $this->_data[strtolower($key)] = $value;
    }
   
    /**
     * Get the specified value from the array registry
     */
    public function get($key)
    {
        $key = strtolower($key);
        return isset($this->_data[$key]) ?
               $this->_data[$key] :
               null;
    }
   
    /**
     * Clear the array registry
     */
    public function clear()
    {
        $this->_data = array();
    }     
}

So far, so good. With this concrete array-based registry up and running, I could create a script that puts it to work directly. But, since I want to abstract the storage and retrieval of data, I'm going to hide the registry from the outside and use a client class instead.

If you're wondering how this client class obtains an instance of the array registry, the following service locator should answer your question quickly. Check it out:

(Registry/RegistryLocator.php)

<?php

namespace Registry;

class RegistryLocator
{
    /**
     * Private constructor
     */
    private function __construct(){}
     
    /**
     * Get the array registry
     */
    public static function getArrayRegistry()
    {
        return new ArrayRegistry;
    } 
}

Effectively, the above locator is a static factory responsible for creating the pertinent array-based registry. With this element added to this sample library, building a client that consumes the registry is as simple as defining the following class:

(Registry/Registry.php)

<?php

namespace Registry;

class Registry
{  
    protected $_registryBackend;
   
    public function __construct()
    {
        $this->_registryBackend = RegistryLocator::getArrayRegistry();
    }
    
    /**
     * Save the specified element to the registry
     */
    public function set($key, $value)
    {
        $this->_registryBackend->set($key, $value);
        return $this;
    }

    /**
     * Get the specified element from the registry
     */
    public function get($key)
    {
        return $this->_registryBackend->get($key);
    }
   
    /**
     * Clear the registry
     */
    public function clear()
    {
        $this->_registryBackend->clear();
    }         
}

Even though the implementation of the above "Registry" class is somewhat primitive, it shows in a nutshell the role of a service locator. In this case, the class appeals to the locator to get its collaborator, via a call to its static "getArrayRegistry()" method.

Of course, the complexities of this internal process are shielded from client code. This is demonstrated by the following code snippet (the autoloader's definition has been omitted for the sake of brevity, but you can see it here). Look at it:

<?php

// example using a service locator
use RegistryRegistry as Registry;

// include the autoloader
require_once 'Autoloader.php';
Autoloader::getInstance();

// create an instance of the registry and save some data in it
$registry = new Registry;
$registry->set('name', 'Mary Wilson')
         ->set('email', 'mary@domain.com');

// get the data from the registry
echo 'Name: ' . $registry->get('name') . ' Email: ' . $registry->get('email');

I don't want to make Mary Wilson angry, but the way that previous script manipulates her data is in this case is completely irrelevant. What's really important here is that the "Registry" class gets its dependency through a service locator, which is invoked statically. 

But, before I hear you insisting that the same functionality can be achieved with dependency injection, first take a deep breath and calm down. If you analyze the "Registry" and "RegistryLocator" classes, you'll realize that it's possible to modify their respective definitions and make the former ask for the latter in the constructor.

Does this sound confusing? Well, fear not, as this approach is pretty common nowadays. It consists of injecting a service locator, instead of invoking it statically. Therefore, in the next section I'm going to amend the source code of the aforementioned classes, so the locator can be injected into the client registry's internals.

To see how this will be done, move ahead and keep reading.



 
 
>>> 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: