3 * Class used internally by Diff to actually compute the diffs.
5 * This class uses the Unix `diff` program via shell_exec to compute the
6 * differences between the two input arrays.
8 * Copyright 2007-2010 The Horde Project (http://www.horde.org/)
10 * See the enclosed file COPYING for license information (LGPL). If you did
11 * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
13 * @author Milian Wolff <mail@milianw.de>
17 class Text_Diff_Engine_shell {
20 * Path to the diff executable
24 var $_diffCommand = 'diff';
27 * Returns the array of differences.
29 * @param array $from_lines lines of text from old file
30 * @param array $to_lines lines of text from new file
32 * @return array all changes made (array with Text_Diff_Op_* objects)
34 function diff($from_lines, $to_lines)
36 array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
37 array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
39 $temp_dir = Text_Diff::_getTempDir();
41 // Execute gnu diff or similar to get a standard diff file.
42 $from_file = tempnam($temp_dir, 'Text_Diff');
43 $to_file = tempnam($temp_dir, 'Text_Diff');
44 $fp = fopen($from_file, 'w');
45 fwrite($fp, implode("\n", $from_lines));
47 $fp = fopen($to_file, 'w');
48 fwrite($fp, implode("\n", $to_lines));
50 $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
55 // No changes were made
56 return array(new Text_Diff_Op_copy($from_lines));
63 // Get changed lines by parsing something like:
67 preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
68 $matches, PREG_SET_ORDER);
70 foreach ($matches as $match) {
71 if (!isset($match[5])) {
72 // This paren is not set every time (see regex).
76 if ($match[3] == 'a') {
80 if ($match[3] == 'd') {
84 if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
86 assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
88 new Text_Diff_Op_copy(
89 $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
90 $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
97 new Text_Diff_Op_delete(
98 $this->_getLines($from_lines, $from_line_no, $match[2])));
105 new Text_Diff_Op_change(
106 $this->_getLines($from_lines, $from_line_no, $match[2]),
107 $this->_getLines($to_lines, $to_line_no, $match[5])));
113 new Text_Diff_Op_add(
114 $this->_getLines($to_lines, $to_line_no, $match[5])));
120 if (!empty($from_lines)) {
121 // Some lines might still be pending. Add them as copied
123 new Text_Diff_Op_copy(
124 $this->_getLines($from_lines, $from_line_no,
125 $from_line_no + count($from_lines) - 1),
126 $this->_getLines($to_lines, $to_line_no,
127 $to_line_no + count($to_lines) - 1)));
134 * Get lines from either the old or new text
138 * @param array &$text_lines Either $from_lines or $to_lines
139 * @param int &$line_no Current line number
140 * @param int $end Optional end line, when we want to chop more
143 * @return array The chopped lines
145 function _getLines(&$text_lines, &$line_no, $end = false)
149 // We can shift even more
150 while ($line_no <= $end) {
151 array_push($lines, array_shift($text_lines));
155 $lines = array(array_shift($text_lines));