Разработка на PHP: Как реализовать паттерн Singleton через защищенное статическое свойство $instance, чтобы гарантировать единственность объекта подключения к БД?
Паттерн Singleton (Одиночка) — один из фундаментальных порождающих паттернов проектирования, цель которого — гарантировать существование только одного экземпляра класса в рамках всего приложения. В контексте подключения к базе данных это особенно важно: создание нескольких соединений с БД расходует ресурсы и может приводить к непредсказуемому поведению.
**Основные принципы реализации Singleton в PHP:**
1. **Приватный конструктор** — запрещает создание объекта через `new` извне класса.
2. **Защищённое (или приватное) статическое свойство `$instance`** — хранит единственный экземпляр класса.
3. **Статический метод `getInstance()`** — единственная точка доступа к экземпляру.
4. **Запрет клонирования и десериализации** — методы `__clone()` и `__wakeup()` объявляются приватными.
**Пример реализации:**
php
class Database
{
protected static ?Database $instance = null;
private PDO $connection;
private function __construct()
{
$dsn = ‘mysql:host=localhost;dbname=mydb;charset=utf8’;
$this->connection = new PDO($dsn, ‘user’, ‘password’, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
private function __clone() {}
public function __wakeup() {
throw new Exception(‘Cannot unserialize singleton.’);
}
public static function getInstance(): static
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function getConnection(): PDO
{
return $this->connection;
}
}
**Почему именно `protected static`, а не `private static`?**
Использование `protected` вместо `private` позволяет дочерним классам переопределять логику создания экземпляра. Это особенно актуально при использовании позднего статического связывания (`static::$instance` и `new static()`), что делает паттерн расширяемым для наследников.
**Использование:**
php
$db = Database::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->query(‘SELECT * FROM users’);
Каждый последующий вызов `Database::getInstance()` возвращает тот же объект, что и первый — соединение с БД устанавливается ровно один раз.
**Важные нюансы:**
— В многопоточных средах (например, при использовании Swoole) Singleton требует дополнительной синхронизации.
— Singleton усложняет юнит-тестирование, так как создаёт глобальное состояние. Для тестов рекомендуется использовать dependency injection в связке с Singleton.
— Начиная с PHP 8.0, можно использовать `static` в качестве возвращаемого типа для корректной поддержки наследования.
Паттерн Singleton в PHP — проверенный способ управления ресурсоёмкими объектами, такими как соединения с базой данных, логгеры и конфигурационные менеджеры.
