Home arrow PHP arrow The PHP Plug-in Pattern

The PHP Plug-in Pattern

In this first part of a series, I introduce the key concepts that surround the implementation of the Plug-in design pattern in PHP. I also show you how to use it to build an easily extensible application. The sample program in this case will render a few simple HTML widgets on screen, such as divs and paragraphs, but this functionality can be easily extended to other elements as well.

TABLE OF CONTENTS:
  1. The PHP Plug-in Pattern
  2. Implementing the Plug-in pattern
By: Alejandro Gervasio
Rating: starstarstarstarstar / 2
January 19, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

If you frequently work with objects and classes, you know that the intrinsic nature of object-oriented programming provides mechanisms that allow you to extend the functionality of an application, or sections of it, in a fairly straightforward fashion. Of course, the first approach that comes to my mind is Inheritance. It permits you to share, between multiple subclasses, a number of features exposed by a parent class; it even lets you implement new ones.

While Inheritance can be of some help in a case like this, it's an admittedly overrated approach that only allows you to expand an application in a hierarchical way. This process is not always flexible; it can even lead to erroneous creation of hierarchies of classes that don't belong to the same type, due to the improper use of the so-called "is a" relationship.

Luckily, the OOP paradigm offers an alternative which not only makes it easy to reuse common functionality and extend the existing one, but promotes the programming principle of favoring Composition over Inheritance. Of course, I'm talking specifically about  Composition. This approach, when coupled to the power of interfaces, allows you to build "pluggable" systems in a snap. In fact, this association is usually known in OOP jargon as the "Plug-in" design pattern. Not surprisingly, it's covered in depth by Martin Fowler in his already classic book Patterns of Enterprise Application Architecture.

Are you wondering what I mean by "pluggable" systems? Well, let me elaborate this concept with a simple example: say that you've created a handy PHP class that performs strict validation on email addresses. It checks for RFC

compliant formats, MX records and even is capable of querying a mail server to see if it replies back. On the other hand, there's a client class that happily consumes your email validator and injects it into its internals to check email addresses entered by users in an HTML form.

All of this works like a charm -- until the very moment that you realize you need to validate URLs, too. That's not really a big deal after all; you appeal to your programming skills (or someone else's), and then write a URL validator and inject it into the client class. Bam! It turns out that it accepts only an email validator, and therefore refuses to accept your shiny new URL checking class. The party is spoiled.

In a situation like this, the Plug-in pattern can be used for to constructing all sorts of validators, which can be easily plugged into the aforementioned client class, thus implementing a truly "pluggable" system. Did you get the point of using this pattern? I hope so.

Of course, the best way to understand the functionality of "Plug-in" is by example, so in this article series I'll be developing a few with PHP, which will help you grasp its underlying logic more quickly. Now, let's get started!

Getting started: developing an "unpluggable" sample application

As with many other patterns, there's no need to appeal to the "Plug-in" paradigm in every single case. It's possible, however, to recreate a scenario similar to the one described in the introduction that justifies its implementation.

First, I will build a very basic program, which will make you see quickly why the pattern can be so useful when creating scalable systems. The functionality of this program will be limited to rendering some trivial divs on screen and nothing else.

With that said, here's the first element of the program. It happens to be an abstract parent class that models the structure and behavior of generic HTML elements. Its definition is as follows:

(Html/AbstractHtmlElement.php)

<?php

namespace Html;

abstract class AbstractHtmlElement
{
    protected $_content;
    protected $_id;
    protected $_class;
   
    /**
     * Constructor
     */
    public function __construct($content, $id = '', $class = '')
    {
        if (is_string($content)) {
           $this->_content = $content;
        }
        if (is_string($id) && !empty($id)) {
            $this->_id = $id;
        }
        if (is_string($class) && !empty($class)) {
            $this->_class = $class;
        }   
    }

     /**
      * Implementation delegated to concrete HTML elements
      */    
     abstract public function render();    
}

As you can see above, the previous "AbstractHtmlElement" class accepts three arguments through its constructor. The first one is the text that will be wrapped by the HTML element, while the remaining two are optional, and come in handy for assigning to the element an "id" and a "class" attribute respectively.

With that abstract parent comfortably seated on top of the hierarchy, building a refined implementation that renders Divs specifically is as easy as defining the following child class:

(Html/Div.php)

<?php

namespace Html;

class Div extends AbstractHtmlElement
{
    /**
     * Render the Div element
     */
    public function render()
    {
        $html = '<div';
        if ($this->_id) {
            $html .= ' id="' . $this->_id . '"';
        }
        if ($this->_class) {
           $html .= ' class="' . $this->_class . '"';
        }
        $html .= '>' . $this->_content . '</div>' . "n";
        return $html;
    }
}

That was really simple to code and read, wasn't it? As its name suggests, all that the above "Div" class does is generate the HTML markup corresponding to a div element via its "render()" method. Since I'd like to keep the entire rendering process abstract, I'm also going to create a client class that consumes the previous "Div."

This brand new client class is called "Renderer," and its source code looks like this:

(Render/Renderer.php)

<?php

namespace Render;
use Html;

class Renderer
{
    protected $_elements = array();
   
    /**
     * Add a single div element
     */
    public function addElement(HtmlDiv $element)
    {
        $this->_elements[] = $element;
        return $this;
    }
   
    /**
     * Remove a div element
     */
    public function removeElement(HtmlDiv $element)
    {
        if (in_array($element, $this->_elements, true)) {
            $elements = array();
            foreach ($this->_elements as $_element) {
                if ($element !== $_element) {
                    $elements[] = $_element;
                }
            }
            $this->_elements = $elements;
        }
    }
   
    /**
     * Add multiple div elements
     */
    public function addElements(array $elements)
    {
        if (!empty($elements)) {
            foreach ($elements as $element) {
                $this->addElement($element);
            }
        }
    }
   
    /**
     * Render all the inputted div elements
     */
    public function render()
    {
        $output = '';
        if (!empty($this->_elements)) {
            foreach ($this->_elements as $_element) {
                $output .= $_element->render();
            }
        }
        return $output;
    }    
}

At this point, things are becoming a bit more interesting. The earlier "Renderer" class is a sort of Composite that can inject into its internals one or multiple divs objects, which can be rendered in one go through its "render()" method.

So far, so good. Since you probably want to see how all of these sample classes can be put to work together in a concrete example, I'm going to define a simple autoloader. It will save us from the hassle of using multiple PHP requires and will lazy-load the classes in question.

The implementation of this autoloading class is as follows:

(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{}

Since this autoloader looks very similar to the ones used in other tutorials previously published here at the Developer Shed network, I'm not going to waste time discussing its underlying logic. In short, all that you need to know is that this class is a Singleton that includes on demand a namespace class. That is all that it does, in fact.

With the autoloader up and running, it's time to set up a script that displays some divs on screen using all of the sample classes defined so far. The one included below performs this task in a fairly straightforward fashion. Check it out:

<?php

use HtmlDiv as Div,
    RenderRenderer as Renderer;
   
// include the autoloader
require_once 'Autoloader.php';
Autoloader::getInstance();

// create some divs
$div1 = new Div('This is the sample content for the first div element.', 'one_id', 'one_class');
$div2 = new Div('This is the sample content for the second div element.', 'another_id', 'another_class');

// create the renderer and add the previous elements to it
$renderer = new Renderer;
echo $renderer->addElement($div1)
              ->addElement($div2)
              ->render();

Effectively, the previous script first creates two div objects, which are added to the renderer via its "addElement()" method. Finally, the HTML of these objects is displayed on the browser by calling the renderer's "render()" method. Certainly, I don't want to sound like I'm bragging here, but the script yields quite impressive results!

Well, not so fast. What happens if I also want to display a few paragraphs or lists along with the previous divs? In a case like this, the renderer will simply refuse to do so, as it only accepts objects of type "Div." I could change its implementation and make it accept generic "AbstractHtmlElement" objects; then I would derive a couple of subclasses from the mentioned abstract parent, which would render the pertinent paragraphs and lists, thus solving this issue in a pretty effective way.

Unfortunately, this approach offers only a temporary solution. If I decide to write a new class entirely independent from the parent "AbstractHtmlElement" that displays content in a PDF, and pass an instance of it to the renderer, it'll complain loudly again, and refuse to accept this object.

So, is there a way to make the renderer accept any type of "renderable" element, other than closely-related HTML objects? This is exactly the scenario where the "Plug-in" pattern really shines, as its proper implementation permits you to solve this problem in an elegant and effective manner.

Therefore, in the next segment I'm going to introduce some minor changes into the previous sample application. Thanks to the pattern's functionality, the changes will enable the application to accept all sorts of "renderable" objects.

To see how this will be done, jump ahead and read the following lines.  

 



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