The Singleton Pattern with PHP

May 25th, 2006 by rvdavid Leave a reply »

The Singleton Pattern is often mistakenly referred to as the OOP nuts “Global variable”.

Admittedly, I myself have been one of those people who had referred to it as such (this is an edited version you are reading), but in fact, the Global Variable tag that has been given to this pattern is actually a side effect – all you need to do is look at the name.

Classes that utilise the Singleton Pattern aims to have a single instance throughout the lifetime of the request/process.

Now that we’ve got it clear of what the Singleton Pattern is designed for, let’s move on.

Don’t let all this scare you, your class can be made into a singleton (in php 4 – although it’s easier with php 5) by adding a single public static method into your Class’ API and a quick check in the constructor.

Take for example, a MySQL Database Abstraction Class.

MySQL Class – I’ll omit any further documentation as this is really for demonstration only

Simple MySQL Class

< ?php
class MySQL
{
var $host = 'yourhostname';
var $username = 'yourusername';
var $password = 'yourpassword';
var $database = 'yourdatabase';

function MySQL()
{
$this->_connect();
$this->_selectDb();
}

function _connect()
{
$this->connId = mysql_connect($this->host, $this->username, $this->password);
if (!$this->connId) {
die("Could not connect to DB - ".mysql_error());
}
}

function disconnect()
{
return mysql_close($this->getConnId());
}

function _selectDb()
{
$selectDb = mysql_select_db($this->database, $this->getConnId());
if (!$selectDb) {
die("Could not select DB - ".mysql_error());
}
}

function &query($sql)
{
$result = mysql_query($sql,$this->getConnId());
if (!$result) {
die(mysql_error().' '.$sql);
} else {
return new MysqlQuery($this, $result);
}
}
}
?>

Implementation

If we were to take the example of applying the Singleton Pattern to the above MySQL Abstraction Class, the implementation can be something to the effect of:

< ?php

class MySQL
{
var $host = 'yourhostname';
var $username = 'yourusername';
var $password = 'yourpassword';
var $database = 'yourdatabase';
var $connId = null;
var $errors = array();

function MySQL($getInstanceKey = false)
{
if (M_E != $getInstanceKey) {
trigger_error('The MySQL object is a Singleton Pattern. Do not instantiate directly');
}
$this->_connect();
$this->_selectDb();
}

function &getInstance()
{
static $instance = false;
if (!$instance) {
$instance[0] =&new MySQL(M_E);
}
return $instance[0];
}

function _connect()
{
$this->connId = mysql_connect($this->host, $this->username, $this->password);
if (!$this->connId) {
die("Could not connect to DB - ".mysql_error());
}
}

function disconnect()
{
return mysql_close($this->getConnId());
}

function _selectDb()
{
$selectDb = mysql_select_db($this->database, $this->getConnId());
if (!$selectDb) {
die("Could not select DB - ".mysql_error());
}
}

function &query($sql)
{
$result = mysql_query($sql,$this->getConnId());
if (!$result) {
die(mysql_error().' '.$sql);
} else {
return new MysqlQuery($this, $result);
}
}
}
?>

The constructor checks for a key which is passed by the get_instance() method upon instantiation. The key can be anything, but I’ve seen the PHP constant M_E used in other examples* and I’m too lazy to think up my own convention.

Usage

$db =& MySQL::get_instance();
$result = $db->query($sql);

A quick word about using the Singleton pattern

The singleton pattern, is in essence, a global variable but not quite. It cannot be changed accidentally. But it is susceptible to abuse. There is nothing stopping you from accessing a singleton class instance from all over your application, and for this reason, if you use it haphazardly, you might start finding calls to it all over your application, turning the well meaning class into a virus.

Till next time.

Regards,

R. Villar David

* Example from PHP|Architects Guide to PHP Design Patterns

if you enjoyed this post, make sure you subscribe to my RSS feed!
You can also follow me on Twitter here.

No related posts.

Advertisement

1 comment

Leave a Reply

Notify me of followup comments via e-mail. You can also subscribe without commenting.