query('SELECT 1'); return true; } catch (\PDOException $e) { Logger::warning('Database connection lost, reconnecting', ['error' => $e->getMessage()]); return false; } } private static function connect(): \PDO { $host = self::env('DB_HOST', '127.0.0.1'); $port = self::env('DB_PORT', '5432'); $name = $_ENV['DB_NAME'] ?? ''; $user = $_ENV['DB_USER'] ?? ''; $pass = $_ENV['DB_PASS'] ?? ''; if ($name === '') { Logger::error('DB_NAME is not configured'); throw new \RuntimeException('DB_NAME is not configured.'); } if (($_ENV['DB_USER'] ?? '') === '') { Logger::warning('DB_USER is empty; using empty database user'); } $dsn = sprintf('pgsql:host=%s;port=%s;dbname=%s', $host, $port, $name); try { $pdo = new \PDO($dsn, $user, $pass, [ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, \PDO::ATTR_PERSISTENT => true, // Emula prepares no cliente: evita o round-trip extra de // Parse/Describe server-side a cada query. Com o banco remoto // (~160ms de latência por ida-e-volta), isso reduz o custo de // cada query de ~3 round-trips para 1. \PDO::ATTR_EMULATE_PREPARES => true, ]); } catch (\PDOException $e) { Logger::error('Failed to connect to database', [ 'host' => $host, 'port' => $port, 'name' => $name, 'error' => $e->getMessage(), ]); throw $e; } Logger::info('Database connection established', ['host' => $host, 'port' => $port, 'name' => $name]); return $pdo; } /** * Lê uma variável de ambiente registrando em log quando cai no valor default. * Torna visível o uso silencioso de defaults (ex.: host/porta ausentes). */ private static function env(string $key, string $default): string { $value = $_ENV[$key] ?? null; if ($value === null || $value === '') { Logger::warning('Environment variable missing, using default', ['key' => $key, 'default' => $default]); return $default; } return (string) $value; } }