Объяснение на примере написания плагина для WordPress.
Ядро плагина – абстрактный класс.
abstract class Plugin
{
protected string $version;
protected string $plugin_file;
// Хранилище экземпляров — массив, потому что может быть
// несколько наследников (SprmBLocks, другой плагин...)
private static array $instances;
abstract public function init(): void;
final public static function getInstance(): static
{
// static::class = имя класса-наследника, на котором вызвали
$class = static::class;
if (! isset(self::$instances[$class])) {
// Первый вызов — создаём экземпляр
self::$instances[$class] = new static();
}
// Все последующие вызовы — возвращаем тот же экземпляр
return self::$instances[$class];
}
// Конструктор закрыт — нельзя сделать new Plugin() снаружи
final private function __construct()
{
// Singleton
}
Делаем плагин на основе абстрактного класса
Используем getInstance() чтобы создать один класс.
class SprmBLocks extends Plugin
{
protected string $version = '__PLUGIN_VERSION__';
protected string $plugin_file = __FILE__;
public function init(): void
{
...
}
}
SprmBLocks::getInstance()->init();
Что происходит при вызовах
Первый вызов:
SprmBLocks::getInstance()
→ $class = 'SprmBLocks'
→ $instances['SprmBLocks'] не существует
→ new static() → new SprmBLocks()
→ сохранил в $instances['SprmBLocks']
→ вернул объект
Второй вызов:
SprmBLocks::getInstance()
→ $class = 'SprmBLocks'
→ $instances['SprmBLocks'] уже существует
→ вернул тот же объект ← не создаёт новый
Три защиты Singleton
1. private __construct() → нельзя: new SprmBLocks()
2. final __construct() → нельзя: переопределить конструктор в наследнике
3. final getInstance() → нельзя: переопределить логику получения в наследнике
Гарантия: экземпляр класса всегда один, создаётся при первом вызове, повторно не создаётся.