Home arrow PHP arrow Building a Service Locator in PHP

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.

TABLE OF CONTENTS:
  1. Building a Service Locator in PHP
  2. Implementing a basic service locator
By: Alejandro Gervasio
Rating: starstarstarstarstar / 4
February 14, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

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.          



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