/ : invalider une session // se reduit a supprimer le dossier, sans risque de match partiel sur d'autres ids. // Toutes les fonctions sont silencieuses en cas d'echec (best-effort, jamais bloquant). if (!defined('PROSERVE_CACHE_DIR')) define('PROSERVE_CACHE_DIR', __DIR__ . '/../cache'); function cache_session_dir($sessionId) { return PROSERVE_CACHE_DIR . '/sess_' . intval($sessionId); } function cache_path($sessionId, $key) { $safe = preg_replace('/[^a-zA-Z0-9_-]/', '_', $key); return cache_session_dir($sessionId) . '/' . $safe . '.json'; } function cache_get($sessionId, $key) { if (intval($sessionId) <= 0) return null; $path = cache_path($sessionId, $key); if (!file_exists($path)) return null; $data = @file_get_contents($path); if ($data === false || $data === '') return null; return $data; } function cache_put($sessionId, $key, $jsonString) { if (intval($sessionId) <= 0) return; $dir = cache_session_dir($sessionId); if (!is_dir($dir)) @mkdir($dir, 0755, true); @file_put_contents(cache_path($sessionId, $key), $jsonString, LOCK_EX); } function cache_invalidate_session($sessionId) { $sid = intval($sessionId); if ($sid <= 0) return; $dir = cache_session_dir($sid); if (!is_dir($dir)) return; $files = glob($dir . '/*'); if (is_array($files)) { foreach ($files as $f) @unlink($f); } @rmdir($dir); } // ============================================================================= // DB-level cache for the materialized sessiondebriefs view (table session_debriefs_cache) // Populated by stored procedure rebuild_debrief_cache(p_session_id) created in migration 1.7.0. // ============================================================================= // Marque le cache DB d'une session comme stale (delete des lignes). // Appele depuis les endpoints d'evenements (storetriggerevent, storereactevent) // pour amortir le cout : la reconstruction se fait paresseusement au prochain // read via ensure_session_debriefs_cached_db, ou explicitement via rebuild... function invalidate_session_debrief_cache_db($db, $sessionId) { $sid = intval($sessionId); if ($sid <= 0 || $db === null) return; try { $stmt = $db->prepare("DELETE FROM session_debriefs_cache WHERE SessionId = ?"); $stmt->execute([$sid]); } catch (Exception $e) { // best-effort, ne jamais bloquer la requete metier } } // Reconstruit immediatement le cache DB d'une session (DELETE + INSERT via la proc). // Appele depuis session/stop, session/userleave, session/updateobjectives. function rebuild_session_debrief_cache_db($db, $sessionId) { $sid = intval($sessionId); if ($sid <= 0 || $db === null) return; try { $stmt = $db->prepare("CALL rebuild_debrief_cache(?)"); $stmt->execute([$sid]); $stmt->closeCursor(); } catch (Exception $e) { // best-effort } } // Garantit que le cache DB contient au moins une ligne pour la session. // Appele avant tout SELECT sur sessiondebriefs cote stats_object.php. // Si le cache a ete invalide par un evenement et n'a pas encore ete reconstruit, // cette fonction le reconstruit a la demande. function ensure_session_debriefs_cached_db($db, $sessionId) { $sid = intval($sessionId); if ($sid <= 0 || $db === null) return; try { $stmt = $db->prepare("SELECT 1 FROM session_debriefs_cache WHERE SessionId = ? LIMIT 1"); $stmt->execute([$sid]); $exists = $stmt->fetchColumn(); $stmt->closeCursor(); if ($exists === false) { rebuild_session_debrief_cache_db($db, $sid); } } catch (Exception $e) { // si la table n'existe pas (migration 1.7.0 pas encore appliquee), // on ne fait rien : le code legacy continue de marcher via la vue } } ?>