Design Patterns – Singletons in PHP 5.x

Design Patterns (and anti-patterns) are techniques of programming. There are a lot of design patterns. It is upto the programmer to pick and choose which patterns to use based on their needs. Singleton is also a design pattern. A singleton is a type of class which can have only 1 object of its kind through the lifecycle of an application. It is very suitable for database, caching, etc. I will use a database class as an example as it is probably the most-widely used class in web development.

Why use a singleton you ask? A popular practice among PHP4’s developers is to make a database object global then use that variable anything a query needs to be run. It works in small applications and even in not-so-big applications. In enterprise applications where you use hundreds (if not thousands) of variables, there is a very good chance that you database object will get mutated or overridden. You also don’t want to have multiple instance of a database class (unless you are running a multi-threaded application).

A database class should be able to connect to a database, run queries, return results and close the connection to the database when it’s done. Keeping that in mind, let’s start writing our database class.

/**
 * Database wrapper
 */
class Database {
	private $_host;
	private $_user;
	private $_pass;
	private $_name;
	private $_conn;
	private $_err;

	/**
	 * Setting the constructor as private will make sure
	 * that the class cannot be instantiated using the
	 * "new" keyword. You can to instantiate it like this :
	 * $db = Database::getInstance();
	 * getInstance() is implemented right after the constructor
	 */
	private function __construct($host, $user, $pass, $name)
	{
		$this->_host = $host;
		$this->_user = $user;
		$this->_pass = $pass;
		$this->_name = $name;
	}

	/**
	 * This function is what makes this whole class a singleton.
	 * It will make sure only one instance of this class is available
	 */
	function getInstance($host='', $user='', $pass='', $name='')
	{
		static $obj;
		if (null == $obj) {
			$obj = new Database($host, $user, $pass, $name);
			$obj->connect();
		}
		return obj;
	}
	/**
	 * Connects to the database
	 */
	function connect()
	{
		$this->_err = null;
		if (!mysql_connect($this->_host, $this->_user, $this->_pass)) {
			$this->_err = mysql_error();
			return false;
		}
		if (!mysql_select_db($this->_name)) {
			$this->_err = mysql_error();
			return false;
		}
		return true;
	}

	function error()
	{
		return $this->_err;
	}

	function user($val=null)
	{
		if (null == $val) return $this->_user;
		$this->_user = $val;
	}
	function host($val=null)
	{
		if (null == $val) return $this->_host;
		$this->_host = $val;
	}
	function pass($val=null)
	{
		if (null == $val) return $this->_pass;
		$this->_pass = $val;
	}
	function name($val=null)
	{
		if (null == $val) return $this->_name;
		$this->_name = $val;
	}
}

So, we wrote a class called database and made its constructor private so you can’t instantiate it using the “new” keyword. We also wrote a method/function for this class called getInstance() which will return a database object. getInstance() has a local static variable called $obj. It checks if this variable is null. If it’s null then that means there is no object of the type “Database”. So, it creates an object and assigns it to $obj. It also returns the object to the code that called it. Setting permissions of private, protected and public could not have been done in PHP 4, which frustrated a lot of OOP enthusiasts. PHP 5, however, has the capability of doing it and thus fulfilling one of OOP’s main goals – encapsulation.

The first time  an object of the class Database is instantiated, it will have to pass all the variables required to make a database connection. Its subsequent calls need not do that.

// First call
$db = Database::getInstance('localhost', 'root', '', 'myDB');

function hello()
{
	// Second call
	$database = Database::getInstance();
}

Both $db and $database point to the same object. Any changes made to $database will be reflected in $db also. This eliminates the use of global variables for quite a few things (especially our database class).

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *