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.
CodePudding user response:
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