views:

135

answers:

4

So, I've always implemented a singleton like so:

class Singleton {
    private static $_instance = null;
    public static function getInstance() {
        if (self::$_instance === null) self::$_instance = new Singleton();
        return self::$_instance;
    }
    private function __construct() { }
}

However, it recently struck me that I could also implement it with member-wise static variables:

class Singleton {
    public static function getInstance() {
        //oops - can't assign expression here!
        static $instance = null; // = new Singleton();
        if ($instance === null) $instance = new Singleton();
        return $instance;
    }
    private function __construct() { }
}

To me, this is cleaner because it doesn't clutter the class, and I don't have to do any explicit existence check, but because I've never seen this implementation anywhere else, I'm wondering:

Is there anything wrong with using the second implementation over the first?

+5  A: 

You probably mean it with a slight modification (I got a syntax error otherwise):

<?php
class Singleton {
    public static function getInstance() {
        static $instance;
        if ($instance === null)
            $instance = new Singleton();
        xdebug_debug_zval('instance');
        return $instance;
    }
    private function __construct() { }
}
$a = Singleton::getInstance();
xdebug_debug_zval('a');
$b = Singleton::getInstance();
xdebug_debug_zval('b');

This gives:

instance: (refcount=2, is_ref=1), object(Singleton)[1]

a: (refcount=1, is_ref=0), object(Singleton)[1]

instance: (refcount=2, is_ref=1), object(Singleton)[1]

b: (refcount=1, is_ref=0), object(Singleton)[1]

So it has the disadvantage a new zval will be created on each call. This is not particularly serious, so if you prefer it, go ahead.

The reason a zval separation is forced is that inside getInstance, $instance is a reference (in the sense of =&, and it has reference count 2 (one for the symbol inside the method, another for the static storage). Since getInstance doesn't return by reference, the zval must be separated -- for the return, a new one is created with reference count 1 and the reference flag clear.

Artefacto
Yeah, forgot you can't do can't complex static assignments... But I don't follow: what is a zval?
Austin Hyde
@Austin See http://php.net/manual/en/features.gc.refcounting-basics.php
Artefacto
Ok, that makes more sense. But in PHP 5, don't objects get passed by reference by default?
Austin Hyde
@Austin It's only the reference that's separated. All the theree references in the example point to the same object (object #1).
Artefacto
@Austin Objects are not passed at all. References to them are passed around. It's a different thing a reference being passed by value and an object being passed by reference.
Artefacto
@Austin: No! Objects aren't passed by reference, neither in PHP 4 nor in PHP 5. They have only a reference like behavior. More reading: <http://saragolemon.blogspot.com/2007/01/youre-being-lied-to.html>
nikic
@nikic: Thanks, that link helped explain a lot. @Artefacto: To sum it up, there's nothing wrong with using method variables over class properties other than a little extra behind-the-scenes baggage?
Austin Hyde
@Austin yes, there's an extra data structure copied, but the performance penalty should be very small.
Artefacto
A: 

After some playing around the best method I can think of is like so:

Create a file called SingletonBase.php and include it in root of your script!

The code is

abstract class SingletonBase
{
    private static $storage = array();

    public static function Singleton($class)
    {
        if(in_array($class,self::$storage))
        {
            return self::$storage[$class];
        }
        return self::$storage[$class] = new $class();
    }
    public static function storage()
    {
       return self::$storage;
    }
}

Then for any class you want to make a singleton just add this small single method.

public static function Singleton()
{
    return SingletonBase::Singleton(get_class());
}

Here is a small example:

include 'libraries/SingletonBase.resource.php';

class Database
{
    //Add that singleton funtion.
    public static function Singleton()
    {
        return SingletonBase::Singleton(get_class());
    }

    public function run()
    {
        echo 'running...';
    }
}

$Database = Database::Singleton();

$Database->run();

And you can just add this singleton function in any class you have and it will only create 1 instance per class.

Just another idea you can also do

if(class_exists('Database'))
{
   $Database = SingletonBase::Singlton('Database');
}

and at the end of your script you can do some dfebugging if you need too,

at the end of your script you can just do

foreach(SingletonBase::storage () as $name => $object)
{
     if(method_exists("debugInfo",$object))
     {
         debug_object($name,$object,$object->debugInfo());
     }
}

so this method will be great for a debugger to get access to all classes and object states that have been initialized

RobertPitt
Good idea, something I hadn't thought about. However, this really doesn't answer my question.
Austin Hyde
your question was answered by many other people in regards with that you cant call it a singleton because your creating a new instance every time, My code was to show you how you can be clean, small amount of coding, and acts as a true singleton, witch in fact it does answer parts of your question :)
RobertPitt
This doesn't create a singleton because you can still call 'new Database();'
Scott Saunders
Are you sure this will work? The `in_array` seems wrong. `in_array` checks for the values, not the keys. So, maybe you meant to write `isset(self::$storage[$class])`.
nikic
Maybe better create an `abstract class Singleton`, which has `private function __clone` and `protected __construct` and a method `public static getInstance` which has the same functionality as your `Singleton` method but instead returns the instance, based on `get_called_class`?
nikic
it works and tested :) `PHP 5.3`, and if you notice the `return self::$storage[$class] = new $class();` its actually storing the class name as the key.
RobertPitt
@RobertPitt: My code doesn't create new instances. A `static` declaration is only initialized, once: at compile time. And my question wasn't answered by "many other people". It was (not) answered by you. It was (partially incorrectly) answered by @Gordon. It **was**, however, answered by @Artefacto.
Austin Hyde
No, I don't think it works. It'll create a new Instance every time.
nikic
@Scott Saunders, you are right, i totally missed that, eoither way its still a nice way to handle objects. and there's less code and all your objects are in 1 single storage.
RobertPitt
@Scott Saunders, You can just make the __construct a private! this will resolve the issue with new XXX()
RobertPitt
+3  A: 

Go with a class property. There are a few advantages...

class Foo {
    protected static $instance = null;

    public static function instance() {
        if (is_null(self::$instance)) {
            self::$instance = new Foo();
        }
        return self::$instance;
    }
}

First, it's easier to perform automated tests. You can create a mock foo class to "replace" the instance so that other classes which depend on foo will get a copy of the mock instead of the original:

class MockFoo extends Foo {
    public static function initialize() {
        self::$instance = new MockFoo();
    }
    public static function deinitialize() {
        self::$instance = null;
    }
}

Then, in your test cases (assuming phpunit):

protected function setUp() {
    MockFoo::initialize();
}

protected function tearDown() {
    MockFoo::deinitialize();
}

This gets around a common gripe with singletons that they are hard to test.

Second, it makes your code more flexible. If you ever want to "replace" the functionality at run time in that class, all you need to do is subclass it and replace self::$instance.

Third, it allows you to operate on the instance in other static function. This isn't a huge deal for single instance classes (a true singleton) since you can just call self::instance(). But if you have multiple "named" copies (say for database connections or other resources where you want more than one, but don't want to create a new one if they already exist), it becomes dirty because you then need to keep track of the names:

protected static $instances = array();

public static function instance($name) {
    if (!isset(self::$instances[$name])) {
        self::$instances[$name] = new Foo($name);
    }
    return self::$instances[$name];
}

public static function operateOnInstances() {
    foreach (self::$instances as $name => $instance) {
        //Do Something Here
    }
}

One other note, I wouldn't make the constructor private. It will make it impossible to extend or test properly. Instead, make it protected so that you can sub-class if needed and still operate on the parent...

ircmaxell
You make some good points. Thanks!
Austin Hyde
Why o why did you make `instance` return by reference. This allows you to externally replace the singleton instance, which you shouldn't be able to. I'd upvote you otherwise.
Artefacto
@Artefacto: good point. Fixed...
ircmaxell
A: 

The cleanest solution is to remove the singleton logic from the class itself (because it's something not related to the job of the class itself).

For an interesting implementation see this: http://phpgoodness.wordpress.com/2010/07/21/singleton-and-multiton-with-a-different-approach/

gphilip