Menü schliessen
Created: July 14th 2023
Last updated: July 14th 2023
Categories: IT Development,  Php
Author: Tim Fürer

PHP: Funktion zum Vergleich von Verzeichnissen

Tags:  function,  guide,  PHP
Donation Section: Background
Monero Badge: QR-Code
Monero Badge: Logo Icon Donate with Monero Badge: Logo Text
82uymVXLkvVbB4c4JpTd1tYm1yj1cKPKR2wqmw3XF8YXKTmY7JrTriP4pVwp2EJYBnCFdXhLq4zfFA6ic7VAWCFX5wfQbCC

Das Vergleichen zweier Verzeichnisse auf Unterschiede ist eine typische Aufgabe, die in Bereichen wie Versionskontrolle, Datensicherung oder einfach zur Verfolgung von Änderungen anfallen kann. Heute werden wir uns eine schlichte PHP-Funktion ansehen, die diese Aufgabe vereinfacht.


FUNKTIONSÜBERBLICK

Die Funktion, die wir uns anschauen (nennen wir sie compareDirectories), nimmt zwei Verzeichnispfade entgegen und gibt ein Array zurück, das anzeigt, welche Dateien hinzugefügt, gelöscht oder verändert wurden.

Zunächst prüft die Funktion, ob die übergebenen Pfade tatsächlich Verzeichnisse sind. Das macht sie mit der in PHP eingebauten Funktion is_dir, die 'true' zurückgibt, wenn der Pfad ein Verzeichnis ist, und sonst 'false'.

if (!is_dir($dir1) || !is_dir($dir2)) {
    throw new InvalidArgumentException("Both parameters must be directory paths.");
}

Danach sammelt die Funktion Dateiinformationen aus beiden Verzeichnissen mit einer Hilfsfunktion, die wir getFiles nennen. Anschliessend vergleicht sie die Dateilisten, um hinzugefügte und gelöschte Dateien mittels der PHP-Funktion array_diff_key zu ermitteln.

$dir1Files = getFiles($dir1);
$dir2Files = getFiles($dir2);

$addedFiles = array_diff_key($dir2Files, $dir1Files);
$removedFiles = array_diff_key($dir1Files, $dir2Files);

Die Funktion array_intersect_key identifiziert dann die Dateien, die in beiden Verzeichnissen vorhanden sind.

$commonFiles = array_intersect_key($dir1Files, $dir2Files);

Um veränderte Dateien festzustellen, vergleicht die Funktion die md5_file-Hash-Werte der gemeinsamen Dateien in den Verzeichnissen mit einer foreach-Schleife.

$changedFiles = [];
foreach ($commonFiles as $file => $dir1Hash) {
    $dir2Hash = $dir2Files[$file];
    if ($dir1Hash !== $dir2Hash) {
        $changedFiles[$file] = $dir2Hash;
    }
}

Das Resultat ist ein Array, das hinzugefügte, gelöschte und veränderte Dateien enthält.

return ['added' => $addedFiles, 'removed' => $removedFiles, 'changed' => $changedFiles];

HILFSFUNKTION VERSTEHEN: GETFILES

Die Funktion getFiles spielt eine entscheidende Rolle bei der Sammlung der Dateiinformationen aus einem Verzeichnis. Schauen wir uns diese genauer an.

function getFiles($dir, $base = '') {
    $files = [];
    $dh = opendir($dir);
    while (($file = readdir($dh)) !== false) {
        if ($file === '.' || $file === '..') {
            continue;
        }
        
        $path = $dir . '/' . $file;
        $filePath = ($base ? $base . '/' : '') . $file;
        
        if (is_dir($path)) {
            $files += getFiles($path, $filePath);
        } else {
            $files[$filePath] = md5_file($path);
        }
    }
    closedir($dh);
    return $files;
}

Die Funktion öffnet zunächst das Verzeichnis mit opendir($dir). Dann beginnt eine while-Schleife, um die Einträge des Verzeichnisses mit readdir($dh) zu lesen. Die Dateinamen "." und ".." stehen immer für das aktuelle Verzeichnis und das Elternverzeichnis und werden daher mit continue übersprungen.

Bei jeder Datei prüft getFiles mit is_dir($path), ob es sich um ein Verzeichnis handelt. Wenn ja, ruft sie sich selbst rekursiv auf. Dies ermöglicht es der Funktion, nicht nur das Basisverzeichnis, sondern auch alle Unterverzeichnisse zu durchsuchen.

if (is_dir($path)) {
    $files += getFiles($path, $filePath);
}

Ist die Datei kein Verzeichnis, errechnet die Funktion mittels md5_file den MD5-Hash der Datei, was einen eindeutigen Identifikator für den aktuellen Zustand des Dateiinhalts liefert. Dieser Hash-Wert wird im Array $files gespeichert, wobei der relative Dateipfad als Schlüssel dient.

else {
    $files[$filePath] = md5_file($path);
}

Sind alle Dateien gelesen, schliesst die Funktion das Verzeichnis mit closedir($dh). Am Ende gibt die Funktion das Array $files zurück, welches alle Dateipfade und die zugehörigen MD5-Hashes enthält.


PRAKTISCHE ANWENDUNG

Diese Funktion kann beispielsweise in der Versionskontrolle nützlich sein. Wenn Sie ein Projekt mit häufigen Updates betreuen, kann diese Funktion dabei helfen, schnell zu erkennen, welche Dateien hinzugefügt, gelöscht oder verändert wurden. Es ist zu beachten, dass die Funktion nicht die genauen Änderungen innerhalb einer Datei ausgeben kann. Sie weist aber darauf hin, dass eine Änderung stattgefunden hat und eine genauere Überprüfung erforderlich ist.

Zusammengefasst ist diese PHP-Funktion ein praktisches Werkzeug für grundlegende "Diff-Checks". Sie nutzt eingebaute PHP-Funktionen und bietet eine einfache und effiziente Möglichkeit, Verzeichnisinhalte zu vergleichen.


VOLLSTÄNDIGER QUELLCODE

Hier ist der komplette Quellcode für die Funktion compareDirectories und ihre Hilfsfunktion getFiles. Sie können diese in Ihre PHP-Projekte einfügen und nach Belieben verwenden.

function compareDirectories($dir1, $dir2) {
    // Check if directories exist
    if (!is_dir($dir1) || !is_dir($dir2)) {
        throw new InvalidArgumentException("Both parameters must be directory paths.");
    }
    
    // Collect file data from directories
    $dir1Files = getFiles($dir1);
    $dir2Files = getFiles($dir2);
    
    // Compare the file arrays
    $addedFiles = array_diff_key($dir2Files, $dir1Files);
    $removedFiles = array_diff_key($dir1Files, $dir2Files);
    
    $commonFiles = array_intersect_key($dir1Files, $dir2Files);
    
    // Check if any common files have changed
    $changedFiles = [];
    foreach ($commonFiles as $file => $dir1Hash) {
        $dir2Hash = $dir2Files[$file];
        if ($dir1Hash !== $dir2Hash) {
            $changedFiles[$file] = $dir2Hash;
        }
    }
    
    return ['added' => $addedFiles, 'removed' => $removedFiles, 'changed' => $changedFiles];
}

function getFiles($dir, $base = '') {
    $files = [];
    $dh = opendir($dir);
    while (($file = readdir($dh)) !== false) {
        if ($file === '.' || $file === '..') {
            continue;
        }
        
        $path = $dir . '/' . $file;
        $filePath = ($base ? $base . '/' : '') . $file;
        
        if (is_dir($path)) {
            $files += getFiles($path, $filePath);
        } else {
            $files[$filePath] = md5_file($path);
        }
    }
    closedir($dh);
    return $files;
}