Skip to content
Florian Wolters edited this page Mar 6, 2013 · 1 revision

Singleton

Singleton ensures that only a single instance of a class exists per application.

By using the trait FlorianWolters\Component\Util\Singleton\SingletonTrait, a class implements the Singleton creational design pattern.

The following Factory Method (to create logger instances) demonstrates this. Factory Methods are a good example for the usage of the Singleton design pattern (whereas a database connection is a bad example).

<?php
namespace FlorianWolters\Component\Util\Log;

use \InvalidArgumentException;
use FlorianWolters\Component\Util\Singleton\SingletonTrait;
use Psr\Log\LoggerInterface;

class LoggerFactory
{
    use SingletonTrait;

    /*
     * Constructs a new logger.
     *
     * @param string $type The type of the logger to create.
     *
     * @return LoggerInterface The logger instance.
     */
    public function createLogger($type = 'echo')
    {
        /* @var $result Psr\Log\LoggerInterface */
        $result = null;

        switch ($type) {
            case 'echo':
                // A logger implementation may also be a Singleton or Multiton.
                $result = EchoLogger::getInstance();
                break;
            case 'null':
                $result = NullLogger::getInstance();
                break;
            default:
                InvalidArgumentException('The given logger type is invalid.');
                break;
        }

        return $result;
    }
}

The following source code demonstrates the usage of the class LoggerFactory.

<?php
$nullLogger = LoggerFactory::getInstance('null');
$nullLogger->info('foo');

The Singleton implementation of the trait FlorianWolters\Component\Util\Singleton\SingletonTrait disallows creating (via the method __construct), cloning (via the method __clone) and unserializing (via the method __wakeup) of a Singleton instance.

<?php
$loggerFactory = LoggerFactory::getInstance();         // OK
$serializedLoggerFactory = \serialize($loggerFactory); // OK
\unserialize($serializedLoggerFactory);                // E_WARNING: Invalid callback FlorianWolters\Component\Util\Log\LoggerFactory::__wakeup, cannot access private method FlorianWolters\Component\Util\Log\LoggerFactory::__wakeup() in [...] on line [...]
clone $loggerFactory;                                  // E_ERROR: Call to private LoggerFactory::__clone() from invalid context in [...] on line [...]
new LoggerFactory;                                     // E_ERROR: Call to protected LoggerFactory::__construct() from invalid context in [...] on line [...]

Multiton

Multiton (a.k.a. Registry of Singletons) is a variation of the Singleton design pattern.

It expands the Singleton concept to manage a map of named instances as key-value pairs. In contrast to Singleton, Multiton ensures that only a single instance of a class exists per key. All other constraints of Singleton do also apply for Multiton.

By using the trait FlorianWolters\Component\Util\Singleton\MultitonTrait, a class implements the Multiton creational design pattern.

The following Value Object (which models a simple user) demonstrates this.

<?php
namespace FlorianWolters\Component\Model;

use FlorianWolters\Component\Util\Singleton\MultitonTrait;

class UserModel
{
    use MultitonTrait;

    private $username;

    private $password;

    protected function __construct($username, $password)
    {
        $this->username = $username;
        $this->password = $password;
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function getPassword()
    {
        return $this->password;
    }
}

The following usage example for the class UserModel demonstrates the passing of arguments to the protected constructor of the class via the static Creational Method getInstance.

<?php
$user = UserModel::getInstance('john_doe', '1234');
$user->getUsername(); // 'john_doe'
$user->getPassword(); // '1234'

A new instance of a Multiton is created, if the arguments of the static Creational Method getInstance differ.

<?php
$userJohn = UserModel::getInstance('john_doe', '1234');
$userJane = UserModel::getInstance('jane_doe', 'test');
\var_dump($userJohn === $userJane);                                  // false
\var_dump(UserModel::getInstance('john_doe', '1234') === $userJohn); // true
\var_dump(UserModel::getInstance('jane_doe', 'test') === $userJane); // true

The following calls will fail with a PHP error:

<?php
new UserModel('john_doe', '1234');  // E_ERROR: Call to protected UserModel::__construct() from invalid context in [...] on line [...]
UserModel::getInstance('john_doe'); // E_WARNING: Missing argument 2 for UserModel::__construct() in [...] on line [...]
Clone this wiki locally