How to avoid many instances of Singleton pattern in PHP?
How to avoid many instances of Singleton pattern in PHP?

Problem:

While implementing Singleton pattern in our PHP classes, sometimes people forgot or even doesn’t know, that there are some methods, whoose give us possibility to break this pattern by creating another instances. I will show you how to avoid it in some cases (why not all cases? the answer is on the bottom of article)

Let’s see an example of typical singleton class :

class Singleton 
{
  private static $instance;
  
  public static function getInstance() 
  {
    if(!self::$instance) {
      self::$instance = new self;	
    }
    
    return self::$instance;
  }
}

 Solution 1: Public constructor

Not secured: It seems to be fine, but when we’ll use new operator instead of getInstance() static method – then other instances will be created.
Example of breaking pattern:

$firstInstance = Singleton::getInstance();
$secondInstance = new Singleton();

What should we do to avoid that? By limiting public accessibility of constructor. We should use private constructor instead.

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

Solution 2: Cloning

Partially secured, but... we can also use a clone operator, so object instance will be cloned.
Example of breaking pattern:

$firstInstance = Singleton::getInstance();
$secondInstance = clone $firstInstance;

We need also to limit public accessibility of clone (magic) method.

class Singleton 
{
    private static $instance;
    
    private function __construct() {}
    private function __clone() {}
    
    public static function getInstance() 
    {
        if(!self::$instance) {
            self::$instance = new self;	
        }
        
        return self::$instance;
    }
}

Solution 3: Inheritance

There is one more small case, which will break also the singleton pattern – the inheritance. Someone can extend our Singleton class and override accessibility:

class OtherSingleton extends Singleton {
    public function __construct() {}
    public function __clone() {}	
}

$firstInstance = OtherSingleton::getInstance();
$secondInstance = new OtherSingleton();
$thirdInstance = clone $secondInstance;

So we should define all the methods from Singleton (parent class) as final. This thing will block overriding:

class Singleton 
{
    private static $instance;
    
    private final function __construct() {}
    private final function __clone() {}
    
    public final static function getInstance() 
    {
        if(!self::$instance) {
            self::$instance = new self;	
        }
        
        return self::$instance;
    }
}

Solution 4: Unserializing

There is another way to create an instance of PHP class – by using unserialize() build-in PHP method. This method are getting the serialized data (string) and will recognise – which class the object represent. After that it will create a new instance of this class:

$firstInstance = Singleton::getInstance();
$serialized = serialize($firstInstance);

$secondInstance = unserialize($serialized);
// or
$thirdInstance = unserialize('O:9:"Singleton":0:{}');

As you can see we are able to create as many instances as we want. There is also solution for that. The unserialize() method (php_documentation) after unserializing will automatically looking for __wakeup() method in our Singleton class (if it does exist). So we need to add this method as final and private:

class Singleton 
{
    private static $instance;
    
    private final function __construct() {}
    private final function __clone() {}
    private final function __wakeup() {}
    
    public final static function getInstance() 
    {
        if(!self::$instance) {
            self::$instance = new self;	
        }
        
        return self::$instance;
    }
}

Danger: Reflection mechanism

It is already ok? If you think this is enough, you’re wrong. Unfortunatelly in PHP we have mechanism called “Reflection“.

By creating ReflectionClasswe are able to manipulate on classes, methods, properties. For example we can change accessibility of some methods back to public.  We can also create a new instance of every class without constructor. So there are many ways to break the Singleton pattern:

$firstInstance = Singleton::getInstance();
$reflectionClass = new ReflectionClass("Singleton"); // create second instance inside
$thirdInstance = $reflectionClass->newInstanceWithoutConstructor($firstInstance); // create third instance

In these example you can notice, that even starting using ReflectionClassaffect creating a new instance of our choosed (Singleton) class. It is done inside the ReflectionClass. So if we have some connections e.g. to database in our Singleton – using reflection might also affect connections to DB or some other unknown things (implemented in singleton class).

There isn’t any solution for that, but I want you to know, that using reflection for incorrect purposes like in this examples is a bad practice. It is useful for other actions like detecting method arguments types or creating dynamically some DTO’s (e.g. from API response).

Good practice: Traits

DRY – means “don’t repeat yourself”.

In PHP we have a nice tool to avoid duplicating the same blocks of code in different classes. It’s called Traits. In our case we can create a singleton trait – instead of singleton class, which will be used by many other classes if they should have this functionality implemented :

trait Singleton 
{
    private static $instance;
    
    private final function __construct() {}
    private final function __clone() {}
    private final function __wakeup() {}
    
    public final static function getInstance() 
    {
        if(!self::$instance) {
            self::$instance = new self;	
        }
        
        return self::$instance;
    }
}

class DatabaseManager {
    use Singleton;
}

class FilesWatcher {
    use Singleton;
}

// etc..

Summary

In some programming languages it is possible to have singleton pattern implemented and secured against many instances creation, but in PHP it is impossible for now (because of reflection mechanism). Maybe it will be changed in the future version of PHP, but we have to wait for that.
Anyway we are able to protect our Singleton partially using techniques, that I have described.

Mateusz Palichleb

Blog author

I am a experienced programmer in building web applications (generally I started programming in 2005). I'm always open to conversation about clean code, and interesting programming techniques, whoose can improve our work as developers.

You Might Also Like

Abstract Factory meets… Factory Method

Abstract Factory meets… Factory Method

3 Comments

  1. 18/05/2018 at 11:25

    It is better that PHP has created a ReflectionClass with this we can create a new instance for an object new or special that we want, imagine in a big project where everything is ‘Singleton’ and you can not create a new instance of an object, it would be terrible! I think ‘Singleton’ and ‘ReflectionClass’ are going very caught hands. And they can be combined correctly.

    1. 14/12/2018 at 14:37

      Exactly! It all depends on how the person uses these “tools”. In this article, I wanted to show you the potential risks if someone will misused them.

  2. 28/09/2018 at 10:56

    I’m trying not to use singleton pattern but trait is really good idea if we using this pattern in many places.

Leave a Reply

Your email address will not be published. Required fields are marked *