175 lines
4.5 KiB
PHP
175 lines
4.5 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* Database Connection Factory
|
||
|
|
*
|
||
|
|
* Centralizes database connection creation and management.
|
||
|
|
* Provides a singleton connection for the request lifecycle.
|
||
|
|
*/
|
||
|
|
class Database {
|
||
|
|
private static $connection = null;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get database connection (singleton pattern)
|
||
|
|
*
|
||
|
|
* @return mysqli Database connection
|
||
|
|
* @throws Exception If connection fails
|
||
|
|
*/
|
||
|
|
public static function getConnection() {
|
||
|
|
if (self::$connection === null) {
|
||
|
|
self::$connection = self::createConnection();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if connection is still alive
|
||
|
|
if (!self::$connection->ping()) {
|
||
|
|
self::$connection = self::createConnection();
|
||
|
|
}
|
||
|
|
|
||
|
|
return self::$connection;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create a new database connection
|
||
|
|
*
|
||
|
|
* @return mysqli Database connection
|
||
|
|
* @throws Exception If connection fails
|
||
|
|
*/
|
||
|
|
private static function createConnection() {
|
||
|
|
// Ensure config is loaded
|
||
|
|
if (!isset($GLOBALS['config'])) {
|
||
|
|
require_once dirname(__DIR__) . '/config/config.php';
|
||
|
|
}
|
||
|
|
|
||
|
|
$conn = new mysqli(
|
||
|
|
$GLOBALS['config']['DB_HOST'],
|
||
|
|
$GLOBALS['config']['DB_USER'],
|
||
|
|
$GLOBALS['config']['DB_PASS'],
|
||
|
|
$GLOBALS['config']['DB_NAME']
|
||
|
|
);
|
||
|
|
|
||
|
|
if ($conn->connect_error) {
|
||
|
|
throw new Exception("Database connection failed: " . $conn->connect_error);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set charset to utf8mb4 for proper Unicode support
|
||
|
|
$conn->set_charset('utf8mb4');
|
||
|
|
|
||
|
|
return $conn;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Close the database connection
|
||
|
|
*/
|
||
|
|
public static function close() {
|
||
|
|
if (self::$connection !== null) {
|
||
|
|
self::$connection->close();
|
||
|
|
self::$connection = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Begin a transaction
|
||
|
|
*
|
||
|
|
* @return bool Success
|
||
|
|
*/
|
||
|
|
public static function beginTransaction() {
|
||
|
|
return self::getConnection()->begin_transaction();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Commit a transaction
|
||
|
|
*
|
||
|
|
* @return bool Success
|
||
|
|
*/
|
||
|
|
public static function commit() {
|
||
|
|
return self::getConnection()->commit();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Rollback a transaction
|
||
|
|
*
|
||
|
|
* @return bool Success
|
||
|
|
*/
|
||
|
|
public static function rollback() {
|
||
|
|
return self::getConnection()->rollback();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Execute a query and return results
|
||
|
|
*
|
||
|
|
* @param string $sql SQL query with placeholders
|
||
|
|
* @param string $types Parameter types (i=int, s=string, d=double, b=blob)
|
||
|
|
* @param array $params Parameters to bind
|
||
|
|
* @return mysqli_result|bool Query result
|
||
|
|
*/
|
||
|
|
public static function query($sql, $types = '', $params = []) {
|
||
|
|
$conn = self::getConnection();
|
||
|
|
|
||
|
|
if (empty($types) || empty($params)) {
|
||
|
|
return $conn->query($sql);
|
||
|
|
}
|
||
|
|
|
||
|
|
$stmt = $conn->prepare($sql);
|
||
|
|
if (!$stmt) {
|
||
|
|
throw new Exception("Query preparation failed: " . $conn->error);
|
||
|
|
}
|
||
|
|
|
||
|
|
$stmt->bind_param($types, ...$params);
|
||
|
|
$stmt->execute();
|
||
|
|
|
||
|
|
$result = $stmt->get_result();
|
||
|
|
$stmt->close();
|
||
|
|
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Execute an INSERT/UPDATE/DELETE and return affected rows
|
||
|
|
*
|
||
|
|
* @param string $sql SQL query with placeholders
|
||
|
|
* @param string $types Parameter types
|
||
|
|
* @param array $params Parameters to bind
|
||
|
|
* @return int Affected rows (-1 on failure)
|
||
|
|
*/
|
||
|
|
public static function execute($sql, $types = '', $params = []) {
|
||
|
|
$conn = self::getConnection();
|
||
|
|
|
||
|
|
$stmt = $conn->prepare($sql);
|
||
|
|
if (!$stmt) {
|
||
|
|
throw new Exception("Query preparation failed: " . $conn->error);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!empty($types) && !empty($params)) {
|
||
|
|
$stmt->bind_param($types, ...$params);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($stmt->execute()) {
|
||
|
|
$affected = $stmt->affected_rows;
|
||
|
|
$stmt->close();
|
||
|
|
return $affected;
|
||
|
|
}
|
||
|
|
|
||
|
|
$error = $stmt->error;
|
||
|
|
$stmt->close();
|
||
|
|
throw new Exception("Query execution failed: " . $error);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the last insert ID
|
||
|
|
*
|
||
|
|
* @return int Last insert ID
|
||
|
|
*/
|
||
|
|
public static function lastInsertId() {
|
||
|
|
return self::getConnection()->insert_id;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Escape a string for use in queries (prefer prepared statements)
|
||
|
|
*
|
||
|
|
* @param string $string String to escape
|
||
|
|
* @return string Escaped string
|
||
|
|
*/
|
||
|
|
public static function escape($string) {
|
||
|
|
return self::getConnection()->real_escape_string($string);
|
||
|
|
}
|
||
|
|
}
|