13

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?

7
  • The second approach can also be implemented as a normal function, I use this sometimes, for instance i()->DB(). Commented Jul 13, 2010 at 14:15
  • @Alix: I don't follow. How could the getInstance function be implemented non-statically and still pertain to singletons? Commented Jul 13, 2010 at 14:42
  • 1
    @Austin Hyde: He is referring to a regular function, not a method. However, it would violate the rule of the singleton that there should only ever be one instance because it would require a public constructor. All it would be is a convenience function for not having to instantiate the object each time. Commented Jul 13, 2010 at 14:44
  • 1
    @ircmaxell: Ah. That makes more sense. Thanks for the clarification. Commented Jul 13, 2010 at 14:46
  • @ircmaxell: Yes, but with a "normal" singleton we can also violate the same rule, can't we? Commented Jul 13, 2010 at 19:50

4 Answers 4

12

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

Sign up to request clarification or add additional context in comments.

1 Comment

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

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.

7 Comments

Yeah, forgot you can't do can't complex static assignments... But I don't follow: what is a zval?
Ok, that makes more sense. But in PHP 5, don't objects get passed by reference by default?
@Austin It's only the reference that's separated. All the theree references in the example point to the same object (object #1).
@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 yes, there's an extra data structure copied, but the performance penalty should be very small.
|
1

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(isset(self::$storage[$class]))
        {
            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 function.
    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

10 Comments

Good idea, something I hadn't thought about. However, this really doesn't answer my question.
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 :)
This doesn't create a singleton because you can still call 'new Database();'
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]).
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?
|
0

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/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.