HiveBrain v1.2.0
Get Started
← Back to all entries
patternphpMinor

Base class for singletons

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
classforsingletonsbase

Problem

Even though singletons are bad practice and often unnecessary, I still see many developers using this pattern over and over. Since implementation of this pattern usually requires some common code, I thought we could just make a base class to be extended.

I came up with this so far.

<?php
namespace wl;

/**
 * @dosc allows only one instance for each extending class
 * @example use it for database connection, config setup...
 * Be aware, the singleton pattern is consider to be an anti-pattern
 * because it can be hard to debug.
 * In most cases you do not need to use singleton pattern
 * so take a longer moment to think about it before you use it.
 */
class Singleton
{
    /**
     *  holds an single instance of a 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 function getInstance(){
        $class = get_called_class();
        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];
    }

    /**
     *  do not allow create new instance by new keyword
     * 
     */
    protected function __construct(){}

    /**
     *  Do not clone the object
     */
    protected function __clone(){}

    /**
     *  Do not allow reserialization of this object
     */
    protected function __wakeup(){}

}


```
/**
* ----------------------------------------------USE EXAMPLE---------------------------------------------------
* @docs example database

Solution

Not a Singleton

class Database extends Singleton
{
    public function __construct(){

    }
}


This is not a singleton. Add the following code:

$bd7 = new Config();


You have a new Config object.

class Config extends Singleton {

    protected function __construct() {

    }

}


This is a singleton. It will give an error if you try to create a Config object directly. Or

class Database extends Singleton {

}


Is a singleton that gives an error if you create a Database object directly as it inherits its constructor from its parent.

Registry

You don't need to use inheritance to get this functionality.

public static function getInstance(){
        $class = get_called_class();
        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];
    }


Change to

public static function getInstance($class, $object = null) {
        if (!isset(self::$instance[$class]) && $object != null) {
            self::$instance[$class] = $object;
        }

        return static::$instance[$class];
    }


or just

public static function getInstance($key) {
        return static::$instance[$key];
    }


with

public static function setInstance($key, $object) {
        static::$instance[$key] = $object;
    }


And change

$bd1 = Database::getInstance(); // new


to

Registry::setInstance('database', new Database());
$bd1 = Registry::getInstance('database');


Now, it's possible that in a real application you would make more use of what inheritance gives you. But in this toy application, you don't.

I changed the name from Singleton to Registry, as better fitting this pattern.

You lose the just-in-time instantiation of the original, but you gain control over how it is instantiated. An advantage of this form is that you can also do things like

Registry::setInstance('database', new MockDatabase());


Your original form prevented this, exacerbating the Singleton pattern's problems with user testing.

Nitpicks

/**
 * @dosc allows only one instance for each extending class
 * @example use it for database connection, config setup...
 * Be aware, singleton pattern is consider to be an antipatern and becaouse of it build it is hard to debug.
 * In most cases you do not need to use singleton patern so make a longer moment to think about it befor you use it.
 */


Without the spelling errors:

/**
 * @dosc allows only one instance for each extending class
 * @example use it for database connection, config setup...
 * Be aware, the singleton pattern is consider to be an anti-pattern
 * because it can be hard to debug.
 * In most cases you do not need to use singleton pattern
 * so take a longer moment to think about it before you use it.
 */


I also changed the wording a bit and broke up two lines so that they don't cause scrolling (at least in my browser).

Code Snippets

class Database extends Singleton
{
    public function __construct(){

    }
}
$bd7 = new Config();
class Config extends Singleton {

    protected function __construct() {

    }

}
class Database extends Singleton {

}
public static function getInstance(){
        $class = get_called_class();
        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];
    }

Context

StackExchange Code Review Q#142630, answer score: 4

Revisions (0)

No revisions yet.