phpBuddy

PHP und Sex

Was PHP und Sex gemeinsam haben? Beides sollte man niemals ohne ausreichenden Schutz praktizieren! Diese Rubrik gibt wichtige Tipps, wie man PHP gegen Attacken absichert und wie man sichere Scripts programmiert. Pflichtlektüre!

Sie sind hier: Startseite Verzeichnisse vergleichen
Verzeichnisse vergleichen, Dateileichen finden

Verzeichnisse vergleichen, Dateileichen finden

Gelegentlich kommt es vor, dass man zwei Verzeichnisse vergleichen möchte um festzustellen, ob sich darin identische Dateien befinden. Ein klassischer Fall wäre hier z.B. eine Galerie, die Thumbnails in einem Verzeichnis liegen hat und die zugehörigen Bilder in hoher Auflösung in einem anderen Verzeichnis. Oftmals speichert man in einer Datenbank nur den Name des Bildes und sucht sich die Bilder aus den Verzeichnissen zusammen. Löscht man jetzt ein Bild, oder lädt ein Bild über ein Webinterface auf den Server, und es kommt während dieser Operation zu einem Problem, kann es vorkommen, dass man nur das Image oder nur das Thumbnail auf dem Server hat. Ohne passendes Gegenstück handelt es sich hier um eine Dateileiche.

Jetzt gibt es verschiedene Möglichkeiten die Verzeichnisse für Thumbs und Images zu vergleichen. Am häufigsten trifft man wohl diese, dass aus einem Verzeichnis alle Dateien ermittelt werden und dann in einer Schleife Datei für Datei mit file_exists im anderen Verzeichnis das Gegenstück gesucht wird. Das muss man dann in beide Richtungen machen, da ja entweder Thumb oder Image fehlen kann.
Diese Variante mit der Schleife und bei jedem Durchlauf extra eine Funktion aufzurufen -und das 2 Mal für Thumbs und Images- und im Fall eines Treffers dann den Dateiname zur späteren Verarbeitung umzuladen, ist schrecklich Ressource verschwendend und unperformant, sprich langsam, besonders bei vielen Dateien.

Stattdessen möchte ich hier eine sehr schlichte Variante vorstellen, die zum einen schnell ist, ohne Schleife auskommt und im Prinzip gerade mal aus 3 PHP-Funktionen besteht!
Als Beispiel nehmen wir mal eine Galerie her, die diese Struktur hat:
Images: testbilder/Tierwelt/images/
Thumbs: testbilder/Tierwelt/thumbs/
Der Inhalt beider Ordner:

Ordner images:
Array
(
    [0] => echse.jpg
    [1] => eisbaer.jpg
    [2] => hirsch.jpg
    [3] => hundeshow.jpg
    [4] => katze.jpg
    [5] => panda.jpg
    [6] => schimpanse.jpg
    [7] => schlange.jpg
    [8] => steak.jpg
)
 
 
Ordner thumbs:
Array
(
    [0] => echse.jpg
    [1] => eisbaer.jpg
    [2] => hirsch.jpg
    [3] => hundeshow2.jpg
    [4] => panda.jpg
    [5] => schimpanse.jpg
    [6] => schlange2.jpg
    [7] => steak.jpg
)

In images und thumbs liegen also die Dateien mit den nicht ganz so identischen Namen. Wie zu sehen ist, gibt es ein Bild hundeshow.jpg aber ohne Thumbnail, sowie schlange.jpg ohne Thumb. Stattdessen gibt es Thumbs hundeshow2.jpg und schlang2.jpg aber ohne Image. Auch die katze.jpg steht ziemlich allein im Images Ordner.
Hier nun mal der Code zum aufspüren der Dateileichen und anschließend die Erklärung, was der Code macht.

$images = glob( 'testbilder/Tierwelt/images/*.jpg' );
$thumbs = glob( 'testbilder/Tierwelt/thumbs/*.jpg' );
 
$images = array_map( 'basename', $images );
$thumbs = array_map( 'basename', $thumbs );
 
$ohne_image = array_diff( $thumbs, $images );
$ohne_thumb = array_diff( $images, $thumbs );

Die ersten 4 Zeilen könnte man auf 2 Zeilen reduzieren, aber so ist es etwas übersichtlicher. Reduziert hätten wir also gerade mal 4 Zeilen Code, um Abweichungen in beiden Verzeichnissen zu entdecken, dazu keine unnötige Schleife und das namentliche Aussortieren der Dateileichen ist bereits erledigt.
Lassen wir uns mal $ohne_image und $ohne_thumb ausgeben erhalten wir folgendes:

Folgende Bilder haben kein zugehöriges Thumbnail:
Array
(
    [3] => hundeshow.jpg
    [4] => katze.jpg
    [7] => schlange.jpg
)
 
 
Folgende Thumbnails haben kein zugehöriges Bild:
Array
(
    [3] => hundeshow2.jpg
    [6] => schlange2.jpg
)

Die Ausgabe stimmt also tatsächlich mit der Liste der Dateien überein. Statt irgend welche Schleifen und Funktionen zum ausfiltern, erledigen die 3 mehr oder weniger selten benutzten, aber sehr mächtigen PHP-Funktionen.

Wer meine Tutorials kennt, der hat schon häufiger die Funktion glob gesehen. Damit kann man auf einfache Art ein Verzichnis auslesen und die Dateinamen werden als Array zurück geliefert. Eine detaillierte Beschreibung spare ich mir hier, weil dies bereits im Tutorial Zufallsbild auf Seite ausgeben geschehen ist.

array_map erwartet mindestens 2 Parameter. Der Erste ist eine sogenannte Callback Funktion und die Zweite ist das Array, auf die die Callback angewendet wird. Anschließend wird das so bearbeitete Array zurück gegeben.
Diese Callback Funktion kann jede PHP-interne, aber auch eigene Funktionen sein und sie wird auf jedes Element des Array von Parameter 2 angewandt. In diesem Fall benutzen wir als Callback die Funktion basename, die aus einem übergebenen Pfad den Dateiname extrahiert.
Aus "testbilder/Tierwelt/images/eisbaer.jpg" wird also "eisbaer.jpg". Das wird auf jede Datei in beiden Verzeichnissen angewandt und somit erhalten wir eine Liste der Dateinamen aus den Verzeichnissen.

Statt nun zu schauen, welche Dateien gleich sind und die Fehlenden aufzuspüren, vergleichen wir einfach die Arrays miteinander und lassen uns von PHP direkt die Array-Elemente (Dateinamen) liefern, die nicht in beiden Arrays vorkommen. Genau das macht die Funktion array_diff für uns. Das diff ist die Kurzform für Difference, zu Deutsch Abweichung, Unterschied. Dabei wird das Array, das als erster Parameter übergeben wurde, mit dem Zweiten verglichen und alle Elemente, die im Zweiten nicht vorkommen, werden als Element eines neuen Arrays zurückgeliefert. So können wir feststellen, welche Images keine Thumbs haben und für den umgekehrten Fall drehen wir einfach die Reihenfolge der Arrays für array_diff um.

Da nun klar ist, welche Dateien in welchem Ordner kein Gegenstück haben, kann man die Arrays $ohne_image und $ohne_thumb verarbeiten und das Problem aus der Welt schaffen.

Ziemlich beeindruckend, wie ressourceschonend, performant und einfach man doch manchmal an's Ziel kommen kann, wenn man nur die richtigen PHP Funktionen kennt, was?! ;-)


Vielen Dank für's Lesen und bis zum nächsten Kurztipp,
Andreas Skodzek

Beispiel herunterladen (650 KB)