Creating the Singleton design pattern in PHP5

Viewed 202077

How would one create a Singleton class using PHP5 classes?

22 Answers
/**
 * Singleton class
 *
 */
final class UserFactory
{
    private static $inst = null;

    // Prevent cloning and de-serializing
    private function __clone(){}
    private function __wakeup(){}


    /**
     * Call this method to get singleton
     *
     * @return UserFactory
     */
    public static function Instance()
    {
        if ($inst === null) {
            $inst = new UserFactory();
        }
        return $inst;
    }
    
    /**
     * Private ctor so nobody else can instantiate it
     *
     */
    private function __construct()
    {
        
    }
}

To use:

$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();

$fact == $fact2;

But:

$fact = new UserFactory()

Throws an error.

See http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static to understand static variable scopes and why setting static $inst = null; works.

PHP 5.3 allows the creation of an inheritable Singleton class via late static binding:

class Singleton
{
    protected static $instance = null;

    protected function __construct()
    {
        //Thou shalt not construct that which is unconstructable!
    }

    protected function __clone()
    {
        //Me not like clones! Me smash clones!
    }

    public static function getInstance()
    {
        if (!isset(static::$instance)) {
            static::$instance = new static;
        }
        return static::$instance;
    }
}

This solves the problem, that prior to PHP 5.3 any class that extended a Singleton would produce an instance of its parent class instead of its own.

Now you can do:

class Foobar extends Singleton {};
$foo = Foobar::getInstance();

And $foo will be an instance of Foobar instead of an instance of Singleton.

You probably should add a private __clone() method to disallow cloning of an instance.

private function __clone() {}

If you don't include this method the following gets possible

$inst1=UserFactory::Instance(); // to stick with the example provided above
$inst2=clone $inst1;

now $inst1 !== $inst2 - they are not the same instance any more.

You don't really need to use Singleton pattern because it's considered to be an antipattern. Basically there is a lot of reasons to not to implement this pattern at all. Read this to start with: Best practice on PHP singleton classes.

If after all you still think you need to use Singleton pattern then we could write a class that will allow us to get Singleton functionality by extending our SingletonClassVendor abstract class.

This is what I came with to solve this problem.

<?php
namespace wl;


/**
 * @author DevWL
 * @dosc allows only one instance for each extending class.
 * it acts a litle bit as registry from the SingletonClassVendor abstract class point of view
 * but it provides a valid singleton behaviour for its children classes
 * Be aware, the singleton pattern is consider to be an anti-pattern
 * mostly because it can be hard to debug and it comes with some limitations.
 * In most cases you do not need to use singleton pattern
 * so take a longer moment to think about it before you use it.
 */
abstract class SingletonClassVendor
{
    /**
     *  holds an single instance of the child class
     *
     *  @var array of objects
     */
    protected static $instance = [];

    /**
     *  @desc provides a single slot to hold an instance interchanble between all child classes.
     *  @return object
     */
    public static final function getInstance(){
        $class = get_called_class(); // or get_class(new static());
        if(!isset(self::$instance[$class]) || !self::$instance[$class] instanceof $class){
            self::$instance[$class] = new static(); // create and instance of child class which extends Singleton super class
            echo "new ". $class . PHP_EOL; // remove this line after testing
            return  self::$instance[$class]; // remove this line after testing
        }
        echo "old ". $class . PHP_EOL; // remove this line after testing
        return static::$instance[$class];
    }

    /**
     * Make constructor abstract to force protected implementation of the __constructor() method, so that nobody can call directly "new Class()".
     */
    abstract protected function __construct();

    /**
     * Make clone magic method private, so nobody can clone instance.
     */
    private function __clone() {}

    /**
     * Make sleep magic method private, so nobody can serialize instance.
     */
    private function __sleep() {}

    /**
     * Make wakeup magic method private, so nobody can unserialize instance.
     */
    private function __wakeup() {}

}

Use example:

/**
 * EXAMPLE
 */

/**
 *  @example 1 - Database class by extending SingletonClassVendor abstract class becomes fully functional singleton
 *  __constructor must be set to protected becaouse: 
 *   1 to allow instansiation from parent class 
 *   2 to prevent direct instanciation of object with "new" keword.
 *   3 to meet requierments of SingletonClassVendor abstract class
 */
class Database extends SingletonClassVendor
{
    public $type = "SomeClass";
    protected function __construct(){
        echo "DDDDDDDDD". PHP_EOL; // remove this line after testing
    }
}


/**
 *  @example 2 - Config ...
 */
class Config extends SingletonClassVendor
{
    public $name = "Config";
    protected function __construct(){
        echo "CCCCCCCCCC" . PHP_EOL; // remove this line after testing
    }
}

Just to prove that it works as expected:

/**
 *  TESTING
 */
$bd1 = Database::getInstance(); // new
$bd2 = Database::getInstance(); // old
$bd3 = Config::getInstance(); // new
$bd4 = Config::getInstance(); // old
$bd5 = Config::getInstance(); // old
$bd6 = Database::getInstance(); // old
$bd7 = Database::getInstance(); // old
$bd8 = Config::getInstance(); // old

echo PHP_EOL."COMPARE ALL DATABASE INSTANCES".PHP_EOL;
var_dump($bd1);
echo '$bd1 === $bd2' . ($bd1 === $bd2)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd2 === $bd6' . ($bd2 === $bd6)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd6 === $bd7' . ($bd6 === $bd7)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE

echo PHP_EOL;

echo PHP_EOL."COMPARE ALL CONFIG INSTANCES". PHP_EOL;
var_dump($bd3);
echo '$bd3 === $bd4' . ($bd3 === $bd4)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd4 === $bd5' . ($bd4 === $bd5)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE
echo '$bd5 === $bd8' . ($bd5 === $bd8)? ' TRUE' . PHP_EOL: ' FALSE' . PHP_EOL; // TRUE

This should be the right way of Singleton.

class Singleton {

    private static $instance;
    private $count = 0;

    protected function __construct(){

    }

    public static function singleton(){

        if (!isset(self::$instance)) {

            self::$instance = new Singleton;

        }

        return self::$instance;

    }

    public function increment()
    {
        return $this->count++;
    }

    protected function __clone(){

    }

    protected function __wakeup(){

    }

} 

Quick example:

final class Singleton
{
    private static $instance = null;

    private function __construct(){}

    private function __clone(){}

    private function __wakeup(){}

    public static function get_instance()
    {
        if ( static::$instance === null ) {
            static::$instance = new static();
        }
        return static::$instance;
    }
}

Hope help.

The above answers are ok, But I'll add more.

Whoever come here in 2021, I'll show another example of using Singleton Pattern class as a trait and Re-use this in any class.

<?php

namespace Akash;

trait Singleton
{
    /**
     * Singleton Instance
     *
     * @var Singleton
     */
    private static $instance;

    /**
     * Private Constructor
     *
     * We can't use the constructor to create an instance of the class
     *
     * @return void
     */
    private function __construct()
    {
        // Don't do anything, we don't want to be initialized
    }

    /**
     * Get the singleton instance
     *
     * @return Singleton
     */
    public static function getInstance()
    {
        if (!isset(self::$instance)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Private clone method to prevent cloning of the instance of the
     * Singleton instance.
     *
     * @return void
     */
    private function __clone()
    {
        // Don't do anything, we don't want to be cloned
    }

    /**
     * Private unserialize method to prevent unserializing of the Singleton
     * instance.
     *
     * @return void
     */
    private function __wakeup()
    {
        // Don't do anything, we don't want to be unserialized
    }
}

So, use it like in any class easily. Suppose, we want to implement Singleton pattern in UserSeeder class.

<?php

class UserSeeder
{
    use Singleton;

    /**
     * Seed Users
     *
     * @return void
     */
    public function seed()
    {
        echo 'Seeding...';
    }
}
Related