11.5 Text_Diff 

Besprochene Version: 0.0.4 | Lizenz: LGPL |
Klassendatei(en): Text/Diff.php; Text/Diff3.php; Text/Diff/Renderer.php; Text/Diff/ Renderer/inline.php; Text/Diff/Renderer/unified.php |
Text_Diff ist ein Paket, mit dem Sie die Funktionalität des diff-Befehls implementieren können. Dieser ist unter vielen Unix-Derivaten vorhanden und bietet die spannende Möglichkeit, zwei bzw. als diff3 drei Textdateien zeilenweise miteinander zu vergleichen. Das ist immer dann besonders praktisch, wenn Sie in einem langen Quelltext nur eine »Kleinigkeit« geändert haben, danach nichts mehr funktioniert und Sie die Stellen nicht mehr finden, an denen Sie etwas geändert haben. Das setzt natürlich voraus, dass Sie vorher eine Sicherheitskopie erstellt haben.
Da das Paket ursprünglich für die Nutzung in Wikis und für den Vergleich von Wiki-Dateien gedacht war, kennt es zwei Ausgabe-Modi. Zum einen können die ermittelten Daten gleich in Wiki-Syntax ausgegeben werden, um eine entsprechende Integration möglichst einfach zu gestalten. Die zweite Variante ist die Ausgabe als einfacher Text mit HTML-Tags.
Für die folgenden Beispiele habe die beiden Dateien aus Tabelle 11.4 genutzt.
Zeile | eins.text | zwei.txt |
1 2 3 4 5 6 | Homer Bart Lisa Ned Tod Marge | Homer Bard Lisa Ned Tod Grampa |
Um die Unterschiede zwischen den beiden Texten zu ermitteln, müssen diese erst als Array, z. B. mit file(), eingelesen werden. Diese beiden Arrays werden dann an den Konstruktor Text_Diff() übergeben. Dabei wird sofort ausgewertet, ob Unterschiede zwischen den beiden Datensätzen ermittelt werden können.
Ist das der Fall, können die Daten mithilfe des inline- oder des unified-Renderers ausgegeben werden. Der inline-Renderer, den ich nachfolgend nutzen werde, liefert die Daten in HTML-Syntax zurück. Die unified-Variante generiert dementsprechend den Wiki-Code.
// Einbinden der benoetigten Dateien require_once ('Text/Diff.php'); // Quelldateien einlesen $zeilen1 = file('eins.txt'); $zeilen2 = file('zwei.txt'); // Objekt instanziieren $diff = new Text_Diff($zeilen1, $zeilen2); // Konnten Unterschiede gefunden werden? if (false===$diff->isEmpty()) { // Renderer-Objekt ableiten und Daten ausgeben require_once 'Text/Diff/Renderer.php'; require_once 'Text/Diff/Renderer/inline.php'; $renderer = new Text_Diff_Renderer_inline(); $res= $renderer->render($diff); echo nl2br($res); } else { echo 'Es wurden keine Unterschiede festgestellt'; }
Listing 11.8 Ermitteln von Unterschieden in Texten mit Text_Diff
Nachdem die Daten in Listing 11.8 an den Konstruktor übergeben worden sind, ermittelt dieser sofort, ob es Unterschiede in den Datensätzen gibt. Ob welche gefunden wurden, wird hier mit der Methode isEmpty() geprüft. Liefert diese ein logisches Falsch zurück, wurden Unterschiede gefunden, die dann ausgegeben werden. In dem true-Teil der if-Abfrage werden zunächst die Dateien eingebunden, die zur Ausgabe der Daten benötigt werden. Möchten Sie auf den unified-Renderer zurückgreifen, müsste an dieser Stelle 'Text/Diff/Renderer/unified.php' eingebunden werden. Des Weiteren müsste ein Text_Diff_Renderer_unified-Objekt an Stelle eines Text_Diff_Renderer_inline-Objekts abgeleitet werden.
Das Text_Diff-Objekt wird an die Methode render() des Renderer-Objekts übergeben, die die Daten auswertet und aufbereitet. Da die Zeilenumbrüche aus der ursprünglichen Datei übernommen werden, greife ich auf nl2br() zurück, um die Daten für die Bildschirmausgabe aufzubereiten, die Sie in Abbildung 11.5 sehen können.
Abbildung 11.5 Die Unterschiede in den beiden Dateien
In der linken Spalte finden Sie alle Zeilen aus der Datei eins.txt. Zeilen, die sich von denen in der Datei zwei.txt unterscheiden, sind links jeweils durchgestrichen, und rechts daneben ist die Zeile aus zwei.txt zu finden. Das eigentliche Unterscheidungsmerkmal, wohin eine Zeile gehört, ist aber nicht die Position, sondern die Formatierung (unter- oder durchgestrichen), was sich besonders dann bemerkbar macht, wenn in der zweiten Datei eine zusätzliche Zeile vorhanden ist.
Sollten sich in einer Zeile nur einzelne Buchstaben geändert haben, erkennt der Renderer das und markiert nur die geänderten Zeichen.
Sollte Ihnen diese Auswertung nicht zusagen, können Sie die Daten auch selbst für die Ausgabe aufbereiten. Die Methode getDiff(), die zum Text_Diff-Objekt gehört, liefert Ihnen alles, was Sie brauchen.
Sie gibt ein Array aus Objekten zurück. Hierbei kann es sich um Objekte der folgenden Klassen handeln:
- Text_Diff_Op_change Enthält Zeilen, die sich geändert haben.
- Text_Diff_Op_add Zeilen, die in der Ziel-Version der Datei hinzugefügt wurden (zurzeit nicht genutzt).
- Text_Diff_Op_delete Zeilen, die in der Ziel-Version der Datei gelöscht wurden (zurzeit nicht genutzt).
Momentan reicht es also, wenn Sie zwischen den ersten beiden Klassen unterscheiden.
Innerhalb eines solchen Objekts gibt es wiederum zwei Arrays. Eines verbirgt sich hinter dem Schlüssel 'orig' und das andere hinter dem Schlüssel 'final'. Innerhalb dieser Arrays befindet sich dann immer ein Feld für jede Zeile der Datei, so dass diese gegenübergestellt werden können. In Listing 11.9 finden Sie eine Möglichkeit, um eine solche Gegenüberstellung in Form einer Tabelle zu realisieren.
require_once 'Text/Diff.php'; $datei1='eins.txt'; $datei2='zwei.txt'; $zeilen1 = file($datei1); $zeilen2 = file($datei2); $diff = new Text_Diff($zeilen1, $zeilen2); // Aenderungen vorhanden if (false==$diff->isEmpty()) { echo '<table border="1">'; // Kopf der Tabelle echo "<tr><th>$datei1</th> <th>$datei2</th></tr>"; $daten=$diff->getDiff(); // Zurueckgeliefertes Array abarbeiten foreach ($daten as $objekt) { // Sind die Daten identisch? $identisch=($objekt instanceof Text_Diff_Op_copy); for ($cnt=0;$cnt < count ($objekt->final); $cnt+=1) { // Nicht identische Zeilen rot hinterlegen if (true===$identisch) { echo '<tr>'; } else { echo '<tr bgcolor="red">'; } echo "<td>{$objekt->orig[$cnt]}</td> <td>{$objekt->final[$cnt]}</td></tr>"; } } echo '</table>'; }
Listing 11.9 Manuelle Auswertung der ermittelten Daten
Um zu gewährleisten, dass Listing 11.9 zukunftssicher ist, wird auf Basis des mit PHP 5 eingeführten instanceof-Operators nur geprüft, ob das gefundene Objekt nicht von der Klasse Text_Diff_Op_copy abstammt. Natürlich könnte hier noch explizit geprüft werden, ob das Objekt von einer der anderen Klassen abstammt, um die Tabellenzeilen farblich unterschiedlich zu hinterlegen.
Nicht verschweigen möchte ich, dass Sie mit dem Paket auch drei Dateien miteinander vergleichen können. In dem Fall müsste die Datei Diff3.php inkludiert werden, und die Klasse, mit der Sie arbeiten müssten, heißt Text_Diff3 und bekommt natürlich drei Arrays mit Datei-Inhalten übergeben.
Die Nutzung der anderen Methoden verändert sich nicht. Allerdings liefert getDiff() Ihnen natürlich auch immer drei Arrays in einem Objekt zurück. Das erste ist nach wie vor unter dem Schlüssel 'orig' zu finden und die beiden anderen unter den Schlüsseln 'final1' und 'final2'.