2.5. Singleton

2.5.1. Intent

The purpose of the Singleton pattern is to ensure that a class will only have one instance at any time during code execution. Another key feature is to provide a global access to that instance.

2.5.2. When to use it?

For many people (including myself), Singleton is the political and nice name for global variables on object-oriented programming and that is the main drawback (global access + global state).

Global access makes source code hard to test ! Indeed, each objects should declare its collaborators in the constructor (dependency injection) in order to be able to mock dependencies.

For more details, I suggest you the great Miško Hevery’s blog (The Testability Explorer) and especially these posts:

Having said this, a few Singletons may be used inside your application without “harming” it. All objects that do not affect the behavior of your application (like a Logger) may be perfect examples.

2.5.3. Diagram

Created using PhpStorm and yFiles.

Class diagram of the Singleton pattern

2.5.4. Implementation

Logger.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php

namespace Phpatterns\Creational\Singleton;

/**
* Logger used as a Singleton
*
* 'final' keyword used to prevent from creating subclasses (be sure to keep only one instance of the Logger)
*
*/
final class Logger
{
    private static $instance = null;

    /**
    * Creates the Logger instance at the first call and then always returns
    * the same instance
    */
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self;
        }

        return self::$instance;
    }

    /**
    * Prevent from creating object directly
    */
    private function __construct()
    {

    }

    /**
    * Prevent from cloning object
    */
    private function __clone()
    {

    }

    /**
    * Prevent from being serialized
    */
    private function __sleep()
    {

    }

    /**
    * Prevent from being unserialized
    */
    private function __wakeup()
    {

    }
}

2.5.5. Tests

LoggerTest.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php

namespace Test\Phpatterns\Creational\Singleton;

use Phpatterns\Creational\Singleton\Logger;

class LoggerTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var \ReflectionObject
     */
    private $reflectionObject;

    protected function setUp()
    {
        $this->reflectionObject = new \ReflectionObject(
            Logger::getInstance()
        );
    }

    /**
     * Assert that 2 Logger instances are equal
     */
    public function testSingleton()
    {
        $logger1 = Logger::getInstance();
        $logger2 = Logger::getInstance();

        $this->assertSame($logger1, $logger2);
    }

    /**
     * @param string $method
     * @dataProvider methodProvider
     */
    public function testPrivateMethods($method)
    {
        $reflectionMethod = $this->reflectionObject->getMethod($method);
        $this->assertTrue($reflectionMethod->isPrivate());
    }

    public function methodProvider()
    {
        return [
            ['__construct'],
            ['__clone'],
            ['__sleep'],
            ['__wakeup']
        ];
    }
}