1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
22: class Horde_Text_Diff_Engine_String
23: {
24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
38: public function diff($diff, $mode = 'autodetect')
39: {
40:
41: $lnbr = "\n";
42: if (strpos($diff, "\r\n") !== false) {
43: $lnbr = "\r\n";
44: } elseif (strpos($diff, "\r") !== false) {
45: $lnbr = "\r";
46: }
47:
48:
49: if (substr($diff, -strlen($lnbr)) != $lnbr) {
50: $diff .= $lnbr;
51: }
52:
53: if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
54: throw new Horde_Text_Diff_Exception('Type of diff is unsupported');
55: }
56:
57: if ($mode == 'autodetect') {
58: $context = strpos($diff, '***');
59: $unified = strpos($diff, '---');
60: if ($context === $unified) {
61: throw new Horde_Text_Diff_Exception('Type of diff could not be detected');
62: } elseif ($context === false || $unified === false) {
63: $mode = $context !== false ? 'context' : 'unified';
64: } else {
65: $mode = $context < $unified ? 'context' : 'unified';
66: }
67: }
68:
69:
70: $diff = explode($lnbr, $diff);
71: if (($mode == 'context' && strpos($diff[0], '***') === 0) ||
72: ($mode == 'unified' && strpos($diff[0], '---') === 0)) {
73: array_shift($diff);
74: array_shift($diff);
75: }
76:
77: if ($mode == 'context') {
78: return $this->parseContextDiff($diff);
79: } else {
80: return $this->parseUnifiedDiff($diff);
81: }
82: }
83:
84: 85: 86: 87: 88: 89: 90:
91: public function parseUnifiedDiff($diff)
92: {
93: $edits = array();
94: $end = count($diff) - 1;
95: for ($i = 0; $i < $end;) {
96: $diff1 = array();
97: switch (substr($diff[$i], 0, 1)) {
98: case ' ':
99: do {
100: $diff1[] = substr($diff[$i], 1);
101: } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
102: $edits[] = new Horde_Text_Diff_Op_Copy($diff1);
103: break;
104:
105: case '+':
106:
107: do {
108: $diff1[] = substr($diff[$i], 1);
109: } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
110: $edits[] = new Horde_Text_Diff_Op_Add($diff1);
111: break;
112:
113: case '-':
114:
115: $diff2 = array();
116: do {
117: $diff1[] = substr($diff[$i], 1);
118: } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
119:
120: while ($i < $end && substr($diff[$i], 0, 1) == '+') {
121: $diff2[] = substr($diff[$i++], 1);
122: }
123: if (count($diff2) == 0) {
124: $edits[] = new Horde_Text_Diff_Op_Delete($diff1);
125: } else {
126: $edits[] = new Horde_Text_Diff_Op_Change($diff1, $diff2);
127: }
128: break;
129:
130: default:
131: $i++;
132: break;
133: }
134: }
135:
136: return $edits;
137: }
138:
139: 140: 141: 142: 143: 144: 145:
146: public function parseContextDiff(&$diff)
147: {
148: $edits = array();
149: $i = $max_i = $j = $max_j = 0;
150: $end = count($diff) - 1;
151: while ($i < $end && $j < $end) {
152: while ($i >= $max_i && $j >= $max_j) {
153:
154: for ($i = $j;
155: $i < $end && substr($diff[$i], 0, 3) == '***';
156: $i++);
157: for ($max_i = $i;
158: $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
159: $max_i++);
160: for ($j = $max_i;
161: $j < $end && substr($diff[$j], 0, 3) == '---';
162: $j++);
163: for ($max_j = $j;
164: $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
165: $max_j++);
166: }
167:
168:
169: $array = array();
170: while ($i < $max_i &&
171: $j < $max_j &&
172: strcmp($diff[$i], $diff[$j]) == 0) {
173: $array[] = substr($diff[$i], 2);
174: $i++;
175: $j++;
176: }
177:
178: while ($i < $max_i && ($max_j-$j) <= 1) {
179: if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
180: break;
181: }
182: $array[] = substr($diff[$i++], 2);
183: }
184:
185: while ($j < $max_j && ($max_i-$i) <= 1) {
186: if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
187: break;
188: }
189: $array[] = substr($diff[$j++], 2);
190: }
191: if (count($array) > 0) {
192: $edits[] = new Horde_Text_Diff_Op_Copy($array);
193: }
194:
195: if ($i < $max_i) {
196: $diff1 = array();
197: switch (substr($diff[$i], 0, 1)) {
198: case '!':
199: $diff2 = array();
200: do {
201: $diff1[] = substr($diff[$i], 2);
202: if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
203: $diff2[] = substr($diff[$j++], 2);
204: }
205: } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
206: $edits[] = new Horde_Text_Diff_Op_Change($diff1, $diff2);
207: break;
208:
209: case '+':
210: do {
211: $diff1[] = substr($diff[$i], 2);
212: } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
213: $edits[] = new Horde_Text_Diff_Op_Add($diff1);
214: break;
215:
216: case '-':
217: do {
218: $diff1[] = substr($diff[$i], 2);
219: } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
220: $edits[] = new Horde_Text_Diff_Op_Delete($diff1);
221: break;
222: }
223: }
224:
225: if ($j < $max_j) {
226: $diff2 = array();
227: switch (substr($diff[$j], 0, 1)) {
228: case '+':
229: do {
230: $diff2[] = substr($diff[$j++], 2);
231: } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
232: $edits[] = new Horde_Text_Diff_Op_Add($diff2);
233: break;
234:
235: case '-':
236: do {
237: $diff2[] = substr($diff[$j++], 2);
238: } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
239: $edits[] = new Horde_Text_Diff_Op_Delete($diff2);
240: break;
241: }
242: }
243: }
244:
245: return $edits;
246: }
247: }
248: