5

I'm having some trouble with PHP Inheritance. Here's deal:

I have this base class, Singleton:

namespace My_Namespace;

abstract class Singleton {
    protected static $instance = null;

    static function get() {
        if ( null == static::$instance ) {
            static::$instance = new static;
        }
        return static::$instance;
    }

    private function __construct() {

    }
}

I have a bunch of classes inheriting that Singleton class, call them A,B,C,D. One of them looks like this:

namespace My_Namespace;

class A extends Singleton {

    protected function __construct() {

        B::get();

        if ( some_condition() ) {
            C::get();
        }
        else {
            D::get();
        }
    }
}

Now, I just do a A::get() to get it all rolling. The constructor of A is called, as expected. Then, the constructor of B is called, again without a problem. Now it gets weird. Once C::get() is called, it recognizes static::$instance as already an object of class B and doesn't instantiate C at all. I know if I kinda daisy-chain them, that is __construct of B calls C::get or D::get it works but that's not optimal for my purposes. Is that caused by them being in the same scope? If so, is there any way around this? I'm asking this more of curiosity rather than practical purpose - I know I can just as easily implement the singleton pattern in each one of them. So, any ideas? Thanks!

P.S. Please no 'singletons are evil and you should burn in hell' comments. I know that perfectly well.

4
  • 1
    +1 for no ... burn in hell comments Commented Jun 10, 2013 at 20:45
  • 1
    I believe the inherited classes need a static property for the instance to be shoved into ... so add protected static $instance = null; to the subclasses. Commented Jun 10, 2013 at 20:45
  • @Orangepill you got it. I added that line and it all works as expected. BUT: That kinda makes the whole Singleton class and inheritance stuff useless though... The idea was to have the singleton functionality enclosed in that class. Is that possible at all? Commented Jun 10, 2013 at 20:48
  • 1
    Nope... if you don't have a property to tuck it into it's going to fall back to inherited property, which in a static sense is the base classes class property. :( Commented Jun 10, 2013 at 20:49

2 Answers 2

2

Note that static::$instance = new static calls the constructor of (in your case) A.

With your solution, you will need a static property for your instance in your subclasses.

Just add

protected static $instance = null;

to them, and it should work fine.

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

4 Comments

Why does it work for the first two times though? How come if I'm not in the same scope, as in A::get() and B::get() it creates the appropriate instances but as soon as it hits C::get() it falls back to the one it used for B::get? Is that making any sense?
It doesn't work for the first two times, it works for the first time. You call A::get(). Then the constructor of A is called. That calls B::get(), than the $instance from the base class is set (first time that it is set). And now you want to call C::get(), that checks null == static::$instance, but now the $instance (from class A) is already an object.
Took me a while to realize how easy the explanation is. At that static::$instance = new static, the constructor is called which calls another constructor, all before assigning anything to the actual $instance variable cause it hasn't return-ed yet. Silly me. Thanks for the answer.
No problem, btw: maybe you should also put final private function __clone() { } to your singleton, to prevent object cloning
1

When dealing with static properties if you want the inherited classes's static properties to differ from the base classes you have to provide a home for it to live in.

To solve the problem just define

protected static $instance = null;

on your derived class. If not it will use the base class' property.

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.