Singletons in PHP

If you’ve heard of design patterns you’ll have probably heard of the singleton pattern. It’s debatably the easiest to start with and get you on the road to becoming the Walter White of design patterns. Below I will explain what a singleton is, when you should use a singleton, rules that must be followed, drawbacks and I will provide sample code. Lets get started.

What is a singleton

As the name sort of implies, a singleton is a class that can only be initialised once per request to an application. E.g once per HTTP request on a website.

When to use a singleton

The ideal use for a singleton is when you only need one instance of a particular class in your application. The most common use for this in small to mid sized application is probably for creating a database connection. In such an application it would be very rare that you would need to connect to two separate databases. You would also want to make sure that it’s not possible to open multiple connections to the same database, because this would be inefficient.

Rules when creating a singleton

  1. As a singleton should only be initialised once, you have to prevent multiple instantiations, this is done my making the _construct() method private, this will stop the singleton class from being initialised with the new keyword.
  2. Initialising must be done by creating a public static method (typically called getInstance() or singleton()) which you can use to create an instance of itself.
  3. When an instance is created of itself, that instance must be stored inside a static attribute.
  4. On the second call to the static method instead of creating an instance to itself again, it will return the object it created the first time around. This way the class can only be initialised once.
  5. Lastly the Singleton must have a private _clone() method to prevent one instance of the Singleton being cloned using the PHP clone keyword.

Drawbacks

  1. Typically singletons are used in the global state, which means they are called within other classes (e.g. via ClassName::getInstance()), this adds dependencies to your system. This can be countered by passing around the singleton instance, however it does not follow the pattern goals.
  2. Singletons are extremely hard to test because static methods cannot be stubbed and a static method is used to initialise and retrieve the class instance. Last year I wrote an article on testing singleton dependencies.
  3. Singletons violate the single responsibility principle by controlling their own life cycle, i.e they create themselves.< \li>

Code Example

class Database {

    /**
     * Used to store and provide instance for the getInstance() method
     */ 
    private static $instance;

    /**
     * Hidden constructor, only callable from within this class
     */
    private function __construct() { }

    /**
     * Create an instance if it hasn't been already
     * Then return the instance of this class
     * @return self
     */
    public static function getInstance() {

        if(!self::$instance) {
            // instance doesn't exist yet, so create it
            self::$instance = new self();
        }

        // return an instance of this class (Database)
        return self::$instance;
    }

    /**
     * Hidden magic clone method, make sure an instance this class 
     * cannot be cloned using the clone keyword
     */
    private function __clone() { }
    
}

// Making two calls to Database::getInstance(); return the same object
$db = Database::getInstance();
var_dump($db); // object(Database)#1 (0) { }

$db2 = Database::getInstance();
var_dump($db2); // object(Database)#1 (0) { }

// Database cannot be initialise with the new keyword and will create a fatal error
// Fatal error: Call to private Database::__construct() from invalid context 
// in singleton.php on line 44
$db_fatal_error = new Database();
18 Love This

Leave a Reply

Your email address will not be published.