271 lines
7.4 KiB
PHP
271 lines
7.4 KiB
PHP
<?php
|
|
|
|
namespace classes;
|
|
|
|
use Exception;
|
|
use mysqli;
|
|
use objects\orders_o;
|
|
|
|
class db
|
|
{
|
|
public mysqli $conn;
|
|
private string $host;
|
|
private string $user;
|
|
private string $password;
|
|
private string $database;
|
|
private int $port = 3306;
|
|
private string $ssl_mode = 'DISABLED'; // mysqldump SSL mode (e.g., DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY)
|
|
|
|
public function __construct(array $config)
|
|
{
|
|
$this->host = $config['host'];
|
|
$this->user = $config['user'];
|
|
$this->password = $config['password'];
|
|
$this->database = $config['database'];
|
|
if (isset($config['port']) && is_numeric($config['port'])) {
|
|
$this->port = (int)$config['port'];
|
|
}
|
|
if (isset($config['ssl_mode']) && is_string($config['ssl_mode']) && $config['ssl_mode'] !== '') {
|
|
$this->ssl_mode = $config['ssl_mode'];
|
|
}
|
|
}
|
|
|
|
public static function getPDO(): \PDO
|
|
{
|
|
global $CONFIG_DB;
|
|
$port = $CONFIG_DB['port'] ?? 3306;
|
|
$dsn = "mysql:host={$CONFIG_DB['host']};port={$port};dbname={$CONFIG_DB['database']};charset=utf8mb4";
|
|
return new \PDO($dsn, $CONFIG_DB['user'], $CONFIG_DB['password'], [
|
|
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
|
|
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
|
|
\PDO::ATTR_EMULATE_PREPARES => false,
|
|
]);
|
|
}
|
|
|
|
public function testConnection(): bool
|
|
{
|
|
try {
|
|
$conn = new mysqli($this->host, $this->user, $this->password, $this->database, $this->port);
|
|
if ($conn->connect_error) {
|
|
return false;
|
|
}
|
|
$this->conn = $conn;
|
|
return true;
|
|
} catch (Exception) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function connect(): void
|
|
{
|
|
global $response;
|
|
try {
|
|
// Enable error reporting for mysqli to catch connection issues via exceptions
|
|
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
|
$this->conn = new mysqli($this->host, $this->user, $this->password, $this->database, $this->port);
|
|
if ($this->conn->connect_error) {
|
|
throw new Exception($this->conn->connect_error);
|
|
}
|
|
} catch (Exception $e) {
|
|
if ($response) {
|
|
$response->internal_server_error("Database connection failed: " . $e->getMessage());
|
|
} else {
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function escape_string(string $string): string
|
|
{
|
|
return $this->conn->real_escape_string($string);
|
|
}
|
|
|
|
public function close(): void
|
|
{
|
|
if (!isset($this->conn)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$this->conn->close();
|
|
} catch (\Throwable) {
|
|
}
|
|
}
|
|
|
|
public function get(string $table, int $id)
|
|
{
|
|
$sql = "SELECT * FROM $table WHERE id = $id";
|
|
$result = $this->query($sql);
|
|
return $this->fetch_assoc($result);
|
|
}
|
|
|
|
public function query(string $sql): \mysqli_result|bool
|
|
{
|
|
// If the connection is not established, connect
|
|
return $this->conn->query($sql);
|
|
}
|
|
|
|
public function fetch_assoc($result)
|
|
{
|
|
return $result->fetch_assoc();
|
|
}
|
|
|
|
public function list_objects(string $table): array
|
|
{
|
|
$sql = "SELECT * FROM $table";
|
|
$result = $this->query($sql);
|
|
return $this->fetch_all($result);
|
|
}
|
|
|
|
public function fetch_all($result)
|
|
{
|
|
return $result->fetch_all(MYSQLI_ASSOC);
|
|
}
|
|
|
|
public function list_objects_paginated(string $table, int $page, int $limit): array
|
|
{
|
|
$offset = ($page - 1) * $limit;
|
|
$sql = "SELECT * FROM $table LIMIT $limit OFFSET $offset";
|
|
$result = $this->query($sql);
|
|
return $this->fetch_all($result);
|
|
}
|
|
|
|
public function count_objects(string $table): int
|
|
{
|
|
$sql = "SELECT COUNT(*) FROM $table";
|
|
$result = $this->query($sql);
|
|
return $result->fetch_row()[0];
|
|
}
|
|
|
|
public function insert_id(): int
|
|
{
|
|
return $this->conn->insert_id;
|
|
}
|
|
|
|
public function conn(): mysqli
|
|
{
|
|
return $this->conn;
|
|
}
|
|
|
|
public function prepare(string $sql): false|\mysqli_stmt
|
|
{
|
|
return $this->conn->prepare($sql);
|
|
}
|
|
|
|
public function num_rows(\mysqli_result|bool $result): int|string
|
|
{
|
|
return $result->num_rows;
|
|
}
|
|
|
|
public function getUsername()
|
|
{
|
|
return $this->user;
|
|
}
|
|
|
|
public function getPassword()
|
|
{
|
|
return $this->password;
|
|
}
|
|
|
|
public function getHost()
|
|
{
|
|
return $this->host;
|
|
}
|
|
|
|
public function getDatabase()
|
|
{
|
|
return $this->database;
|
|
}
|
|
|
|
public function getPort(): int
|
|
{
|
|
return $this->port;
|
|
}
|
|
|
|
public function getSslMode(): string
|
|
{
|
|
return $this->ssl_mode;
|
|
}
|
|
|
|
public function backupDatabase(string $path): bool
|
|
{
|
|
$mode = strtoupper(trim($this->ssl_mode));
|
|
$sslFlag = '';
|
|
switch ($mode) {
|
|
case 'DISABLED':
|
|
$sslFlag = '--skip-ssl';
|
|
break;
|
|
case 'PREFERRED':
|
|
$sslFlag = '';
|
|
break;
|
|
case 'REQUIRED':
|
|
case 'VERIFY_CA':
|
|
case 'VERIFY_IDENTITY':
|
|
default:
|
|
$sslFlag = '--ssl';
|
|
break;
|
|
}
|
|
|
|
$host = escapeshellarg($this->host);
|
|
$user = escapeshellarg($this->user);
|
|
$db = escapeshellarg($this->database);
|
|
$port = (int)$this->port;
|
|
$sslPart = $sslFlag !== '' ? ($sslFlag . ' ') : '';
|
|
$command = "mysqldump {$sslPart}--single-transaction --quick --routines --triggers --events --hex-blob -h $host -P $port -u $user $db";
|
|
|
|
$directory = dirname($path);
|
|
if (!is_dir($directory) && !mkdir($directory, 0770, true) && !is_dir($directory)) {
|
|
return false;
|
|
}
|
|
|
|
$environment = array_merge(getenv() ?: [], $_ENV);
|
|
$environment['MYSQL_PWD'] = $this->password;
|
|
$descriptors = [
|
|
0 => ['pipe', 'r'],
|
|
1 => ['file', $path, 'w'],
|
|
2 => ['pipe', 'w'],
|
|
];
|
|
|
|
$process = proc_open($command, $descriptors, $pipes, null, $environment);
|
|
if (!is_resource($process)) {
|
|
return false;
|
|
}
|
|
|
|
fclose($pipes[0]);
|
|
$stderr = stream_get_contents($pipes[2]);
|
|
fclose($pipes[2]);
|
|
$return = proc_close($process);
|
|
|
|
if ($return !== 0 && is_string($stderr) && $stderr !== '') {
|
|
@file_put_contents($path . '.error.log', $stderr);
|
|
}
|
|
|
|
return $return === 0 && is_file($path) && filesize($path) !== false;
|
|
}
|
|
|
|
public function getView(string $view): array
|
|
{
|
|
$sql = "SELECT * FROM $view";
|
|
$result = $this->query($sql);
|
|
return $this->fetch_all($result);
|
|
}
|
|
|
|
/**
|
|
* Generate a fake ID for a table.
|
|
* Fake ids are not stored in the database, they are generated on every request starting from -1.
|
|
* This is useful for testing purposes.
|
|
* @see orders_o::simulateOrderFromXLVask()
|
|
* @param string $table
|
|
* @return int
|
|
*/
|
|
public function getNextFakeId(string $table): int
|
|
{
|
|
// This is a static variable that will be shared across all instances of the class
|
|
static $fakeId = 0; // Start from 0 and decrement on each call
|
|
$fakeId--;
|
|
return $fakeId;
|
|
}
|
|
|
|
|
|
}
|