Singleton – ensure that a class has only one instance,
while providing a global access point 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 FileLoggerIf 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:
| Feature | Version 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.

Leave a Reply