Search code examples
phpdesign-patternssingleton

Implement a PHP singleton: static class properties or static method variables?


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?


Solution

  • 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.