Posts tagged as:

singleton

The Singleton design pattern creates a class that can only have one instance. This is useful for tasks such as creating a globally available database connection for your script.

The prototype for the singleton class, in PHP, goes like this:

Singleton Pattern

class ClassName {     static $instance = NULL;     private function __construct() {}     private function __clone() {}     static function getInstance() {         if(self::$instance === NULL) self::$instance = new ClassName();         return self::$instance;     }

     $obj = ClassName::getInstance();}

In PHP, variables have scope restrictions that prevent them from being visible in all parts of your program. We often need these variables inside classes and functions, for example. One way to provide globally accessible variables is to pass them into classes or functions as parameters. This method makes it clear that the function of class method is dependent on an external variable or object. Here’s an example:

Option #1: Pass the Variable As A Parameter

Here’s an example of passing a database handle into a hypothetical function and into a class.

function logger($info, $dbhandle);

class Logger {    static $handle = NULL;    static function logIt($info, $dbhandle) {         self::$handle = $dbhandle;    }}

When you have only a few parameters this tactic works fine, but if your functions have many parameters the result is unwieldly code. It is not uncommon to find programmers creating functions or methods that have parameteritis, with dozens of confusing parameters.

Fortunately, a singleton class provides a cleaner solution: it provides global availability of a single object—with the side-effect that the sole object cannot be modified. If we want to ensure that this class cannot be cloned or sub-classed, we will mark it as final and the construct() and clone() methods as protected.

Option #2: Use a Singleton Class

Here’s an example a properly formed singleton class that cannot be cloned or sub-classed, ensuring that there will only be one instance of this class.

final class DbConnection {

    private static $handle = null;    private static $instance = NULL;    private static $db = 'default.db';

    // Deny new DbConnection();    private function __construct($db = NULL) {} 

    // We deny cloning, as with:     //     $instance = DbConnection::connect();    //     $clone = $instance;    private function __clone() {} 

    function connect() {        if(is_null(self::$handle)) {            if(defined('DEFAULT_DB') {                self::$db = DEFAULT_DB;            }            // Create the only possible instance of this class            self::$handle = new SqliteDatabase(self::$db);        }        return self::$handle;    }

    static function getInstance() {        if(self::$instance === NULL) {            self::$instance = new DbConnection();        }        return self::$instance;    }}

This class encapsulates the entire process of creating the single instance and the database handle, and checks to see if a default database constant exists. We can use it this way:

class Logger {    // Notice that this, too, is a singleton pattern class.    static private $logger = NULL;

    static function logIt($it) {        $dbc = DbConnection::getInstance()->connect();        if(self::$instance == NULL) {             self::$instance = new Logger);        }    }

    private function __construct() {}

}

Refactoring

Since the sole function of the DbConnection class is to provide a database connection, we can simplify the code considerably by eliminating the getInstance() method:

final class DbConnection {

    private static $handle = null;    private static $db = 'default.db';

    // Deny new DbConnection();    private function __construct($db = NULL) {} 

    // We deny cloning    private function __clone() {} 

    function connect() {        if(is_null(self::$handle)) {            if(defined('DEFAULT_DB') {                self::$db = DEFAULT_DB;            }            // Create the only possible instance of this class            self::$handle = new SqliteDatabase(self::$db);        }        return self::$handle;    }    

}

class Logger {

    static private $logger = NULL;

    static function logIt($it) {        $dbc = DbConnection::connect();        if(self::$instance == NULL) {             self::$instance = new Logger);        }    }

    private function __construct() {}

}

Happy hacking…

2 comments