You are herePHP Passing By Value Killed The Singleton

PHP Passing By Value Killed The Singleton


By Gerd Riesselmann - Posted on 01 February 2005

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.

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. ;)

Thanks, Ben! One should test code before publishing it :-).

You're absolutely right about the missing of a private constructor. Very good point!

Singletons in PHP
The singleton pattern comes in quite handy for doing away with the evils of globals in PHP.

I use it a fair bit for global config access using a config class I have written, which I will post here at some point too.

[php]class Config
{
func...

It's an easy mistake to forget the &, and one I've made on a number of occasions. It can also be a very difficult bug to track down on occasion.

PHP5 should sort this out I think. I have to confess that I haven't taken, or had, the time to looking into PHP5 much as yet.