I have opinions

Design Pattern I: Singleton

In this first episode of PHP design patterns, we will be looking at the Singleton. The Singleton is available in most, if not all, OO languages. The purpose of the Singleton is to only ever have 1 instance of a class available which supplies the rest of the application with consistent data. I would like to point out that my version of a Singleton may differ slightly from other peoples; design patterns are not exact nor are they set in stone.

Due to its consistency, the Singleton design pattern is great for classes such as user information, site details, or site settings whereby it is needed all over the system, not just in a single place.

To start we need to define our class, and create the static property which will hold the class instance, and create the repository for all our data. This static variable will remain the same over the course of the runtime of the script, allowing us to access our consistent data.


Here we have defined our class, created the static variable, and defined the constructor method. Several things may hit you when you view this snippet, first being that we have an underscore after the dollar sign; this is to show that it is a class attribute. Also, we have made out constructor private and given it no logic. This is because we do not want to create an instances of the class, we instead want to return an instance of the class. We do this by adding another function called getSingleton() which will set the class and return it. Let’s see what that will look like.


We first test to see whether or not we have created an instance of the Singleton before, if we haven’t then we create it, and then return the instance. We use self::$_Singleton because we are dealing with a static method. The only thing left to do now is to set the data and allow our script to obtain that data.

 'Woof!',
                                 'Cat' => 'Meow!',
                                 'Cow' => 'Mooo!');
 
    private function __construct(){}
 
    public function getSingleton() {
        if (is_null(self::$_Singleton)) {
            self::$_Singleton = new Singleton();
        }
 
        return self::$_Singleton;
    }
 
    public function retrieve($index)
    {
        if (array_key_exists($index, $this->_repository)) {
            return $this->_repository[$index];
        }
    }
}

And there we have it, our lovely Singleton design pattern. I have decided for this example for simplicity sake to set some values to play with, in a live application you might query a database or get the values from a file out of the document root. Within the retrieve() function we first make sure that the index exists within our repository; if it does then it returns the value. Since the Singleton class should contain set values and be called from other classes, the array index should always exist—you might even want to put some error handling on the function to log anything that it can’t find. Lets look at am example of this in action.

 'Woof!',
                                 'Cat' => 'Meow!',
                                 'Cow' => 'Mooo!');
 
    private function __construct(){}
 
    public function getSingleton() {
        if (is_null(self::$_Singleton)) {
            self::$_Singleton = new Singleton();
        }
 
        return self::$_Singleton;
    }
 
    public function retrieve($index) {
        if (array_key_exists($index, $this->_repository)) {
            return $this->_repository[$index];
        }
    }
 
    public function changeCat() {
        $this->_repository['Cat'] = 'Baaa!';
    }
}
 
$eg1 = Singleton::getSingleton();
echo 'Singlton 1: ' . $eg1->retrieve('Cat') . '
'; $eg1->changeCat(); $eg2 = Singleton::getSingleton(); echo 'Singlton 1: ' . $eg1->retrieve('Cat') . '
'; echo 'Singlton 2: ' . $eg2->retrieve('Cat');

This should hopefully produce the following:

Singlton 1: Meow!
Singlton 1: Baaa!
Singlton 2: Baaa!

In real life you wouldn’t change the values of the data stored in a singleton (as that would then become more a Registry pattern, which I will look at next), however, this is just to show you that the data is the same for all instances created.