Conexão Única com PDO usando o Padrão Singleton em PHP
Gerenciar múltiplas conexões com o banco de dados em uma mesma requisição pode esgotar os recursos do servidor e desacelerar a aplicação. A aplicação do padrão de projeto Singleton na classe de conexão PDO garante uma instância única e reutilizável, combinada com Prepared Statements para neutralizar vulnerabilidades de SQL Injection.
PHP
<?php
/**
* Gerenciador de conexão com banco de dados utilizando o padrão Singleton.
*/
class Database
{
private static ?PDO $instance = null;
// Construtor privado impede a criação direta de objetos via 'new'
private function __construct() {}
// Impede a clonagem da instância Singleton
private function __clone() {}
/**
* Retorna a instância única da conexão PDO.
*/
public static function getInstance(): PDO
{
if (self::$instance === null) {
$dsn = 'mysql:host=127.0.0.1;dbname=empresa_db;charset=utf8mb4';
self::$instance = new PDO($dsn, 'db_user', 'senha_segura_456', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
}
return self::$instance;
}
/**
* Executa uma consulta SELECT parametrizada contra SQL Injection.
*/
public static function query(string $sql, array $params = []): array
{
$stmt = self::getInstance()->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
}
// --- Exemplo de Uso Prático ---
try {
// Consulta protegida com parâmetros posicionais ou nomeados
$sql = "SELECT id, nome, email FROM clientes WHERE cidade = :cidade AND ativo = :status";
$parametros = [
':cidade' => 'São Paulo',
':status' => 1
];
// Execução sem instanciar explicitamente o banco várias vezes
$clientes = Database::query($sql, $parametros);
echo "Registros encontrados: " . count($clientes) . PHP_EOL;
print_r($clientes);
} catch (PDOException $e) {
error_log("Falha ao consultar banco: " . $e->getMessage());
echo "Erro interno no processamento da solicitação." . PHP_EOL;
}
- Encapsulamento Restritivo (
private __constructe__clone): Bloqueia a instanciação externa da classe via operadornewou por clonagem, forçando o uso exclusivo do método estáticogetInstance(). - Reaproveitamento de Conexão: Reduz a sobrecarga de E/S no servidor MySQL abrindo o socket de rede uma única vez por ciclo de vida da requisição HTTP.
- Proteção contra SQL Injection: O método
Database::query()utiliza o$pdo->prepare()para assegurar o isolamento estrito entre a sintaxe do comando SQL e os argumentos enviados. - Desativação de Prepared Statements Emulados: A instrução
PDO::ATTR_EMULATE_PREPARES => falsegarante que a preparação da consulta ocorra diretamente na engine do banco de dados, maximizando a segurança.
