| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-23 13:45:03 +00:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2024-11-29 16:38:49 +00:00
										 |  |  |  * class Database | 
					
						
							| 
									
										
										
										
											2024-11-25 14:09:47 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2024-11-23 13:45:03 +00:00
										 |  |  |  * Manages database connections for SQLite and MySQL (or MariaDB). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  | class Database { | 
					
						
							| 
									
										
										
										
											2024-11-23 13:45:03 +00:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2024-11-25 14:09:47 +00:00
										 |  |  |      * @var PDO|null $pdo The database connection instance. | 
					
						
							| 
									
										
										
										
											2024-11-23 13:45:03 +00:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  |     private $pdo; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-23 13:45:03 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Database constructor. | 
					
						
							|  |  |  |      * Initializes the database connection based on provided options. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param array $options An associative array with database connection options: | 
					
						
							|  |  |  |      *                       - type: The database type ('sqlite', 'mysql', or 'mariadb'). | 
					
						
							|  |  |  |      *                       - dbFile: The path to the SQLite database file (required for SQLite). | 
					
						
							|  |  |  |      *                       - host: The database host (required for MySQL). | 
					
						
							|  |  |  |      *                       - port: The port for MySQL (optional, default: 3306). | 
					
						
							|  |  |  |      *                       - dbname: The name of the MySQL database (required for MySQL). | 
					
						
							|  |  |  |      *                       - user: The username for MySQL (required for MySQL). | 
					
						
							|  |  |  |      *                       - password: The password for MySQL (optional). | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception If required extensions are not loaded or options are invalid. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |     public function __construct($options) { | 
					
						
							| 
									
										
										
										
											2024-11-23 13:45:03 +00:00
										 |  |  |         // check if PDO extension is loaded
 | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |         if (!extension_loaded('pdo')) { | 
					
						
							|  |  |  |             throw new Exception('PDO extension not loaded.'); | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // options check
 | 
					
						
							|  |  |  |         if (empty($options['type'])) { | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             throw new Exception('Database type is not set.'); | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-23 13:49:20 +00:00
										 |  |  |         // connect based on database type
 | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         switch ($options['type']) { | 
					
						
							|  |  |  |             case 'sqlite': | 
					
						
							|  |  |  |                 $this->connectSqlite($options); | 
					
						
							|  |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             case 'mysql': | 
					
						
							|  |  |  |             case 'mariadb': | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |                 $this->connectMysql($options); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             default: | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |                 $this->pdo = null; | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-07-04 19:14:12 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-23 13:46:01 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Establishes a connection to a SQLite database. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param array $options An associative array with SQLite connection options: | 
					
						
							|  |  |  |      *                       - dbFile: The path to the SQLite database file. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception If the SQLite PDO extension is not loaded or the database file is missing. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |     private function connectSqlite($options) { | 
					
						
							|  |  |  |         // pdo_sqlite extension is needed
 | 
					
						
							|  |  |  |         if (!extension_loaded('pdo_sqlite')) { | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             throw new Exception('PDO extension for SQLite not loaded.'); | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         // SQLite options
 | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |         if (empty($options['dbFile'])) { | 
					
						
							|  |  |  |             throw new Exception('SQLite database file path is missing.'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // For in-memory database (especially for the tests), skip file check
 | 
					
						
							|  |  |  |         if ($options['dbFile'] !== ':memory:' && !file_exists($options['dbFile'])) { | 
					
						
							|  |  |  |             throw new Exception("SQLite database file \"{$options['dbFile']}\" not found."); | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // connect to SQLite
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             $this->pdo = new PDO("sqlite:" . $options['dbFile']); | 
					
						
							|  |  |  |             $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | 
					
						
							| 
									
										
										
										
											2024-09-11 13:21:05 +00:00
										 |  |  |             // enable foreign key constraints (not ON by default in SQLite3)
 | 
					
						
							|  |  |  |             $this->pdo->exec('PRAGMA foreign_keys = ON;'); | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         } catch (PDOException $e) { | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             throw new Exception('SQLite connection failed: ' . $e->getMessage()); | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-23 13:46:01 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Establishes a connection to a MySQL (or MariaDB) database. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param array $options An associative array with MySQL connection options: | 
					
						
							|  |  |  |      *                       - host: The database host. | 
					
						
							|  |  |  |      *                       - port: The database port (default: 3306). | 
					
						
							|  |  |  |      *                       - dbname: The name of the database. | 
					
						
							|  |  |  |      *                       - user: The database username. | 
					
						
							|  |  |  |      *                       - password: The database password (optional). | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception If the MySQL PDO extension is not loaded or required options are missing. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |     private function connectMysql($options) { | 
					
						
							|  |  |  |         // pdo_mysql extension is needed
 | 
					
						
							|  |  |  |         if (!extension_loaded('pdo_mysql')) { | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             throw new Exception('PDO extension for MySQL not loaded.'); | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // MySQL options
 | 
					
						
							|  |  |  |         if (empty($options['host']) || empty($options['dbname']) || empty($options['user'])) { | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             throw new Exception('MySQL connection data is missing.'); | 
					
						
							| 
									
										
										
										
											2024-07-04 19:14:12 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |         // Connect to MySQL
 | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             $port = $options['port'] ?? 3306; | 
					
						
							|  |  |  |             $dsn = "mysql:host={$options['host']};port={$port};dbname={$options['dbname']};charset=utf8"; | 
					
						
							| 
									
										
										
										
											2024-08-10 18:42:44 +00:00
										 |  |  |             $this->pdo = new PDO($dsn, $options['user'], $options['password'] ?? ''); | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  |             $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | 
					
						
							|  |  |  |         } catch (PDOException $e) { | 
					
						
							| 
									
										
										
										
											2025-02-18 14:42:36 +00:00
										 |  |  |             $this->pdo = null; | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-23 13:46:01 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Retrieves the current PDO connection instance. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return PDO|null The PDO instance or null if no connection is established. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  |     public function getConnection() { | 
					
						
							|  |  |  |         return $this->pdo; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-18 14:45:25 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Executes an SQL query with optional parameters. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $query The SQL query to execute | 
					
						
							|  |  |  |      * @param array $params Optional parameters for the query | 
					
						
							|  |  |  |      * @return PDOStatement|false The result of the query execution | 
					
						
							|  |  |  |      * @throws Exception If the query fails | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function execute($query, $params = []) { | 
					
						
							|  |  |  |         if (!$this->pdo) { | 
					
						
							|  |  |  |             throw new Exception('No database connection.'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             $stmt = $this->pdo->prepare($query); | 
					
						
							|  |  |  |             $stmt->execute($params); | 
					
						
							|  |  |  |             return $stmt; | 
					
						
							|  |  |  |         } catch (PDOException $e) { | 
					
						
							|  |  |  |             throw new Exception('Query execution failed: ' . $e->getMessage()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Prepares an SQL statement for execution. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $query The SQL query to prepare | 
					
						
							|  |  |  |      * @return PDOStatement The prepared statement | 
					
						
							|  |  |  |      * @throws Exception If the preparation fails | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function prepare($query) { | 
					
						
							|  |  |  |         if (!$this->pdo) { | 
					
						
							|  |  |  |             throw new Exception('No database connection.'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             return $this->pdo->prepare($query); | 
					
						
							|  |  |  |         } catch (PDOException $e) { | 
					
						
							|  |  |  |             throw new Exception('Statement preparation failed: ' . $e->getMessage()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-18 14:46:56 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Begins a database transaction. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception If starting the transaction fails | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function beginTransaction() { | 
					
						
							|  |  |  |         if (!$this->pdo) { | 
					
						
							|  |  |  |             throw new Exception('No database connection.'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             return $this->pdo->beginTransaction(); | 
					
						
							|  |  |  |         } catch (PDOException $e) { | 
					
						
							|  |  |  |             throw new Exception('Failed to start transaction: ' . $e->getMessage()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Commits the current database transaction. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception If committing the transaction fails | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function commit() { | 
					
						
							|  |  |  |         if (!$this->pdo) { | 
					
						
							|  |  |  |             throw new Exception('No database connection.'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             return $this->pdo->commit(); | 
					
						
							|  |  |  |         } catch (PDOException $e) { | 
					
						
							|  |  |  |             throw new Exception('Failed to commit transaction: ' . $e->getMessage()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Rolls back the current database transaction. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception If rolling back the transaction fails | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function rollBack() { | 
					
						
							|  |  |  |         if (!$this->pdo) { | 
					
						
							|  |  |  |             throw new Exception('No database connection.'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             return $this->pdo->rollBack(); | 
					
						
							|  |  |  |         } catch (PDOException $e) { | 
					
						
							|  |  |  |             throw new Exception('Failed to rollback transaction: ' . $e->getMessage()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-06-28 17:05:32 +00:00
										 |  |  | } |