Back
Singleton
Implementing Singleton
Observer
Forward


Implementing Singleton

Singleton is even simpler than Abstract Factory; it's an elegant idea but there isn't a lot of variation in it. Here's how the Horde Registry:: class implements it:

<?php
    
/**
     * Returns a reference to the global Registry object, only
     * creating it if it doesn't already exist.
     *
     * This method must be invoked as: $registry = &Registry::singleton()
     *
     * @return object The Horde Registry instance.
     */
    
function &singleton()
    {
        static 
$registry;

        if (!isset(
$registry)) {
            
$registry = new Registry();
        }

        return 
$registry;
    }

Note where we use "&" in the name of the function. It must be written that way to tell the PHP engine that the function should return a reference.

Those of you familiar with this pattern in other languages will be wondering about something: no, we can't make the constructor private in PHP. In most languages, you would declare the constructor of a singleton class private, so that nothing outside of the class could instantiate the class - only the singleton() accessor. In PHP we don't have that enforcement, so you simply have to be consistent about always using the singleton() method and never calling new.

Here's a slightly more complicated example, again from Horde's Auth library. This has two wrinkles in it: it calls a factory() method, instead of calling new directly, and it also modifies the pattern slightly to only create one instance of an Auth object per set of parameters. This means that it will give you back different objects when you ask for a SQL authentication object and a Kerberos authentication object, but if you ask for a SQL authentication object again, it'll give you back the same one you got the first time.

<?php
    
/**
     * Attempts to return a reference to a concrete Auth instance
     * based on $driver. It will only create a new instance if no Auth
     * instance with the same parameters currently exists.
     *
     * This should be used if multiple authentication sources (and,
     * thus, multiple Auth instances) are required.
     *
     * This method must be invoked as: $var = &Auth::singleton()
     *
     * @access public
     *
     * @param string $driver          The type of concrete Auth subclass to
     *                                return. This is based on the storage
     *                                driver ($driver). The code is dynamically
     *                                included.
     * @param optional array $params  A hash containing any additional
     *                                configuration or connection parameters a
     *                                subclass might need.
     *
     * @return object Auth  The concrete Auth reference, or false on an error.
     */
    
function &singleton($driver$params null)
    {
        static 
$instances;

        if (!isset(
$instances)) {
            
$instances = array();
        }

        if (
is_null($params)) {
            
$params Horde::getDriverConfig('auth'$driver);
        }

        if (
is_array($driver)) {
            
$drivertag implode(':'$driver);
        } else {
            
$drivertag $driver;
        }
        
$signature md5(strtolower($drivertag) . '][' . @implode(']['$params));
        if (!
array_key_exists($signature$instances)) {
            
$instances[$signature] = &Auth::factory($driver$params);
        }

        return 
$instances[$signature];
    }