Building a Service Locator in PHP

In this first part of a series, I introduce you to the key concepts that surround the definition of a service locator. I’ll also show you how to use this approach in the development of a basic registry library.

Let’s face it: it takes years to master the complexities of good object-oriented programming, even when using an easy-going language like PHP. Mastering the paradigm demands a solid background in its major pillars, such as Inheritance, Polymorphism, Composition/Aggregation (feel free to add your own topic to the list). It also calls for tackling an unavoidable fact of its intrinsic nature… yes, sooner or later, objects need to be created!

Even though the subject appears trivial at first glance, class instantiation is a challenging process that requires a lot more of planning than many people think. The adoption of good programming habits, coupled with the use of well-known principles, like The Law of Demeter (http://en.wikipedia.org/wiki/Law_of_Demeter) and separation of concerns (AKA The Single Responsibility principle: http://en.wikipedia.org/wiki/Single_responsibility_principle) very often lead to the development of applications that are composed of a huge number of objects with limited responsibilities. These objects need to be properly wired up, so they can interact with each other as intended.

In such a scenario, many programmers ask themselves what the best approach to take is when building object graphs, regardless of how complex they can become. Well, currently there are two streams of thought that clearly stand out from the crowd. These are the implementation of dependency injection (DI) and the use of service locators. While the former has certainly gained a lot of popularity in the last few years, the latter still remains rather overlooked, especially in the PHP field.

Contrary to DI, where objects ask for their collaborators, either through their constructors or setters, service locators act like mediators, responsible for providing those collaborators on demand to the objects in question.    

To be frank, there are some good reasons that justify this “deliberated” indifference to service locators. The first one is that they’re hard to test (unlike DI, where the use of mocks and stubs makes it easy to test classes in isolation). The second one is that, quite frequently, a service locator introduces a strong coupling with the object that requires its services. And we all know that coupling is a clear sign of code that stinks, right?

However, there are certain situations in which a service locator may be pretty helpful, especially when combined with the benefits provided by DI. Therefore, in this article series I’m going to set up some code samples to show you how to implement several service locators in PHP. This way, you’ll understand their underlying logic, and decide whether or not they fit your needs.

Ready to begin this hopefully educational journey? Then let’s go!

Creating object graphs using dependency injection: developing an extensible registry

Since my plan here is to illustrate how to construct a few basic service locators, the first example that I’m going to create will develop a simple registry library. The library will rely on the functionality of dependency injection to switch to different registry back-ends at run time. Once you grasp how the library does its thing, I’m going to refactor its implementation, so it can use a service locator instead.

Having clarified that point, the first element of the library is an interface called “Registrable.” It defines the contract that must be fulfilled by all of the registry back-ends. Here’s the interface:   

(Registry/Registrable.php)

<?php

namespace Registry;

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

As you can see, all that this interface does is define an abstract registry. This process is not especially interesting. So it’s time to move on and create a concrete registry, which naturally must implement the interface.

The one shown below does precisely that. Check it out: 

(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();
    }     
}

As its name suggests, the above “ArrayRegistry” class is a super simple registry. It permits you to save and retrieve data from a protected array. Since this class is effectively an implementer of the previous “Registrable” interface, it’s possible to create a client that injects an instance of it into its internals for further use.

The following client class is capable of consuming the previous registry, or any other that implements the “Registrable” interface. Look at it, please:

(Registry/Registry.php)

<?php

namespace Registry;

class Registry
{
    protected $_registryBackend;
   
    /**
     * Constructor
     */
    public function __construct(Registrable $registryBackend)
    {
        $this->_registryBackend = $registryBackend;
    }
   
    /**
     * 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();
    }         
}

At this point things are becoming a bit more exciting, as I managed to create a client class which can inject different registry back-ends, including the array-based one defined a moment ago. Having achieved that, the next logical step is to set up a script that shows how to use this basic registry library.

Since I want to avoid using multiple PHP requires, I’m going to utilize a simple autoloader. It’s the same one that you’ve probably seen in previous articles published here at the Developer Shed network. For the sake of completeness, I included its definition below. Of course, you’re free to skip over it if you want to:  

(Autoloader.php)

<?php

class Autoloader
{
    private static $_instance;
   
    /**
     * Get the Singleton instance of the autoloader
     */
    public static function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new self;
        }
        return self::$_instance;
    } 
   
    /**
     * Reset the instance of the autoloader
     */
    public static function resetInstance()
    {
        self::$_instance = null;
    }
   
    /**
     * Class constructor
     */
    private function __construct()
    {
        spl_autoload_register(array(__CLASS__, ‘load’));
    }
   
    /**
     * Prevent to clone the instance of the autoloader
     */
    private function __clone(){}
   
    /**
     * Load a given class or interface
     */
    public static function load($class)
    {
        $file = str_replace(”, ‘/’, $class) . ‘.php';
        if (!file_exists($file)) {
            throw new AutoloaderException(‘The file ‘ . $file . ‘ containing the requested class or interface ‘ . $class . ‘ was not found.’);
        }
        require $file;
        if (!class_exists($class, false) && !interface_exists($class, false)) {
            throw new AutoloaderException(‘The requested class or interface ‘ . $class . ‘ was not found.’);
        }
    }  
}

 

(AutoloaderException.php)

<?php

class AutoloaderException extends Exception{}

With the earlier autoloader already up and running, the only thing left to do is write the aforementioned script, which will show how to work with the previous registry classes. Here is the snippet  that performs this task:

<?php

// example using dependency injection
use RegistryRegistry as Registry,
    RegistryArrayRegistry as ArrayRegistry;

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

// create an instance of the registry and save some data in it
$registry = new Registry(new ArrayRegistry);
$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’);

Mission accomplished, at least for now. Thanks to the combined functionality of the earlier classes, storing and fetching data from the array-based registry is actually a breeze.

Beyond the simplicity of this example, you should focus your attention on how the “Registry” class works. Yes, to do its business the class “asks for” a registrable object in its constructor, which in this case turns out to be the pertinent array registry. Simply put, this means that the class’s collaborator is injected. Period.

While this approach is very efficient (and easily testable, by the way), what would happen if the “Registry” class obtained its collaborator by means of a mediator object, instead of using constructor injection? Well, in a case like this, in programming jargon this mediator would be called a “service locator” and would be responsible for factoring all of the dependencies required by the class.

So, now that you have a vague idea of what a service locator is, in the next section I’m going to modify the previous example. This time, it will use a service locator to provide the “Registry” class with its dependency.

To learn the full details of this process, jump ahead and keep reading.          

{mospagebreak title=Implementing a basic service locator}

In order to make the previous “Registry” class get its dependency via a service locator, it’s necessary to introduce two changes into the whole registry library. The first one, logically, is to define the service locator (or a registry locator, to be more precise) and the second one is … yes, to modify the class itself.

Since the definitions corresponding to the “Registrable” interface and its implementer “ArrayRegistry” will remain exactly the same, I’m not going to list them again, to keep the sample codes uncluttered.

With that said, here’s the implementation of the service locator, which not surprisingly is called “RegistryLocator”: 

(Registry/RegistryLocator.php)

<?php

namespace Registry;

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

That was easy to code and read, wasn’t it? As seen above, this service locator is nothing but a static factory that spawns instances of the “ArrayRegistry” class and returns them to client code via its “getArrayRegistry()” method. Nothing less, nothing more.

With this service locator already defined, the last step is to tweak the source code of the “Registry” client class, so it can obtain its dependency through the locator. Here’s the amended version of this 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();
    }         
}

There you have it. Now the above client class fetches its dependency (in this case a single object, but it might be multiple ones) by calling the static factory method of the service locator. Of course, if you’re anything like me at this point, some alarm bells should be ringing off in your head. But why?

First off, the class is no longer capable of working with multiple registry back-ends, unless a new factory method is added to the locator. And last but not least, testing it is much harder.

Aside from demonstrating how to implement a simple service locator in PHP, this trivial example also shows the pros and cons of using this approach. On one hand, the class using the locator effectively hides from the outside how it obtains its dependency; on the other one, though, it sacrifices flexibility and testability. That’s why in many cases a service locator is considered a degradation of dependency injection.

But before you express your own judgment, first it’s necessary to create an example that puts the registry locator to work, so you can see if it really functions as intended.

This will be done in the following segment. So go ahead and read the next few lines. 

The service locator in action

As I stated previously, below I coded an example that demonstrates how to use the “Registry” class, now that it gets its dependency through the array locator. Take a look at the following code fragment:

<?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’);

Although rather contrived, this example shows pretty clearly the impact produced by the implementation of the previous service locator. Effectively, once an instance of the “Registry” class is created, it’s used to store and retrieve some data from the array registry, even though this dependency has been provided “behind the scenes.”

This is in fact a double-edged sword: from the client code’s point of view, using the registry is slightly simpler, as there’s no need to perform any constructor or setter dependency injection. However, from a programmer’s standpoint, testing the registry and even figuring out how the registry gets its dependency is definitely more difficult.

In my opinion, dependency injection is the way to go when building object graphs. This doesn’t mean, however, that a service locator should be abandoned in a dark, isolated corner and condemned for its evil actions. As I said before, there’s a number of cases where it can be useful, particularly when combined when inversion of control.

But I’m getting ahead of myself, since that is the topic that I plan to cover in the next installment.  

Final thoughts

In this first chapter of the series, I provided you with a humble introduction to the key concepts that surround the definition of a service locator, and also showed you how to use this approach during the development of a basic registry library. As you just saw, there are some serious drawbacks that make a service locator more inefficient than dependency injection, especially when it comes to constructing testable object graphs.

It’s possible, though, to combine the best of both worlds and inject a service locator. This method is quite interesting, and deserves deep analysis. Therefore, in the upcoming tutorial I’m going to modify some of the source classes that comprise the earlier registry library, so it can use an injected registry locator.

Don’t miss the next part! 

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri