Singleton

·

,

Singleton – ensure that a class has only one instance,
while providing a global access to this instance.

<?php

class Logger
{
    private static ?Logger $uniqueInstance = null;
    private array $logs = [];

    // prevents direct instantiation
    private function __construct() {}

    // no one outside the class can duplicate the instance.
    private function __clone() {}

    /* __wakeup must be public (by PHP’s design), 
    since it’s automatically called during unserialize().*/
    public function __wakeup()
    {
        throw new \Exception("Cannot unserialize a singleton.");
    }

    /* Lazy construction / initialization - returns the single instance 
    of this class*/
    public static function getInstance(): Logger
    {
        if (self::$uniqueInstance === null) {
            self::$uniqueInstance = new Logger();
        }
        return self::$uniqueInstance;
    }

    // Methods
    public function addLog(string $message): void
    {
        $this->logs[] = $message;
    }

    public function showLogs(): void
    {
        foreach ($this->logs as $log) {
            echo $log . PHP_EOL;
        }
    }
}

// Usage exemple
$logger1 = Logger::getInstance();
$logger2 = Logger::getInstance();

$logger1->addLog("First log entry.");
$logger2->addLog("Second log entry.");

$logger1->showLogs();

//Checking the solid proof...

if ($logger1 === $logger2) {
    echo "Same instance" . PHP_EOL;
} else {
    echo "Different instances" . PHP_EOL;
}

echo "Logger1 ID: " . spl_object_id($logger1) . PHP_EOL;
echo "Logger2 ID: " . spl_object_id($logger2) . PHP_EOL;

/*
Output:

First log entry.
Second log entry.
Same instance
Logger1 ID: 1
Logger2 ID: 1
*/

The code above works perfectly for our purpose, but it’s not inheritance-safe, meaning it always creates a Logger instance, even if called from a subclass. Therefore:

class FileLogger extends Logger {}
FileLogger::getInstance(); //still returns the instance of Logger, not a instance of FileLogger

If we want that each subclass has its own singleton, we must change this behavior by modifying the getInstance() method, like this:

public static function getInstance(): Logger 
{
    $className = static::class;
    if (!isset(self::$uniqueInstance[$className])) {
        self::$uniqueInstance[$className] = new static();
    }
    return self::$uniqueInstance[$className];
}

Comparison:

FeatureVersion
self
Version
static::class
Supports inheritance❌ No✅ Yes
Each subclass has its own singleton❌ No✅ Yes
Refers to who?Refer to the class where the code was originally defined.Refer to the class that was actually called at runtime.
When use?You want a single, strict singleton for only the original class.You expect inheritance or extensions of your original class.

When Singleton pattern is useful?

  • Logger – Keep a single instance that writes all logs from different parts of the app.
  • Configuration Manager – Store settings (like API keys, database credentials, etc.) in one place.
  • Database Connection – Maintain one connection to the database.
  • Cache Manager – Keep a single cache handler (like Redis, Memcached, or file cache)
  • Session Manager – Handle user session data globally.
  • Event Dispatcher or Queue Manager – Centralize how events or background tasks are managed.


Create your own user feedback survey

Comments

2 responses to “Singleton”

  1. David_R Avatar

    Excellent article. Sharing this with my colleagues.

    1. F. Sanches Avatar

      Hello David! I’m glad you enjoyed it. I’ll be finishing other posts about Design Patterns in the coming weeks.

Leave a Reply

Your email address will not be published. Required fields are marked *