117 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| declare(strict_types=1);
 | |
| 
 | |
| /**
 | |
|  * delete_unused.php
 | |
|  *
 | |
|  * Löscht Bilddateien aus $imagedir, die in der DB nicht referenziert sind.
 | |
|  * Sicherheit:
 | |
|  * - CLI-only (kein Webzugriff)
 | |
|  * - Pfad-Härtung via realpath()
 | |
|  * - Dry-Run optional (default)
 | |
|  *
 | |
|  * Aufruf:
 | |
|  *   php delete_unused.php           # Dry-Run (zeigt nur an)
 | |
|  *   php delete_unused.php --apply   # wirklich löschen
 | |
|  */
 | |
| 
 | |
| if (PHP_SAPI !== 'cli') {
 | |
|     http_response_code(403);
 | |
|     exit('Forbidden');
 | |
| }
 | |
| 
 | |
| require_once __DIR__ . '/../config/config.php';
 | |
| 
 | |
| function out(string $msg): void
 | |
| {
 | |
|     fwrite(STDOUT, $msg . PHP_EOL);
 | |
| }
 | |
| function err(string $msg): void
 | |
| {
 | |
|     fwrite(STDERR, "ERROR: " . $msg . PHP_EOL);
 | |
|     exit(1);
 | |
| }
 | |
| 
 | |
| // Flags
 | |
| $apply = in_array('--apply', $argv, true);
 | |
| 
 | |
| // DB verbinden
 | |
| $conn = @new mysqli($GLOBALS['servername'], $GLOBALS['username'], $GLOBALS['password'], $GLOBALS['db']);
 | |
| if ($conn->connect_error)
 | |
|     err('DB-Verbindung fehlgeschlagen');
 | |
| 
 | |
| // Bildverzeichnis prüfen/härten
 | |
| $baseDir = realpath(__DIR__ . '/..');
 | |
| if ($baseDir === false)
 | |
|     err('BaseDir nicht gefunden');
 | |
| 
 | |
| $imgDirCfg = rtrim((string) $GLOBALS['imagedir'], '/');
 | |
| $imgDir = realpath(__DIR__ . '/../' . $imgDirCfg);
 | |
| if ($imgDir === false)
 | |
|     err('imagedir nicht gefunden: ' . $imgDirCfg);
 | |
| 
 | |
| // Verhindere, dass außerhalb des Projekts gelöscht wird
 | |
| if (strpos($imgDir, $baseDir) !== 0)
 | |
|     err('imagedir liegt außerhalb des Projekts: ' . $imgDir);
 | |
| 
 | |
| // Aus DB: genutzte Dateien (NULL/"" filtern)
 | |
| $used = [];
 | |
| $sql = 'SELECT image FROM wishes WHERE image IS NOT NULL AND image <> ""';
 | |
| $res = $conn->query($sql);
 | |
| if ($res === false)
 | |
|     err('Query fehlgeschlagen');
 | |
| 
 | |
| while ($row = $res->fetch_assoc()) {
 | |
|     $used[] = (string) $row['image'];
 | |
| }
 | |
| $res->free();
 | |
| $conn->close();
 | |
| 
 | |
| // In ein Set packen
 | |
| $usedSet = array_flip($used);
 | |
| 
 | |
| $deleted = 0;
 | |
| $kept = 0;
 | |
| $skipped = 0;
 | |
| 
 | |
| $it = new DirectoryIterator($imgDir);
 | |
| foreach ($it as $fileinfo) {
 | |
|     if ($fileinfo->isDot())
 | |
|         continue;
 | |
| 
 | |
|     $path = $fileinfo->getPathname();
 | |
|     if ($fileinfo->isDir()) {
 | |
|         $skipped++;
 | |
|         out("[skip-dir] " . $path);
 | |
|         continue;
 | |
|     }
 | |
| 
 | |
|     $name = $fileinfo->getFilename();
 | |
| 
 | |
|     if (isset($usedSet[$name])) {
 | |
|         $kept++;
 | |
|         out("[keep]     " . $name);
 | |
|         continue;
 | |
|     }
 | |
| 
 | |
|     // Nicht referenziert -> löschen (oder dry-run)
 | |
|     if ($apply) {
 | |
|         if (@unlink($path)) {
 | |
|             $deleted++;
 | |
|             out("[delete]   " . $name);
 | |
|         } else {
 | |
|             err('Konnte Datei nicht löschen: ' . $path);
 | |
|         }
 | |
|     } else {
 | |
|         out("[dry-run]  " . $name . "  (würde gelöscht)");
 | |
|     }
 | |
| }
 | |
| 
 | |
| out("");
 | |
| out("Summary:");
 | |
| out("  kept:    {$kept}");
 | |
| out("  skipped: {$skipped}");
 | |
| out("  deleted: {$deleted}" . ($apply ? "" : " (dry-run)"));
 | |
| 
 | |
| exit(0);
 |