PHP Passing By Value Killed The Singleton
It is a common misunderstanding of a singleton beeing a global variable expressed in OO style. This is not quite correct. Well, of course a singleton is global, but there's more: A singleton implements access control to the one and only instance of a class. That's why it's called singleton. But due to PHP's passing by value policy, this can easily be underminded.
A common implementation for a singleton, like found here, or - correctly using a reference return value - here, looks like this:
function &singleton()
{
static $instance;
if (!isset($instance))
$instance = new SingletonClass();
return $instance;
}
//usage:
$a =& singleton();
However, every client can easily make a copy of the singleton instance, and therefore undermine what a singleton is for, ommiting the reference operator "&":
$a = singleton();
$b = singleton();
$a and $b now point to two different instances of SingletonClass, whom both are different to the singleton instance. Oops! A singleton indeed should prevent the user from doing this. And this, however, is impossible in PHP.
Update: Ben pointed out a bug in assigning the static variable. Fixed this.
Comment by Ben
February 2, 2005 - 21:45
Ah, references in PHP4... the nightmare of anyone trying to do some advanced OOP in that language. Been there, done that, and stumbled upon pitfalls like this, so let me add some minor points:
1. Gerd, your example code returns copies of the SingletonClass object anytime the the singleton() function gets called. Whether you use the reference operator or not outside the function isn't going to make a difference. This code illustrates the issue:
class Person { var $name; function Person($name) { $this-$gt;name = $name; } } function &singleton() { static $instance; if (!isset($instance)) { print "instance not setn"; $instance =& new Person('Bob'); } return $instance; } $a =& singleton(); var_dump($a->name); $b =& singleton(); $b->name = 'Bill'; var_dump($a->name);The reason? You are assigning a reference to a static function variable - but that does not work, a reference is not saved in a static variable once the function returns. Quite a non-intuitive glitch in the language, and curiously enough, PHP doesn't warn you about it. Richard Livsey got that part right, if you want to store an object in a static variable, do it by value, not by reference.
2. The singleton pattern would demand that an object must not be instantiated from outside by invoking the constructor. Typically in Java etc. the constructor gets a "private" visibility. In PHP4, you can't prevent users from directly instantiating the object and bypass the singleton completely.
3. PHP5 offers us more features in the language itself to implement a (fairly?) robust Singleton implementation. Example:
class Person { public $name; private function __construct($name) { $this->name = $name; } public static function &singleton() { static $instance; if (!isset($instance)) { $instance = new Person('Bob'); } return $instance; } } $a = Person::singleton(); var_dump($a->name); $b = Person::singleton(); $b-$gt;name = 'Bill'; var_dump($a->name);I say "fairly" because my experience with PHP5 isn't that deep either, so I can only suspect that this version brings along its own inconsistencies and ambiguieties. ;)