CHANGEMENTS MAJEURS: - Fusion des 3 bases de données (uof_frontal, uof_linet, logs) en une seule base 'cleo' - Migration vers PDO avec pattern Singleton et requêtes préparées - Configuration externalisée via variables d'environnement (.env) - Séparation application (dva-front) et base de données (maria3) SÉCURITÉ: - Suppression des credentials en dur dans le code - Implémentation de la classe Database avec gestion d'erreurs sécurisée - Protection contre les injections SQL via requêtes préparées INFRASTRUCTURE: - Container dva-front : MariaDB supprimé, application PHP uniquement - Container maria3 : Base de données centralisée MariaDB 11.4 - Script de déploiement optimisé (deploy-cleo-fast.sh) CORRECTIONS: - Ajout des tables manquantes (z_sessions, z_stats, marches_listes) - Compatibilité PDO (fetch_assoc → fetch(PDO::FETCH_ASSOC)) - Suppression des commentaires debug dans les réponses AJAX - Permissions fichiers (.env 644, logs 777 avec owner nobody) DOCUMENTATION: - Mise à jour README.md avec architecture actuelle - Migration README.md marqué comme complété - TODO.md avec état d'avancement et prochaines étapes (PROD IN4) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
229 lines
6.9 KiB
PHP
229 lines
6.9 KiB
PHP
<?php
|
|
|
|
class Database {
|
|
private static $instance = null;
|
|
private $pdo;
|
|
private $host;
|
|
private $dbname;
|
|
private $username;
|
|
private $password;
|
|
private $charset = 'utf8mb4';
|
|
private $options = [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
|
|
];
|
|
|
|
private function __construct() {
|
|
$this->loadEnvironment();
|
|
$this->connect();
|
|
}
|
|
|
|
private function loadEnvironment() {
|
|
$envFile = dirname(__DIR__) . '/.env';
|
|
if (file_exists($envFile)) {
|
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($lines as $line) {
|
|
if (strpos(trim($line), '#') === 0) continue;
|
|
|
|
list($name, $value) = explode('=', $line, 2);
|
|
$name = trim($name);
|
|
$value = trim($value);
|
|
|
|
if (!isset($_ENV[$name])) {
|
|
putenv(sprintf('%s=%s', $name, $value));
|
|
$_ENV[$name] = $value;
|
|
$_SERVER[$name] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->host = $_ENV['DB_HOST'] ?? 'localhost';
|
|
$this->dbname = $_ENV['DB_DATABASE'] ?? 'cleo';
|
|
$this->username = $_ENV['DB_USERNAME'] ?? 'root';
|
|
$this->password = $_ENV['DB_PASSWORD'] ?? '';
|
|
}
|
|
|
|
private function connect() {
|
|
try {
|
|
$dsn = "mysql:host={$this->host};dbname={$this->dbname};charset={$this->charset}";
|
|
$this->pdo = new PDO($dsn, $this->username, $this->password, $this->options);
|
|
|
|
if ($_ENV['LOG_SQL'] ?? false) {
|
|
$this->logConnection(true);
|
|
}
|
|
} catch (PDOException $e) {
|
|
if ($_ENV['APP_DEBUG'] ?? false) {
|
|
throw new Exception("Erreur de connexion à la base de données: " . $e->getMessage());
|
|
} else {
|
|
throw new Exception("Erreur de connexion à la base de données");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function getInstance() {
|
|
if (self::$instance === null) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
public function getPDO() {
|
|
return $this->pdo;
|
|
}
|
|
|
|
public function query($sql, $params = []) {
|
|
$start = microtime(true);
|
|
|
|
try {
|
|
if (empty($params)) {
|
|
$stmt = $this->pdo->query($sql);
|
|
} else {
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
}
|
|
|
|
$this->logQuery($sql, $params, microtime(true) - $start);
|
|
return $stmt;
|
|
|
|
} catch (PDOException $e) {
|
|
$this->logError($sql, $params, $e->getMessage());
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
public function fetchAll($sql, $params = []) {
|
|
$stmt = $this->query($sql, $params);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
public function fetchOne($sql, $params = []) {
|
|
$stmt = $this->query($sql, $params);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
public function fetchColumn($sql, $params = [], $column = 0) {
|
|
$stmt = $this->query($sql, $params);
|
|
return $stmt->fetchColumn($column);
|
|
}
|
|
|
|
public function insert($table, $data) {
|
|
$columns = array_keys($data);
|
|
$values = array_map(function($col) { return ':' . $col; }, $columns);
|
|
|
|
$sql = sprintf(
|
|
"INSERT INTO %s (%s) VALUES (%s)",
|
|
$table,
|
|
implode(', ', $columns),
|
|
implode(', ', $values)
|
|
);
|
|
|
|
$this->query($sql, $data);
|
|
return $this->pdo->lastInsertId();
|
|
}
|
|
|
|
public function update($table, $data, $where, $whereParams = []) {
|
|
$set = [];
|
|
foreach ($data as $column => $value) {
|
|
$set[] = "$column = :set_$column";
|
|
}
|
|
|
|
$sql = sprintf(
|
|
"UPDATE %s SET %s WHERE %s",
|
|
$table,
|
|
implode(', ', $set),
|
|
$where
|
|
);
|
|
|
|
$params = [];
|
|
foreach ($data as $column => $value) {
|
|
$params["set_$column"] = $value;
|
|
}
|
|
$params = array_merge($params, $whereParams);
|
|
|
|
$stmt = $this->query($sql, $params);
|
|
return $stmt->rowCount();
|
|
}
|
|
|
|
public function delete($table, $where, $params = []) {
|
|
$sql = "DELETE FROM $table WHERE $where";
|
|
$stmt = $this->query($sql, $params);
|
|
return $stmt->rowCount();
|
|
}
|
|
|
|
public function beginTransaction() {
|
|
return $this->pdo->beginTransaction();
|
|
}
|
|
|
|
public function commit() {
|
|
return $this->pdo->commit();
|
|
}
|
|
|
|
public function rollback() {
|
|
return $this->pdo->rollBack();
|
|
}
|
|
|
|
public function lastInsertId() {
|
|
return $this->pdo->lastInsertId();
|
|
}
|
|
|
|
private function logQuery($sql, $params, $executionTime) {
|
|
// Debug désactivé pour les requêtes SQL
|
|
return;
|
|
}
|
|
|
|
private function logError($sql, $params, $error) {
|
|
// On garde seulement le log d'erreur dans error_log, pas de debug
|
|
error_log("SQL Error: $error | Query: $sql");
|
|
}
|
|
|
|
private function logConnection($success) {
|
|
// Debug désactivé pour les connexions
|
|
return;
|
|
}
|
|
}
|
|
|
|
function getinfos($sql, $dbn = "gen", $format = "normal") {
|
|
try {
|
|
$db = Database::getInstance();
|
|
$result = $db->fetchAll($sql);
|
|
|
|
if (strtolower($format) == "json") {
|
|
return json_encode($result);
|
|
}
|
|
return $result;
|
|
|
|
} catch (Exception $e) {
|
|
if ($_ENV['APP_DEBUG'] ?? false) {
|
|
error_log("Erreur getinfos: " . $e->getMessage());
|
|
}
|
|
return ($format == "json") ? json_encode([]) : [];
|
|
}
|
|
}
|
|
|
|
function qSQL($sql, $dbn = "gen", $lastid = false) {
|
|
try {
|
|
$db = Database::getInstance();
|
|
|
|
$queryType = strtoupper(substr(trim($sql), 0, 6));
|
|
|
|
if ($queryType === 'INSERT' || $queryType === 'UPDATE' || $queryType === 'DELETE') {
|
|
$stmt = $db->query($sql);
|
|
|
|
if ($lastid && $queryType === 'INSERT') {
|
|
return $db->lastInsertId();
|
|
}
|
|
|
|
return $stmt;
|
|
} else {
|
|
return $db->query($sql);
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
if ($_ENV['APP_DEBUG'] ?? false) {
|
|
error_log("Erreur qSQL: " . $e->getMessage());
|
|
}
|
|
return false;
|
|
}
|
|
} |