The singleton pattern applies to the situation where you need a single, global instance of a class. It fits situations where a factory object returns uniform objects, such as file handles or user sessions. These aren't good candidates for being implemented in a PHP script because the language provides built-in solutions such as persistent database connections.
((
{{design:singleton.zip|Sample Singleton Code}}
))
The fact that it's a **global** instance though should warn you that a Singleton should be used wisely (for essentially the same reasons as global variables). You have been warned.
The easiest way to understand a singleton pattern is by example. But first we have to overcome a small problem in PHP - the lack of static class variables (something that's coming in the [[http://www.zend.com/engine2/ZendEngine-2.0.pdf|Zend 2 engine]]. The good news is PHP functions can have static variables, like so;
// A generic function to create and fetch static objects
function staticInstance($class) {
// Declare a static variable to hold the object instance
static $instance;
// If the instance is not there, create one
if(!isset($instance)) {
$instance =& new $class;
}
return($instance);
}
We can now call that function from anywhere (assuming it's included somewhere) and fetch the static instance of whatever class we gave it.
**Note:** There are alternatives - try [[http://www.webkreator.com/php/techniques/php-static-class-variables.html|PHP Static Class Variables]]
Now we've overcome that problem, let's put the Singleton pattern to good use. In [[develop:php_and_dom_the_way_of_the_widget|PHP and DOM: The Way of the Widget]] we looked at building XHTML "widgets" with the DOM extension. One thing you may have noticed in that article is we had to pass each widget an instance of the DOM API we'd constructed elsewhere, which can become cumbersome.
As such, DOM makes a prime candidate for a Singleton. To use it this way we need a function like;
// A function to create static instances of the DOM API
function staticDom($type=null,$source=null) {
// Declare a static variable to hold the dom object
static $dom;
// If the instance is not there, create one
if(!isset($dom)) {
// Deal with the possible ways DOM can be constructed
switch ( $type ) {
case "file":
$dom=domxml_open_file($source); // $source: path to file
break;
case "mem":
$dom=domxml_open_mem($source); // $sounce: XML as string
break;
default:
$dom=domxml_new_doc('1.0'); // create a new one
break;
}
}
return($dom);
}
OK - now let's re-create the LinkWidget class from the [[develop:php_and_dom_the_way_of_the_widget|PHP and DOM article]], this time with a better design;
dom=& staticDom(); // Construct DOM from static instance
$this->out=$out;
$this->createLink();
}
//! An accessor
/**
* Create an <a /> tag
* @return void
*/
function createLink () {
$this->link=$this->dom->create_element('a');
}
//! An accessor
/**
* Adds an XML attribute e.g. href="http://www.php.net"
* @return void
*/
function addAttribute($name,$value) {
$this->link->set_attribute($name,$value);
}
//! An accessor
/**
* Creates the text for the link
* @return void
*/
function addText($text) {
$text=$this->dom->create_text_node($text);
$this->link->append_child($text);
}
//! An accessor
/**
* Returns either XML as a string or a DOM object
* @return mixed
*/
function &fetch () {
if ( $this->out=='string') {
return $this->dom->dump_node ($this->link);
} else {
return $this->link;
}
}
}
?>
Notice this line in the constructor?
$this->dom=& staticDom(); // Construct DOM from static instance
To see this in action, let's compare calling the LinkWidget in two ways.
addAttribute('href','http://www.php.net');
$lw->addAttribute('style','{background-color: yellow; color: navy}');
$lw->addText('Powered by PHP');
echo ($lw->fetch());
?>
In the above code, the LinkWidget is the cause for the DOM object to be created - no need to instaniate it from our calling script.
The result?
Powered by PHP
Now how about this;
addAttribute('href','http://www.php.net');
$lw->addAttribute('style','{background-color: yellow; color: navy}');
$lw->addText('Powered by PHP');
$dom->append_child($lw->fetch());
echo ($dom->dump_mem());
?>
This time we created the DOM object before we instantiated the widget, giving us the following output;
Powered by PHP
Again - use with caution...
======Update======
[20 August 2003]
For an example of implementing a Singleton in PHP5, try [[http://www.sitepoint.com/article/1192/9|here]]