📄 diff.inc.php
字号:
<?php// This file is licensed under the LGPL and is from the Text_Diff PEAR project which// is available at http://pear.php.net/package/Text_Diff/** * Text_Diff * * General API for generating and formatting diffs - the differences between * two sequences of strings. * * The PHP diff code used in this package was originally written by Geoffrey * T. Dairiki and is used with his permission. * * $Horde: framework/Text_Diff/Diff.php,v 1.10 2004/11/01 11:51:15 mdjukic Exp $ * * @package Text_Diff * @author Geoffrey T. Dairiki <dairiki@dairiki.org> */class Text_Diff { /** * Array of changes. * * @var array $_edits */ var $_edits; /** * Computes diffs between sequences of strings. * * @param array $from_lines An array of strings. Typically these are * lines from a file. * @param array $to_lines An array of strings. */ function Text_Diff($from_lines, $to_lines) { array_walk($from_lines, array($this, '_trimNewlines')); array_walk($to_lines, array($this, '_trimNewlines')); if (extension_loaded('xdiff')) { $engine = &new Text_Diff_Engine_xdiff(); } else { $engine = &new Text_Diff_Engine_native(); } $this->_edits = $engine->diff($from_lines, $to_lines); } /** * Returns the array of differences. */ function getDiff() { return $this->_edits; } /** * Computes a reversed diff. * * Example: * <code> * $diff = &new Text_Diff($lines1, $lines2); * $rev = $diff->reverse(); * </code> * * @return Text_Diff A Diff object representing the inverse of the * original diff. Note that we purposely don't return a * reference here, since this essentially is a clone() * method. */ function reverse() { if (version_compare(zend_version(), '2', '>')) { $rev = clone($obj); } else { $rev = $this; } $rev->_edits = array(); foreach ($this->_edits as $edit) { $rev->_edits[] = $edit->reverse(); } return $rev; } /** * Checks for an empty diff. * * @return boolean True if two sequences were identical. */ function isEmpty() { foreach ($this->_edits as $edit) { if (!is_a($edit, 'Text_Diff_Op_copy')) { return false; } } return true; } /** * Computes the length of the Longest Common Subsequence (LCS). * * This is mostly for diagnostic purposed. * * @return int The length of the LCS. */ function lcs() { $lcs = 0; foreach ($this->_edits as $edit) { if (is_a($edit, 'Text_Diff_Op_copy')) { $lcs += count($edit->orig); } } return $lcs; } /** * Gets the original set of lines. * * This reconstructs the $from_lines parameter passed to the constructor. * * @return array The original sequence of strings. */ function getOriginal() { $lines = array(); foreach ($this->_edits as $edit) { if ($edit->orig) { array_splice($lines, count($lines), 0, $edit->orig); } } return $lines; } /** * Gets the final set of lines. * * This reconstructs the $to_lines parameter passed to the constructor. * * @return array The sequence of strings. */ function getFinal() { $lines = array(); foreach ($this->_edits as $edit) { if ($edit->final) { array_splice($lines, count($lines), 0, $edit->final); } } return $lines; } /** * Removes trailing newlines from a line of text. This is meant to be used * with array_walk(). * * @param string $line The line to trim. * @param integer $key The index of the line in the array. Not used. */ function _trimNewlines(&$line, $key) { $line = str_replace(array("\n", "\r"), '', $line); } /** * Checks a diff for validity. * * This is here only for debugging purposes. */ function _check($from_lines, $to_lines) { if (serialize($from_lines) != serialize($this->getOriginal())) { trigger_error("Reconstructed original doesn't match", E_USER_ERROR); } if (serialize($to_lines) != serialize($this->getFinal())) { trigger_error("Reconstructed final doesn't match", E_USER_ERROR); } $rev = $this->reverse(); if (serialize($to_lines) != serialize($rev->getOriginal())) { trigger_error("Reversed original doesn't match", E_USER_ERROR); } if (serialize($from_lines) != serialize($rev->getFinal())) { trigger_error("Reversed final doesn't match", E_USER_ERROR); } $prevtype = null; foreach ($this->_edits as $edit) { if ($prevtype == get_class($edit)) { trigger_error("Edit sequence is non-optimal", E_USER_ERROR); } $prevtype = get_class($edit); } return true; }}/** * $Horde: framework/Text_Diff/Diff.php,v 1.10 2004/11/01 11:51:15 mdjukic Exp $ * * @package Text_Diff * @author Geoffrey T. Dairiki <dairiki@dairiki.org> */class Text_MappedDiff extends Text_Diff { /** * Computes a diff between sequences of strings. * * This can be used to compute things like case-insensitve diffs, or diffs * which ignore changes in white-space. * * @param array $from_lines An array of strings. * @param array $to_lines An array of strings. * @param array $mapped_from_lines This array should have the same size * number of elements as $from_lines. The * elements in $mapped_from_lines and * $mapped_to_lines are what is actually * compared when computing the diff. * @param array $mapped_to_lines This array should have the same number * of elements as $to_lines. */ function Text_MappedDiff($from_lines, $to_lines, $mapped_from_lines, $mapped_to_lines) { assert(count($from_lines) == count($mapped_from_lines)); assert(count($to_lines) == count($mapped_to_lines)); parent::Text_Diff($mapped_from_lines, $mapped_to_lines); $xi = $yi = 0; for ($i = 0; $i < count($this->_edits); $i++) { $orig = &$this->_edits[$i]->orig; if (is_array($orig)) { $orig = array_slice($from_lines, $xi, count($orig)); $xi += count($orig); } $final = &$this->_edits[$i]->final; if (is_array($final)) { $final = array_slice($to_lines, $yi, count($final)); $yi += count($final); } } }}/** * Class used internally by Diff to actually compute the diffs. This class * uses the xdiff PECL package (http://pecl.php.net/package/xdiff) to compute * the differences between the two input arrays. * * @author Jon Parise <jon@horde.org> * @package Text_Diff * @access private */class Text_Diff_Engine_xdiff { function diff($from_lines, $to_lines) { /* Convert the two input arrays into strings for xdiff processing. */ $from_string = implode("\n", $from_lines); $to_string = implode("\n", $to_lines); /* Diff the two strings and convert the result to an array. */ $diff = xdiff_string_diff($from_string, $to_string, count($to_lines)); $diff = explode("\n", $diff); /* Walk through the diff one line at a time. We build the $edits * array of diff operations by reading the first character of the * xdiff output (which is in the "unified diff" format). * * Note that we don't have enough information to detect "changed" * lines using this approach, so we can't add Text_Diff_Op_changed * instances to the $edits array. The result is still perfectly * valid, albeit a little less descriptive and efficient. */ $edits = array(); foreach ($diff as $line) { switch ($line[0]) { case ' ': $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1))); break; case '+': $edits[] = &new Text_Diff_Op_add(array(substr($line, 1))); break; case '-': $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1))); break; } } return $edits; }}/** * Class used internally by Diff to actually compute the diffs. This class is * implemented using native PHP code. * * The algorithm used here is mostly lifted from the perl module * Algorithm::Diff (version 1.06) by Ned Konz, which is available at: * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip * * More ideas are taken from: * http://www.ics.uci.edu/~eppstein/161/960229.html * * Some ideas (and a bit of code) are from from analyze.c, from GNU * diffutils-2.7, which can be found at: * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz * * Some ideas (subdivision by NCHUNKS > 2, and some optimizations) are from * Geoffrey T. Dairiki <dairiki@dairiki.org>. The original PHP version of this * code was written by him, and is used/adapted with his permission. * * @author Geoffrey T. Dairiki <dairiki@dairiki.org> * @package Text_Diff * @access private */class Text_Diff_Engine_native { function diff($from_lines, $to_lines) { $n_from = count($from_lines); $n_to = count($to_lines); $this->xchanged = $this->ychanged = array(); $this->xv = $this->yv = array(); $this->xind = $this->yind = array(); unset($this->seq); unset($this->in_seq); unset($this->lcs); // Skip leading common lines. for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { if ($from_lines[$skip] != $to_lines[$skip]) { break; } $this->xchanged[$skip] = $this->ychanged[$skip] = false; } // Skip trailing common lines. $xi = $n_from; $yi = $n_to; for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { if ($from_lines[$xi] != $to_lines[$yi]) { break; } $this->xchanged[$xi] = $this->ychanged[$yi] = false; } // Ignore lines which do not exist in both files. for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { $xhash[$from_lines[$xi]] = 1; } for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { $line = $to_lines[$yi]; if (($this->ychanged[$yi] = empty($xhash[$line]))) { continue; } $yhash[$line] = 1; $this->yv[] = $line; $this->yind[] = $yi; } for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { $line = $from_lines[$xi]; if (($this->xchanged[$xi] = empty($yhash[$line]))) { continue; } $this->xv[] = $line; $this->xind[] = $xi; } // Find the LCS. $this->_compareseq(0, count($this->xv), 0, count($this->yv)); // Merge edits when possible. $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged); $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged); // Compute the edit operations. $edits = array(); $xi = $yi = 0; while ($xi < $n_from || $yi < $n_to) { assert($yi < $n_to || $this->xchanged[$xi]); assert($xi < $n_from || $this->ychanged[$yi]); // Skip matching "snake". $copy = array(); while ($xi < $n_from && $yi < $n_to && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { $copy[] = $from_lines[$xi++]; ++$yi; } if ($copy) { $edits[] = &new Text_Diff_Op_copy($copy); } // Find deletes & adds. $delete = array(); while ($xi < $n_from && $this->xchanged[$xi]) { $delete[] = $from_lines[$xi++]; } $add = array(); while ($yi < $n_to && $this->ychanged[$yi]) { $add[] = $to_lines[$yi++];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -