PHP: Lets break and fix “Singleton pattern”

Aizaz Aziz
2 min readJun 25, 2023

--

Photo by Igor Peftiev on Unsplash

You would have found this example of PHP singleton pattern all over the internet

class SingletonClass
{
private static $instance; // Hold the singleton instance

// The constructor is made private to prevent direct instantiation
private function __construct()
{
// Initialization code, if needed
}

// The getInstance method ensures only one instance is created
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}

return self::$instance;
}

// Example method of the Singleton class
public function someMethod()
{
// Code for the method
}
}

That code is the most commonly used to perform singleton. But the question is , is it possible to break the Singleton pattern there?

Inheritance:

class Subclass extends SingletonClass {
public static function getInstance() {
return new Subclass();
}
}

$instance = Subclass::getInstance();

In this example, the Subclass extends the SingletonClass and overrides the getInstance() method to create a new instance of the Subclass instead of the SingletonClass. This breaks the Singleton pattern because multiple instances of the class can now be created.

Prevention:

Finalize the Singleton class: By declaring the Singleton class as final, you prevent any subclasses from overriding the getInstance() method or the Singleton's behavior.

Reflection:

$reflection = new ReflectionClass('SingletonClass');
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);

$instance = $reflection->newInstanceWithoutConstructor();
$constructor->invoke($instance);

In this example, we use PHP’s reflection capabilities to access the private constructor of the SingletonClass. By making the constructor accessible and invoking it directly, we can create a new instance of the SingletonClass even though it is intended to have only one instance.

Prevention:

Use reflection guard: You can add a guard within the getInstance() method to check for reflection attempts and throw an exception if detected.

public static function getInstance()
{
if (self::$instance === null) {
if (class_exists('ReflectionClass')) {
$reflection = new ReflectionClass(__CLASS__);
if (!$reflection->isInstantiable()) {
throw new Exception("SingletonClass cannot be instantiated using reflection.");
}
}
self::$instance = new self();
}
return self::$instance;
}

Other possibilities:

By adding the private __clone(), __wakeup(), and __sleep() methods, you prevent cloning, unserialization, and serialization, respectively. These methods act as safeguards against circumvention through reflection. The __clone() method prevents the creation of a clone, while the __wakeup() and __sleep() methods prevent unserialization and serialization, respectively.

Thank you for you time

If you’re looking for informative and engaging content on backend development,
be sure to follow me today. You won’t want to miss latest articles,
tutorials, and insights on this exciting and dynamic field.

Buy me a coffee

Your support allows me to continue improving and providing helpful responses.

--

--