3 * Parses unified or context diffs output from eg. the diff utility.
7 * $patch = file_get_contents('example.patch');
8 * $diff = new Text_Diff('string', array($patch));
9 * $renderer = new Text_Diff_Renderer_inline();
10 * echo $renderer->render($diff);
13 * $Horde: framework/Text_Diff/Diff/Engine/string.php,v 1.7 2008/01/04 10:07:50 jan Exp $
15 * Copyright 2005 Örjan Persson <o@42mm.org>
16 * Copyright 2005-2008 The Horde Project (http://www.horde.org/)
18 * See the enclosed file COPYING for license information (LGPL). If you did
19 * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
21 * @author Örjan Persson <o@42mm.org>
25 class Text_Diff_Engine_string {
28 * Parses a unified or context diff.
30 * First param contains the whole diff and the second can be used to force
31 * a specific diff type. If the second parameter is 'autodetect', the
32 * diff will be examined to find out which type of diff this is.
34 * @param string $diff The diff content.
35 * @param string $mode The diff mode of the content in $diff. One of
36 * 'context', 'unified', or 'autodetect'.
38 * @return array List of all diff operations.
40 function diff($diff, $mode = 'autodetect')
42 if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
43 return PEAR::raiseError('Type of diff is unsupported');
46 if ($mode == 'autodetect') {
47 $context = strpos($diff, '***');
48 $unified = strpos($diff, '---');
49 if ($context === $unified) {
50 return PEAR::raiseError('Type of diff could not be detected');
51 } elseif ($context === false || $context === false) {
52 $mode = $context !== false ? 'context' : 'unified';
54 $mode = $context < $unified ? 'context' : 'unified';
58 // split by new line and remove the diff header
59 $diff = explode("\n", $diff);
63 if ($mode == 'context') {
64 return $this->parseContextDiff($diff);
66 return $this->parseUnifiedDiff($diff);
71 * Parses an array containing the unified diff.
73 * @param array $diff Array of lines.
75 * @return array List of all diff operations.
77 function parseUnifiedDiff($diff)
80 $end = count($diff) - 1;
81 for ($i = 0; $i < $end;) {
83 switch (substr($diff[$i], 0, 1)) {
86 $diff1[] = substr($diff[$i], 1);
87 } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
88 $edits[] = &new Text_Diff_Op_copy($diff1);
94 $diff1[] = substr($diff[$i], 1);
95 } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
96 $edits[] = &new Text_Diff_Op_add($diff1);
100 // get changed or removed lines
103 $diff1[] = substr($diff[$i], 1);
104 } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
106 while ($i < $end && substr($diff[$i], 0, 1) == '+') {
107 $diff2[] = substr($diff[$i++], 1);
109 if (count($diff2) == 0) {
110 $edits[] = &new Text_Diff_Op_delete($diff1);
112 $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
126 * Parses an array containing the context diff.
128 * @param array $diff Array of lines.
130 * @return array List of all diff operations.
132 function parseContextDiff(&$diff)
135 $i = $max_i = $j = $max_j = 0;
136 $end = count($diff) - 1;
137 while ($i < $end && $j < $end) {
138 while ($i >= $max_i && $j >= $max_j) {
139 // Find the boundaries of the diff output of the two files
141 $i < $end && substr($diff[$i], 0, 3) == '***';
144 $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
147 $j < $end && substr($diff[$j], 0, 3) == '---';
150 $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
154 // find what hasn't been changed
156 while ($i < $max_i &&
158 strcmp($diff[$i], $diff[$j]) == 0) {
159 $array[] = substr($diff[$i], 2);
164 while ($i < $max_i && ($max_j-$j) <= 1) {
165 if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
168 $array[] = substr($diff[$i++], 2);
171 while ($j < $max_j && ($max_i-$i) <= 1) {
172 if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
175 $array[] = substr($diff[$j++], 2);
177 if (count($array) > 0) {
178 $edits[] = &new Text_Diff_Op_copy($array);
183 switch (substr($diff[$i], 0, 1)) {
187 $diff1[] = substr($diff[$i], 2);
188 if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
189 $diff2[] = substr($diff[$j++], 2);
191 } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
192 $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
197 $diff1[] = substr($diff[$i], 2);
198 } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
199 $edits[] = &new Text_Diff_Op_add($diff1);
204 $diff1[] = substr($diff[$i], 2);
205 } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
206 $edits[] = &new Text_Diff_Op_delete($diff1);
213 switch (substr($diff[$j], 0, 1)) {
216 $diff2[] = substr($diff[$j++], 2);
217 } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
218 $edits[] = &new Text_Diff_Op_add($diff2);
223 $diff2[] = substr($diff[$j++], 2);
224 } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
225 $edits[] = &new Text_Diff_Op_delete($diff2);