Skip to content
Advertisement

Can you return another instance of the same class from the constructor function (Singleton Pattern)?

I just came across an answer to another question, https://stackoverflow.com/a/32954854/1682790

In that answer, they use the following:

        private static $singleton;

        # save connection to singleton and return itself (the full object)
        public function __construct() {
          # If your singleton is not set
          if(!isset(self::$singleton)) {
            # assign it this class
            self::$singleton = $this;
          }
          # return this class
          return self::$singleton;
        }

But everything else I’ve seen seems to contradict this section of code, namely that in PHP a constructor always returns the object that’s been instantiated, and ignores the return statement altogether.

If that’s actually the case, then this isn’t actually creating a singleton, and instead is just tricking itself in to thinking that it’s the same object, when it’s actually not.

Or am I missing something?

Is there a way to see a unique ID (or something similar) that’s applied to every object in PHP? To tell for sure what’s happening here.

Advertisement

Answer

PHP constructors do not return anything when used in the normal intended way, so in that regard the code pattern you are attempting won’t work. You can see this with a quick demo showing each invocation getting a new object.

class A {
    private static self $singleton;
    
    public function __construct() {
        if(!isset(self::$singleton)) {
            self::$singleton = $this;
        }
        return self::$singleton;
    }
}

$a = new A;
$b = new A;
$c = new A;

var_dump($a);
var_dump($b);
var_dump($c);

/*
Output:
object(A)#1 (0) {
}
object(A)#2 (0) {
}
object(A)#3 (0) {
}
*/

Demo here: https://3v4l.org/AIH4W

However, per the documentation:

Constructors are ordinary methods…

This means that if you invoke them on an existing instance manually they can return something, although this usage is very strange and unexpected for most people, and I would image/home every static analysis tool and code review would flag this. In the following demo the last version manually invokes the constructor and actually gets the singleton back.

class A {
    private static self $singleton;
    
    public function __construct() {
        if(!isset(self::$singleton)) {
            self::$singleton = $this;
        }
        return self::$singleton;
    }
}

$a = new A;
$b = new A;
$c = new A;

var_dump($a);
var_dump($b);
var_dump($c);
var_dump($a->__construct());

/*
Output:
object(A)#1 (0) {
}
object(A)#2 (0) {
}
object(A)#3 (0) {
}
object(A)#1 (0) {
}
*/

Demo here: https://3v4l.org/dADQb

Using var_dump is one way to see an object’s ID, but you can also use spl_object_id to get to this.

If you want a true singleton, a common pattern is to make the constructor private (just to guarantee that no one invokes it) and to use a getInstance method:

class A {
    private static self $singleton;
    
    private function __construct() {}
    
    public static function getInstance(): self
    {
        if(!isset(self::$singleton)) {
            self::$singleton = new self;
        }
        return self::$singleton;
    }
}

$a = A::getInstance();
$b = A::getInstance();
$c = A::getInstance();

var_dump($a);
var_dump($b);
var_dump($c);

/*
Output:
object(A)#1 (0) {
}
object(A)#1 (0) {
}
object(A)#1 (0) {
}
*/

Demo here: https://3v4l.org/MluojW

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement