Latest update on February 8, 2016 at 05:38 AM by jak58 .

The singleton pattern is one the best-known and easiest to implement design patterns, but it is also one of most misused patterns and will often lead to the introduction of a global state in the system (create dependencies that are difficult to maintain or adapt).

If your system requires only a single instance of a class and you'd like to make the selected instance accessible system-wide, you may do so by making the class into a singleton. This guide will teach you how to implement a singleton class in PHP, and also how to overcome the limitations posed by the singleton pattern.

The easiest way to implement a singleton class in PHP is to change the visibility of the constructor class (to prevent instantiation outside of the class) and then create the instance within a static method. Here's a simple example:

This type of implementation has a major drawback. The use of a static method will make the system vulnerable to external attacks (e.g. unauthorized access to the data of a database through a simple SQL query). Instead of using a static method, we are going to use factory pattern to return the desired instance of the class. A result of using the factory pattern is that the constructor class becomes public. It enables the creation multiple instances of the singleton class, which kills the purpose of using a singleton pattern (single instance required).

Fortunately, PHP 7 provides a simple way to stop the creation of multiple instances. You simply have to create an anonymous class within the factory pattern:

instance === null) { $this->instance = new class() { private $var; public function get() { return $this->var; } public function set($value) { $this->var = $value; } }; } return $this->instance; } } $factory = new Factory(); $o = $factory->getInstance(); $o->set(5); $o2 = $factory->getInstance(); // Affiche "int(5)" var_dump($o2->get());

N.B. Type hinting is disabled when using an anonymous class of the object(class@anonymous) type. If you would like to enable type hinting, you simply have to implement an interface in the code:

instance === null) { $this->instance = new class() implements MyInterface { private $var; public function get(): int { return $this->var; } public function set(int $value) { $this->var = $value; } }; } return $this->instance; } } function display(MyInterface $object) { var_dump($object->get()); } $factory = new Factory(); $o = $factory->getInstance(); $o->set(5); $o2 = $factory->getInstance(); display($o2);

The introduction of an anonymous class will lead to another problem. It is now possible to instantiate several factories and therefore create several instances of the singleton class. Anonymous classes will differ from one instance to another, and furthermore, the use of the static method will make it difficult to identify new instances.

To prevent our class from being instantiated multiple times and keep our instance safe within the factory pattern, we'll need to edit our code to:

instance === null) { $this->instance = new class() { private static $instantiated = false; public function __construct() { if (self::$instantiated) { throw new RuntimeException('Could not instantiate class'); } self::$instantiated = true; } private $var; public function get(): int { return $this->var; } public function set(int $value) { $this->var = $value; } }; } return $this->instance; } } $factory = new Factory(); $o = $factory->getInstance(); $o->set(5); $factory2 = new Factory(); $o2 = $factory->getInstance(); var_dump($o2->get());

This will put a stop to the creation of multiple instances of our singleton class, which in turn will make the anonymous class obsolete. We can now edit our code to:

var; } public function set(int $value) { $this->var = $value; } } class Factory { private $instance = null; public function getInstance() { if ($this->instance === null) { $this->instance = new Singleton(); } return $this->instance; } } $factory = new Factory(); $o = $factory->getInstance(); $o->set(5); $factory2 = new Factory(); // Generates the message: "Could not instantiate class" $o2 = $factory2->getInstance();

The method described above has a major limitation. Once the class has been instantiated (an object has been created), you won't be able to test it. But fortunately, it can be solved through the implementation of a resetInstance() static method, which will allow you to create new instances at will.

Tags:
Tomasz David
Tomasz David

Leave a Comment