$ttl); }; if ($mustReauth()) { $qs = http_build_query([ 'page' => 'reauth', 'redir' => ($_SERVER['REQUEST_URI'] ?? 'index.php?page=clean_uploads'), ]); header('Location: ?' . $qs); exit; } // Durcir PDO if (! isset($pdo) || ! ($pdo instanceof PDO)) { http_response_code(500); exit('PDO introuvable (DB non initialisée).'); } $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // Dossier uploads (chez toi: htdocs/public/uploads/) $uploadDir = rtrim(APP_HTDOCS, '/\\') . '/public/uploads/'; if (! is_dir($uploadDir)) { http_response_code(500); exit("Uploads introuvable: " . $uploadDir); } $uploadDir = rtrim($uploadDir, "/\\") . DIRECTORY_SEPARATOR; // Assurer PDO if (! isset($pdo) || ! ($pdo instanceof PDO)) { http_response_code(500); exit("PDO introuvable (DB non initialisée)."); } // ⚙️ Sources DB : tu peux en ajouter plusieurs (plusieurs tables/colonnes) $dbSources = [ [ 'sql' => "SELECT photo FROM praticien WHERE photo IS NOT NULL AND photo <> ''", 'col' => 'photo', ], // Exemple si tu as d'autres tables : // ['sql' => "SELECT image FROM articles WHERE image IS NOT NULL AND image <> ''", 'col' => 'image'], ]; // --- CSRF token --- if (empty($_SESSION['csrf_clean_uploads'])) { $_SESSION['csrf_clean_uploads'] = bin2hex(random_bytes(32)); } $csrf = $_SESSION['csrf_clean_uploads']; // --- Paramètres --- $limitList = 400; // pour éviter une page énorme si milliers de fichiers $action = $_POST['action'] ?? ''; $confirmed = isset($_POST['confirm_delete']) && $_POST['confirm_delete'] === '1'; // --- 1) Charger noms d'images depuis la DB --- $dbNames = []; foreach ($dbSources as $src) { $stmt = $pdo->query($src['sql']); $rows = $stmt->fetchAll(PDO::FETCH_COLUMN); foreach ($rows as $name) { $name = trim((string) $name); if ($name !== '') { $dbNames[$name] = true; } // set } } // --- 2) Scanner le dossier uploads (fichiers seulement) --- $files = @scandir($uploadDir); if (! is_array($files)) { http_response_code(500); exit('Impossible de lire le dossier uploads.'); } $orphans = []; // [ ['name'=>..., 'size'=>..., 'mtime'=>...] ] $totalOrphanSize = 0; foreach ($files as $f) { if ($f === '.' || $f === '..') { continue; } if ($f !== '' && $f[0] === '.') { continue; } // ignore fichiers cachés (.htaccess, .DS_Store, etc.) // ignore fichiers système fréquents (si tu veux garder explicite) if ($f === 'Thumbs.db') { continue; } $full = $uploadDir . $f; if (! is_file($full)) { continue; } // si le fichier n'est pas dans la base => orphelin if (! isset($dbNames[$f])) { $size = filesize($full); $mtime = filemtime($full); $orphans[] = [ 'name' => $f, 'size' => is_int($size) ? $size : 0, 'mtime' => is_int($mtime) ? $mtime : 0, ]; $totalOrphanSize += (is_int($size) ? $size : 0); } } // Tri : plus gros d'abord (pratique pour voir vite les gains) usort($orphans, fn($a, $b) => ($b['size'] <=> $a['size'])); // --- 3) Suppression si demandé --- $deleted = []; $errors = []; if ($action === 'delete') { // CSRF $postedCsrf = (string) ($_POST['csrf'] ?? ''); if (! hash_equals($csrf, $postedCsrf)) { $errors[] = "CSRF invalide. Recharge la page."; } elseif (! $confirmed) { $errors[] = "Coche la confirmation avant de supprimer."; } else { // On supprime uniquement les orphelins trouvés à l’instant T foreach ($orphans as $o) { $name = $o['name']; // Sécurité anti traversal if (str_contains($name, '/') || str_contains($name, '\\') || $name === '') { $errors[] = "Nom de fichier suspect ignoré: " . h($name); continue; } $full = $uploadDir . $name; // recheck réel if (! is_file($full)) { continue; } // s'assurer qu'on reste dans uploadDir $real = realpath($full); if ($real === false || strncmp($real, $uploadDir, strlen($uploadDir)) !== 0) { $errors[] = "Chemin refusé: " . h($name); continue; } if (@unlink($real)) { $deleted[] = $name; } else { $errors[] = "Impossible de supprimer: " . h($name); } } // Re-générer un nouveau token après action destructive $_SESSION['csrf_clean_uploads'] = bin2hex(random_bytes(32)); $csrf = $_SESSION['csrf_clean_uploads']; } } // Helpers function format_bytes(int $bytes): string { $units = ['B', 'KB', 'MB', 'GB', 'TB']; $i = 0; $val = (float) $bytes; while ($val >= 1024 && $i < count($units) - 1) {$val /= 1024; $i++;} return ($i === 0 ? (string) $bytes : number_format($val, 2, '.', ' ')) . ' ' . $units[$i]; } ?>
Aucun fichier orphelin trouvé ✅
| # | Fichier | Taille | Dernière modif |
|---|---|---|---|
Affichage limité à fichiers. (Mais la suppression concerne tous les orphelins trouvés.)