Le design pattern Singleton permet de garantir que l’instance d’une classe sera unique.
L’instanciation ne se fait pas via le constructeur, mais par le biais d’une méthode statiques qui se charge de vérifier l’existance d’une instance de la classe et de la retouner, ou de créer la créer.

Cas d’usage : la connexion à une base de données

<?php

declare(strict_types=1);

class Database
{
    private static ?Database $instance = null;
    private PDO $pdo;

    private function __construct(string $dsn, string $user, string $password)
    {
        $this->pdo = new PDO($dsn, $user, $password);
    }

    public static function getInstance(string $dsn, string $user, string $password): self
    {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        return self::$instance = new self($dsn, $user, $password);
    }

    public function query(string $query): false | PDOStatement
    {
        return $this->pdo->query($query);
    }
}

Usage

<?php
...
$db = Database::getInstance('mysql:host=localhost;dbname=my_db', 'db_user', 'db_password');
$db->query('SELECT * FROM table');

Cas d’usage : la configuration

<?php

declare(strict_types=1);

use Symfony\Component\Dotenv\Dotenv;

class Configuration
{
    private static ?Configuration $instance = null;
    private array $config = [];

    private function __construct()
    {
        $dotenv = new Dotenv();
        $dotenv->load(__DIR__ . './.env');

        $this->config['value_1'] = $_ENV['VALUE_1'];
        $this->config['value_2'] = $_ENV['VALUE_2'];
    }

    public static function getInstance(): self
    {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        return self::$instance = new self();
    }

    public function get(string $key): mixed
    {
        return $this->config[$key] ?? null;
    }
}

Usage

$configuration = Configuration::getInstance();
$configuration->get('value_1');

De design pattern à anti-pattern

Le Singleton est passé de design pattern à anti-partter pour plusieurs raisons :

  • Le non respect du principe de responsabilité unique car il gère à la fois le métier mais également la gestion de l’unicité de son instance.
  • La difficulté de la testabilité d’un Singleton
  • Assimilation à une variable gloable déguisée