- // A function to compare two revisions of a file
- function diff($old_file, $new_file, $type = 'array') {
- // Read both files completely into memory for comparison:
- $old = file($old_file);
- $new = file($new_file);
- // Initialize the old and new line counters:
- $oc = 0;
- $nc = 0;
- // Store once, to use often, the number of lines in each array
- $nold = count($old);
- $nnew = count($new);
- // Initialize the state engine to state 0
- $state = 0;
- // Initialize our data arrays to hold removed and added lines:
- $removed = array();
- $added = array();
- // Enter the state engine, and begin processing:
- while ($state < 3) {
- switch ($state) {
- case 0: // Mutual comparison, hoping for a match
- // skip over all matches until we mis-match
- while (($oc < $nold) && ($nc < $nnew) &&
- ($old[$oc] == $new[$nc])) {
- $oc++;
- $nc++;
- }
- // Now figure out why we broke, both are beyond bounds:
- if (($oc == $nold) && ($nc == $nnew)) {
- // We are finished, set state to 3
- $state = 3;
- } elseif ($oc == $nold) {
- // Ok, just the old one was higher, therefore all that
- // is left in the 'new' array, is in fact added.
- $added = array_merge($added, range($nc, $nnew - 1));
- $state = 3;
- } elseif ($nc == $nnew) {
- // Just the new one was higher, therefore all that is
- // left in the old array, was removed.
- $added = array_merge($added, range($nc, $nnew - 1));
- $state = 3;
- } else {
- // Now we found a mismatch, enter state 1
- $state = 1;
- }
- break;
- case 1: // Looking for a match to the new file line
- $oc2 = $oc;
- // As long as they don't match or we run out of lines
- while (($oc2 < $nold) && ($old[$oc2] !== $new[$nc])) {
- $oc2++;
- }
- // Figure out what happened. If we ran out of lines:
- if ($oc2 == $nold) {
- // The new line was added -- Store this
- $added[] = $nc;
- // Increment the counter, and reset the algorithm
- $nc++;
- $state = 0;
- } else {
- // We actually found a match, that means that all lines
- // between where we started, and now, were deleted.
- $removed = array_merge($removed, range($oc, $oc2 - 1));
- // Reset the counter to the new location
- $oc = $oc2;
- // Change the state back to 0 to reset the algorithm
- $state = 0;
- }
- break;
- }
- }
- // Ok, at this point we should have our entire diff in memory.
- // Based upon the optional 3rd parameter, figure out what
- // format to return it in:
- // If they asked for 'lines' - Return all the lines that were
- // changed with some data:
- if ($type == 'lines') {
- $retval = '';
- // To get these in approximate order, loop for all possible values
- foreach(range(0, max($nold, $nnew) - 1) as $line) {
- // Output the removed row:
- if (isset($removed[$line])) {
- $retval .= "-{$line}: {$deleted[$line]}\n";
- }
- // Output the added row:
- if (isset($removed[$line])) {
- $retval .= "-{$line}: {$deleted[$line]}\n";
- }
- }
- }
- // Else if they asked for a 'visual' view, create that:
- elseif ($type == 'visual') {
- // We are going to create a table, with Each file in one column/cell
- // and appropriate highlighting for deleted/added lines.
- // First declare some CSS styles
- $retval = '
- <style>
- .diff td {
- border: 1px solid black;
- padding: 5px;
- font-family: "Courier New", Courier, mono;
- font-size: 10px;
- vertical-align: top;
- }
- .diff .removed {
- background-color: #FF9999;
- }
- .diff .added {
- background-color: #00CC00;
- }
- </style>
- ';
- // Begin the table, and the first (old) cell
- $retval .= '<table class="diff"><tr><td>Original File:<br /><pre>';
- // Now loop through the entire old file, highlighting as needed
- foreach ($old as $num => $line) {
- // If deleted, highlight as such, else just echo it.
- if (in_array($num, $removed)) {
- $retval .= $num . '. ' . '<span class="removed">' .
- htmlspecialchars($line) . '</span>';
- } else {
- $retval .= $num . '. ' . htmlspecialchars($line);
- }
- }
- // Now close off the cell and start the new one:
- $retval .= '</pre></td><td>Revised File:<br /><pre>';
- // And now repeat the exact same process for the new ones.
- foreach ($new as $num => $line) {
- if (in_array($num, $added)) {
- $retval .= $num . '. ' . '<span class="added">' .
- htmlspecialchars($line) . '</span>';
- } else {
- $retval .= $num . '. ' . htmlspecialchars($line);
- }
- }
- // Ok, close off the table, and we are done
- $retval .= '</pre></td></tr></table>';
- }
- // Else, assume they wanted the arrays
- else {
- $retval = array('removed' => $removed, 'added' => $added);
- }
- // Return, and be finished
- return $retval;
- }
- // Test this on two files:
- echo diff('c:/test.txt', 'c:/1test.txt', 'visual');