The Singleton Design Pattern for PHP

By Tony Marston

26th July 2005
Amended 10th March 2007

Introduction
A non-class Helper function
A separate Helper method within each class
A single Helper method for all classes
A single Helper method with class loader
References
Conclusion
Amendment History

Introduction

In software engineering, a design pattern is a general solution to a common problem in software design. A design pattern isn't a finished design that can be transformed directly into code, it is a description or template for how to solve a problem that can be used in many different situations.

One of these design patterns is called the Singleton. It's purpose can be described quite briefly as follows:

Ensure a class has only one instance and provide a global point of access to it.

It achieves this by only creating a new instance the first time it is referenced, and thereafter it simply returns the handle to the existing instance.

Why should you want this facility in the processing of a web page? There are several reasons:

As with all design patterns there is more than one way in which it can be implemented, so in the following sections I will document some of the methods that I have encountered.

You should also be aware that the code samples in this document will work in both PHP 4 and PHP 5.

A non-class Helper function

This method uses a separate non-class function as a "helper". It contains code similar to the following:

function &getDateObject ()
// return the handle to the standard date validation object.
{
    static $instance;
    
    if (!is_object($instance)) {
        // does not currently exist, so create it
        require_once 'std.datevalidation.class.inc';
        $instance = new date_class;
    } // if
    
    return $instance;
    
} // getDateObject

It is referenced with code similar to the following:

    $dateobj =& getDateObject();

This method has the following advantages:

This method has the following disadvantages:

A separate Helper method within each class

This method requires the addition of a getInstance() (or similar) method within each each and every class, similar to the following:

    function &getInstance ()
    // this implements the 'singleton' design pattern.
    {
        static $instance;
        
        if (!isset($instance)) {
            $c = __CLASS__;
            $instance = new $c;
        } // if
        
        return $instance;
        
    } // getInstance

It is referenced with code similar to the following:

    require_once 'std.datevalidation.class.inc';
    $dateobj =& date_class::getInstance();

This method has the following advantages:

This method has the following disadvantages:

A single Helper method for all classes

This requires the creation of a separate class (called singleton in this example) with a single method (called getInstance() in this example).

class singleton
{
    function &getInstance ($class)
    // implements the 'singleton' design pattern.
    {
        static $instances = array();  // array of instance names
        
        if (!array_key_exists($class, $instances)) {
            // instance does not exist, so create it
            $instances[$class] = new $class;
        } // if
        
        $instance =& $instances[$class];
        
        return $instance;
        
    } // getInstance
    
} // singleton

It is referenced with code similar to the following:

    require_once 'std.datevalidation.class.inc';
    $dateobj =& singleton::getInstance('date_class');

This method has the following advantages:

This method has the following disadvantages:

A single Helper method with class loader

This is similar to the previous example, but does away with the necessity of pre-loading the class definition. Sample code is as follows:

class singleton
// ensure that only a single instance exists for each class.
{
    function &getInstance ($class, $arg1=null)
    // implements the 'singleton' design pattern.
    {
        static $instances = array();  // array of instance names

        if (array_key_exists($class, $instances)) {
            // instance exists in array, so use it
            $instance =& $instances[$class];
            
        } else {
            // load the class file (if not already loaded)
            if (!class_exists($class)) {
                switch ($class) {
                    case 'date_class':
                        require_once 'std.datevalidation.class.inc';
                        break;

                    case 'encryption_class':
                        require_once 'std.encryption.class.inc';
                        break;

                    case 'validation_class':
                        require_once 'std.validation.class.inc';
                        break;

                    default:
                        require_once "classes/$class.class.inc";
                        break;
                } // switch
            } // if

            // instance does not exist, so create it
            $instances[$class] = new $class($arg1);
            $instance =& $instances[$class];
        } // if

        return $instance;

    } // getInstance
    
} // singleton

It is referenced with code similar to the following:

    $dateobj =& singleton::getInstance('whatever');

Notice how most of the classes exist in files with a standard name (classes/$class.class.inc) but some have to be dealt with separately. This is not a problem if the number of variations is small.

The only disadvantage (that I am aware of) to this method is the one regarding case sensitivity, but I avoid this problem by insisting that all class names are in lower case, and that '_' (underscore) be used instead of CamelCaps.

References

Some of the terms used in this document may be new to you, so here are some explanations:

Conclusion

As you can see there is no single way to implement this design pattern, and this is the most simple pattern there is! Just think of the possible variations for the more complex patterns.


© Tony Marston
26th July 2005

http://www.tonymarston.net
http://www.radicore.org

Amendment history:

10th March 2007 Added A single Helper method with class loader.
5th August 2005 Added '&' to the function name and the function call so that when executed in PHP 4 this will return a reference to the original instance instead of a clone (copy) of that instance. The '&' is not necessary in PHP 5 as passing objects 'by reference' is now the default behaviour, which means that creating a 'clone' requires extra effort.

counter