2

I have a main class with the singleton function instance() and the associated variable $instance. Now I create several subclasses and let the main class inherit. I do not re-define the singleton function and variable, because of the useful inheritance. Unfortunately, each instance points to the 1st subclass. Only when in the subclasses the $instance variable is initialized to null it works, but why? With the keywords static and not self, the scope should remain in the subclass.

Here is the source code for a better understanding of what I mean:

// PHP Version 7.0
// Don't work as expected:

class base1
{
    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null;

    /*
     * For Singleton Pattern
     */
    public static function instance() {

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

        return static::$instance;
    }

    public function test()
    {
        $test = "base1";
        var_dump($test);
    }
}

class sub11 extends base1
{
    public function test()
    {
        $test = "base1 -> sub11";
        var_dump($test);
    }
}

class sub12 extends base1
{
    public function test()
    {
        $test = "base1 -> sub12";
        var_dump($test);
    }
}

$sub11 = sub11::instance();
$sub12 = sub12::instance();
$sub11->test();
$sub12->test();
// Output:
// string(14) "base1 -> sub11"
// string(14) "base1 -> sub11"  // It's not different!!!

// Work as expected:

class sub21 extends base1
{

    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null;  // Is needed, but why?

    public function test()
    {
        $test = "base1 -> sub21";
        var_dump($test);
    }
}

class sub22 extends base1
{
    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null; // Is needed, but why?

    public function test()
    {
        $test = "base1 -> sub22";
        var_dump($test);
    }
}

$sub21 = sub21::instance();
$sub22 = sub22::instance();
$sub21->test();
$sub22->test();
// Output:
// string(14) "base1 -> sub21"
// string(14) "base1 -> sub22"  // This is correct !

1 Answer 1

2

In your first part it is working correct and as expected. Both classes sub11 and sub12 using base1's field to store instance. So, the first one, which was instatiated, is placed there and preventing others to overwrite already created instance.

In second part, you specified personal static storage field for each descendant classes, so they uses not base class field, but own field instead (it overlaps parent field, as uses same name).

In a short, first pair of descendant classes uses base1::$instance to check and create instance. Second pair uses own fields, sub21::$instance and sub22::$instance for this task.

You can prevent this by discarding late static binding in base1 class:

class base1
{
    /*
    * Instance of class
    * mixed
    */
    protected static $instance = null;

    /*
     * For Singleton Pattern
     */
    public static function instance() {
        if ( null == self::$instance ) {
        //           ^ self instead of static
            self::$instance = new static();
        //  ^ self instead of static
        }

        return self::$instance;
        //     ^ self instead of static
    }

    public function test()
    {
        $test = "base1";
        var_dump($test);
    }
}

I do really suggest you to read about late static binding and difference between self and static keywords.

UPDv1:

If you still requiring to get only one instance of each descendant class, you can change singleton method to something like this:

class base1
{
    /*
    * Instances of descendant classes
    * array
    */
    protected static $instances = [];

    /*
     * For Singleton Pattern
     */
    public static function instance() {
        if (empty(self::$instances[static::class])) {
            $instance = new static();

            self::$instances[static::class] = $instance;
        } else {
            $instance = self::$instances[static::class];
        }

        return $instance;
    }

    public function test()
    {
        $test = "base1";
        var_dump($test);
    }
}
Sign up to request clarification or add additional context in comments.

10 Comments

I thought the functions and variables of the basic class are inherited and are thus independent. For this, the keywords static (to remain in the inherited class) and self (to remain in the base class).
If I do your example my code don't work as expected: 1. Output: string(14) "base1 -> sub11", string(14) "base1 -> sub11". 2. Output: string(14) "base1 -> sub11", string(14) "base1 -> sub11"
@AndreasRex well, they are inherited and they are accessible, but not independent. You directly requiring to overlap them by own descendant class property with same name. In this case, as you using late static binding, the will look not into a parent field, but in it's own field. Read the link. I'm just clarifying for you how it behaves, so you could understand whats happening there.
@AndreasRex also, I've answered your Is needed, but why? question.
self references the called class using class name in the last non-forwarding call while static references the class its used in. This way each subclass accesses it's own $instance property not that of the parent class.
|

Your Answer

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