Паттерн Одиночка в PHP
Короткий скринкаст: паттерн «Одиночка» (Singleton) — один объект на всё приложение с ленивой инициализацией.
Описание
Мы делаем Logger, у которого не может быть больше одного экземпляра. Конструктор приватный, а getInstance() создаёт объект только при первом обращении и дальше всегда возвращает его же. Пишем сообщение через $a, читаем через $b — и это один и тот же объект.
UML-диаграмма
Код
Logger.php
class Logger
{
private static ?Logger $instance = null;
public array $messages = [];
private function __construct() {}
public static function getInstance(): Logger
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function log(string $message): void
{
$this->messages[] = $message;
}
}
demo.php
require_once 'Logger.php';
$a = Logger::getInstance();
$b = Logger::getInstance();
$a->log('first');
echo $a === $b ? 'Same instance: true' : 'Same instance: false';
echo PHP_EOL;
echo 'Messages: ' . implode(', ', $b->messages);
Заметка: плюсы и критика
Преимущество — одна общая точка доступа и ленивое создание: экземпляр не занимает память, пока не понадобился. Но за это платим глобальным состоянием: кто угодно может писать в лог, что усложняет тесты и прячет зависимость. Во многих случаях между синглтоном и «просто инъекцией одного объекта в контейнере» стоит выбрать второе — тогда решение остаётся в руках вызывающего кода.
Пример результата
После php demo.php в терминале появится:
Same instance: true
Messages: first
Преимущества
- Гарантирует единственный экземпляр на всё приложение.
- Ленивая инициализация: объект создаётся при первом обращении.
- Общая точка доступа к разделяемому ресурсу.
- Экономит память и ресурсы: одно соединение, один логгер.
Когда использовать
- Когда у класса должен быть ровно один экземпляр.
- Когда нужен общий доступ к разделяемому ресурсу (конфиг, лог, кэш).
- Когда создание ресурса дорого и его стоит делать лениво.
- Когда важно контролировать глобальное состояние из одного места.
Использование в Laravel
В Laravel Одиночка — это сервис-контейнер: биндинг через ->singleton() гарантирует, что app() вернёт один и тот же объект. Менеджеры Illuminate\Log\LogManager, Illuminate\Cache\CacheManager и подключение к БД зарегистрированы как одиночки, а фасады (Illuminate\Support\Facades\Log, Cache) дают к ним статический доступ.