PHP/Tutorials/Rekursives Löschen von Verzeichnissen
Irgendwann stößt man auf das Problem in PHP, dass man ein Verzeichnis löschen will, das jedoch schon einen Inhalt besitzt. rmdir versagt dabei jedoch, da es nur leere Verzeichnisse löschen kann.
Hier wird nun eine Funktion vorgestellt, die Verzeichnisse rekursiv abarbeitet und zuerst alle Dateien löscht. Danach wird das Verzeichnis selbst mit rmdir
gelöscht.
Information
Anwendungsbeispiel
<?php
// rec_rmdir - loesche ein Verzeichnis rekursiv
// Rueckgabewerte:
// 0 - alles ok
// -1 - kein Verzeichnis
// -2 - Fehler beim Loeschen
// -3 - Ein Eintrag eines Verzeichnisses war keine Datei und kein Verzeichnis und
// kein Link
function rec_rmdir ($path) {
// schau' nach, ob das ueberhaupt ein Verzeichnis ist
if (!is_dir ($path)) {
return -1;
}
// oeffne das Verzeichnis
$dir = @opendir ($path);
// Fehler?
if (!$dir) {
return -2;
}
// gehe durch das Verzeichnis
while ($entry = @readdir($dir)) {
// wenn der Eintrag das aktuelle Verzeichnis oder das Elternverzeichnis
// ist, ignoriere es
if ($entry == '.' || $entry == '..') continue;
// wenn der Eintrag ein Verzeichnis ist, dann
if (is_dir ($path.'/'.$entry)) {
// rufe mich selbst auf
$res = rec_rmdir ($path.'/'.$entry);
// wenn ein Fehler aufgetreten ist
if ($res == -1) { // dies duerfte gar nicht passieren
@closedir ($dir); // Verzeichnis schliessen
return -2; // normalen Fehler melden
} else if ($res == -2) { // Fehler?
@closedir ($dir); // Verzeichnis schliessen
return -2; // Fehler weitergeben
} else if ($res == -3) { // nicht unterstuetzer Dateityp?
@closedir ($dir); // Verzeichnis schliessen
return -3; // Fehler weitergeben
} else if ($res != 0) { // das duerfe auch nicht passieren...
@closedir ($dir); // Verzeichnis schliessen
return -2; // Fehler zurueck
}
} else if (is_file ($path.'/'.$entry) || is_link ($path.'/'.$entry)) {
// ansonsten loesche diese Datei / diesen Link
$res = @unlink ($path.'/'.$entry);
// Fehler?
if (!$res) {
@closedir ($dir); // Verzeichnis schliessen
return -2; // melde ihn
}
} else {
// ein nicht unterstuetzer Dateityp
@closedir ($dir); // Verzeichnis schliessen
return -3; // tut mir schrecklich leid...
}
}
// schliesse nun das Verzeichnis
@closedir ($dir);
// versuche nun, das Verzeichnis zu loeschen
$res = @rmdir ($path);
// gab's einen Fehler?
if (!$res) {
return -2; // melde ihn
}
// alles ok
return 0;
}
?>
<?php
// importiere die Datei mit der Funktion
require 'recrmdir.inc.php';
// loesche das Verzeichnis /tmp/test_verzeichnis
$res = rec_rmdir ('/tmp/test_verzeichnis');
// wurde das Verzeichnis korrekt gelöscht
switch ($res) {
case 0:
// das Verzeichnis wurde korrekt gelöscht
break;
case -1:
// das war kein Verzeichnis
break;
case -2:
// ein Fehler ist aufgetreten
break;
case -3:
// die Funktion ist über einen Dateityp gestolpert, den sie nicht kennt
break;
default:
// die Funktion hat irgend etwas zurückgegeben, was sie eigentlich nicht sollte
break;
}
?>
Die Funktion rec_rmdir
erwartet einen Parameter $path
. Dieser Parameter enthält das Verzeichnis, das zu löschen ist. Als erstes wird in der Funktion geprüft, ob der angegebene Pfad auch wirklich existiert und ein Verzeichnis ist. Wenn nicht, wird der Rückgabewert -1 zurückgegeben.
Daraufhin wird das Verzeichnis mit Hilfe der opendir-Funktion geöffnet. Wenn dieser Aufruf fehlschlägt, dann wird -2 zurückgegeben. Dieser Aufruf kann nur dann fehlschlagen, wenn der Benutzer nicht genügend Dateirechte hat, um auf dieses Verzeichnis zuzugreifen.
Danach wird mit einer while-Schleife das Verzeichnis Eintrag für Eintrag abgearbeitet. Da readdir auch das aktuelle Verzeichnis (.) und das Elternverzeichnis (..) zurückgibt, muss sofort geprüft werden, ob der zurückgelieferte Eintrag nicht eines der beiden ist. Wenn dies zutrifft, werden diese ignoriert, denn sonst würde man eine Endllosschleife erzeugen.
Jetzt wird geprüft, ob der Eintrag ein Verzeichnis ist. (if (is_dir ($path.'/'.$entry)))
Wenn das der Fall ist, ruft die Funktion sich selbst auf mit diesem Eintrag als Parameter. Der Rückgabewert wird auf Fehler überprüft. Wenn der Rückgabewert -1 ist, dann dürfte das eigentlich nicht passieren, da eine Zeile zuvor überprüft wurde, ob das ein Verzeichnis ist und dieser Rückgabewert von rec_rmdir
zurückgegeben wird, wenn das kein Verzeichnis ist. Dieser Fehler wird hier stillschweigend ignoriert und es wird abgebrochen, indem -2 zurückgegeben wird. Dieser Rückgabewert bedeutet, dass irgendein Fehler beim Löschen selbst aufgetreten ist. Wenn der Aufruf der Funktion -2 oder -3 (es gab ein Objekt im Dateisystem, das diese Funktion nicht kennt) zurückliefert wird das "weitergegeben". Wenn der Rückgabewert der Funktion jetzt noch etwas anderes außer 0 (alles in Ordnung) ist, dann wird wieder der generische Fehlercode -2 zurückgegeben. Vor dem Zurückgeben des Fehlercodes wird das Verzeichnis noch geschlossen.
Falls dieser Eintrag eine Datei (else if (is_file ($path.'/'.$entry))
oder ein symbolischer Link (|| is_link ($path.'/'.$entry)))
ist, dann wird versucht, diese Datei normal per unlink zu löschen. Falls dies fehlschlägt, wird das Verzeichnis geschlossen und der generische Fehlercode -2 zurückgegeben.
Wenn dieser Eintrag werder ein Verzeichnis noch eine Datei noch ein symbolischer Link ist (else), dann wird der Fehlercode -3 zurückgegeben. Dies dürfte bei normalen Anwendungen nicht auftreten.
Schließlich wird das Verzeichnis geschlossen. Der Rückgabewert wird nicht überprüft, da das Ergebnis dieses Vorgangs nicht für die weitere Ausführung kritisch ist. (Wenn das Verzeichnis nicht korrekt geschlossen werden konnte, dann ist es noch offen und das Löschen schlägt fehl, daher ist es redundant, hier eine Fehlerbehandlung einzuführen)
Am Ende wird mit der rmdir
-Funktion das Verzeichnis gelöscht. Wenn diese Funktion einen Fehler liefert, dann wird der generische Fehlercode -2 zurückgeliefert. Falls dies nicht der Fall war, wird der Code 0 zurückgeliefert, was signalisiert, dass die Funktion erfolgreich war.
Bei den Aufrufen der Dateisystemfunktionen wurde immer der Operator @ verwendet. Dieser unterdrückt alle Fehlermeldungen, die diese Funktion eventuell ausgibt. Es ist im Allgemeinen nicht sinnvoll, Fehlermeldungen über Systemfunktionen an den Benutzer weiterzugeben, dies könnte ein Sicherheitsrisiko darstellen. Bei einer korrekten Serverkonfiguration ist log_errors aktiviert und alle Fehler werden mitprotokolliert und können vom Betreuer nachgelesen werden. Bei der Entwicklung von Webanwendungen kann es jedoch von Vorteil sein, dass die Fehlermeldungen angezeigt werden. Daher kann dann dieser Operator entfernt werden.
Es werden nur Dateien, symbolische Links und Verzeichnisse gelöscht. Wenn zusätzlich auch noch Gerätedateien, FIFOs oder Sockets gelöscht werden sollen, dann muss die entsprechende Abfrage entfernt werden.
Weblinks
Die folgenden Stellen werden empfohlen, um das obige Beispiel besser zu verstehen, oder um weitere Möglichkeiten und Details zu erfahren.