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 * $Horde: framework/Text_Diff/Diff/Engine/shell.php,v 1.8 2008/01/04 10:07:50 jan Exp $
10 * Copyright 2007-2008 The Horde Project (http://www.horde.org/)
12 * See the enclosed file COPYING for license information (LGPL). If you did
13 * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
15 * @author Milian Wolff <mail@milianw.de>
19 class Text_Diff_Engine_shell {
22 * Path to the diff executable
26 var $_diffCommand = 'diff';
29 * Returns the array of differences.
31 * @param array $from_lines lines of text from old file
32 * @param array $to_lines lines of text from new file
34 * @return array all changes made (array with Text_Diff_Op_* objects)
36 function diff($from_lines, $to_lines)
38 array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
39 array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
41 $temp_dir = Text_Diff::_getTempDir();
43 // Execute gnu diff or similar to get a standard diff file.
44 $from_file = tempnam($temp_dir, 'Text_Diff');
45 $to_file = tempnam($temp_dir, 'Text_Diff');
46 $fp = fopen($from_file, 'w');
47 fwrite($fp, implode("\n", $from_lines));
49 $fp = fopen($to_file, 'w');
50 fwrite($fp, implode("\n", $to_lines));
52 $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
57 // No changes were made
58 return array(new Text_Diff_Op_copy($from_lines));
65 // Get changed lines by parsing something like:
69 preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
70 $matches, PREG_SET_ORDER);
72 foreach ($matches as $match) {
73 if (!isset($match[5])) {
74 // This paren is not set every time (see regex).
78 if ($match[3] == 'a') {
82 if ($match[3] == 'd') {
86 if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
88 assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
90 new Text_Diff_Op_copy(
91 $this->_getLines($from_lines, $from_line_no, $match[1] - 1),
92 $this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
99 new Text_Diff_Op_delete(
100 $this->_getLines($from_lines, $from_line_no, $match[2])));
107 new Text_Diff_Op_change(
108 $this->_getLines($from_lines, $from_line_no, $match[2]),
109 $this->_getLines($to_lines, $to_line_no, $match[5])));
115 new Text_Diff_Op_add(
116 $this->_getLines($to_lines, $to_line_no, $match[5])));
122 if (!empty($from_lines)) {
123 // Some lines might still be pending. Add them as copied
125 new Text_Diff_Op_copy(
126 $this->_getLines($from_lines, $from_line_no,
127 $from_line_no + count($from_lines) - 1),
128 $this->_getLines($to_lines, $to_line_no,
129 $to_line_no + count($to_lines) - 1)));
136 * Get lines from either the old or new text
140 * @param array &$text_lines Either $from_lines or $to_lines
141 * @param int &$line_no Current line number
142 * @param int $end Optional end line, when we want to chop more
145 * @return array The chopped lines
147 function _getLines(&$text_lines, &$line_no, $end = false)
151 // We can shift even more
152 while ($line_no <= $end) {
153 array_push($lines, array_shift($text_lines));
157 $lines = array(array_shift($text_lines));