Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
diff.php
1<?
2namespace Bitrix\Main\Text;
3
10class Diff
11{
12 protected $upVector = array();
13 protected $downVector = array();
14 protected $modifiedA = array();
15 protected $modifiedB = array();
16
25 public function getDiffHtml($a, $b)
26 {
27 preg_match_all("/(<.*?>\\s*|\\s+)([^\\s<]*)/", " ".$a, $matchA);
28 preg_match_all("/(<.*?>\\s*|\\s+)([^\\s<]*)/", " ".$b, $matchB);
29
30 $diffScript = $this->getDiffScript($matchA[2], $matchB[2]);
31 if(empty($diffScript))
32 {
33 // no difference found
34 return $a;
35 }
36
37 $positionA = 0;
38
39 $result = '';
40 foreach($diffScript as $diffItem)
41 {
42 while($positionA < $diffItem['startA'])
43 {
44 $result .= $matchA[0][$positionA];
45 $positionA++;
46 }
47
48 //deleted items
49 if($diffItem['deletedA'] > 0)
50 {
51 $result .= $matchA[1][$positionA] . '<s style="color:red">' . $matchA[2][$positionA];
52 for($i = 1; $i < $diffItem['deletedA']; $i++)
53 $result .= $matchA[0][$positionA + $i];
54
55 $result .= '</s>';
56 $positionA = $positionA + $diffItem['deletedA'];
57 }
58
59 if($diffItem['insertedB'] > 0)
60 {
61 $result .= $matchB[1][$diffItem['startB']] . '<b style="color:green">' . $matchB[2][$diffItem['startB']];
62 for($i = 1; $i < $diffItem['insertedB']; $i++)
63 $result .= $matchB[0][$diffItem['startB'] + $i];
64
65 $result .= '</b>';
66 }
67 }
68
69 while($positionA < count($matchA[0]))
70 {
71 $result .= $matchA[0][$positionA];
72 $positionA++;
73 }
74
75 return $result;
76 }
77
89 public function getDiffScript(array $a, array $b)
90 {
91 $this->init();
92 $this->longestCommonSubsequence($a, 0, count($a), $b, 0, count($b));
93 return $this->createDiff($a, $b);
94 }
95
101 protected function init()
102 {
103 $this->upVector = array();
104 $this->downVector = array();
105 $this->modifiedA = array();
106 $this->modifiedB = array();
107 }
108
120 protected function longestCommonSubsequence(array $a, $lowerA, $upperA, array $b, $lowerB, $upperB)
121 {
122 // Skipping equal lines at the start
123 while ($lowerA < $upperA && $lowerB < $upperB && $a[$lowerA] == $b[$lowerB])
124 {
125 $lowerA++;
126 $lowerB++;
127 }
128
129 // Skipping equal lines at the end
130 while ($lowerA < $upperA && $lowerB < $upperB && $a[$upperA - 1] == $b[$upperB - 1])
131 {
132 $upperA--;
133 $upperB--;
134 }
135
136 if ($lowerA === $upperA)
137 {
138 // mark as inserted lines.
139 while ($lowerB < $upperB)
140 {
141 $this->modifiedB[$lowerB++] = true;
142 }
143 }
144 else
145 {
146 if ($lowerB === $upperB)
147 {
148 // mark as deleted lines.
149 while ($lowerA < $upperA)
150 {
151 $this->modifiedA[$lowerA++] = true;
152 }
153 }
154 else
155 {
156 // Find the middle snake and length of an optimal path for A and B
157 $sms = $this->shortestMiddleSnake($a, $lowerA, $upperA, $b, $lowerB, $upperB);
158
159 // The path is from LowerX to (x,y) and (x,y) to UpperX
160 $this->longestCommonSubsequence($a, $lowerA, $sms['x'], $b, $lowerB, $sms['y']);
161 $this->longestCommonSubsequence($a, $sms['x'], $upperA, $b, $sms['y'], $upperB);
162 }
163 }
164 }
165
166
179 protected function shortestMiddleSnake(array $a, $lowerA, $upperA, array $b, $lowerB, $upperB)
180 {
181 $result = array();
182
183 $downK = $lowerA - $lowerB; // the k-line to start the forward search
184 $upK = $upperA - $upperB; // the k-line to start the reverse search
185
186 $delta = ($upperA - $lowerA) - ($upperB - $lowerB);
187 $oddDelta = ($delta & 1) != 0;
188
189 $maxD = (($upperA - $lowerA + $upperB - $lowerB) / 2) + 1;
190
191 // init vectors
192 $this->downVector[$downK + 1] = $lowerA;
193 $this->upVector[$upK - 1] = $upperA;
194
195 for ($d = 0; $d <= $maxD; $d++)
196 {
197
198 // Extend the forward path.
199 for ($k = $downK - $d; $k <= $downK + $d; $k += 2)
200 {
201 // find the only or better starting point
202 $x = 0;
203 $y = 0;
204 if ($k == $downK - $d)
205 {
206 $x = $this->downVector[$k + 1]; // down
207 }
208 else
209 {
210 $x = $this->downVector[$k - 1] + 1; // a step to the right
211 if (($k < $downK + $d) && ($this->downVector[$k + 1] >= $x))
212 {
213 $x = $this->downVector[$k + 1];
214 } // down
215 }
216 $y = $x - $k;
217
218 // find the end of the furthest reaching forward D-path in diagonal k.
219 while (($x < $upperA) && ($y < $upperB) && ($a[$x] == $b[$y]))
220 {
221 $x++;
222 $y++;
223 }
224 $this->downVector[$k] = $x;
225
226 // overlap ?
227 if ($oddDelta && ($upK - $d < $k) && ($k < $upK + $d))
228 {
229 if ($this->upVector[$k] <= $this->downVector[$k])
230 {
231 $result['x'] = $this->downVector[$k];
232 $result['y'] = $this->downVector[$k] - $k;
233 return $result;
234 }
235 }
236 }
237
238 // Extend the reverse path.
239 for ($k = $upK - $d; $k <= $upK + $d; $k += 2)
240 {
241
242 // find the only or better starting point
243 $x = 0;
244 $y = 0;
245 if ($k == $upK + $d)
246 {
247 $x = $this->upVector[$k - 1]; // up
248 }
249 else
250 {
251 $x = $this->upVector[$k + 1] - 1; // left
252 if (($k > $upK - $d) && ($this->upVector[$k - 1] < $x))
253 {
254 $x = $this->upVector[$k - 1];
255 } // up
256 }
257 $y = $x - $k;
258
259 while (($x > $lowerA) && ($y > $lowerB) && ($a[$x - 1] == $b[$y - 1]))
260 {
261 // diagonal
262 $x--;
263 $y--;
264 }
265 $this->upVector[$k] = $x;
266
267 // overlap ?
268 if (!$oddDelta && ($downK - $d <= $k) && ($k <= $downK + $d))
269 {
270 if ($this->upVector[$k] <= $this->downVector[$k])
271 {
272 $result['x'] = $this->downVector[$k];
273 $result['y'] = $this->downVector[$k] - $k;
274 return $result;
275 }
276 }
277 }
278 }
279 }
280
287 protected function createDiff(array $a, array $b)
288 {
289 $indexA = 0;
290 $indexB = 0;
291 $result = array();
292 while ($indexA < count($a) || $indexB < count($b))
293 {
294 if (($indexA < count($a)) && (empty($this->modifiedA[$indexA])) && ($indexB < count($b)) && empty($this->modifiedB[$indexB]))
295 {
296 // equal lines
297 $indexA++;
298 $indexB++;
299 }
300 else
301 {
302 // maybe deleted and/or inserted lines
303 $startA = $indexA;
304 $startB = $indexB;
305
306 while ($indexA < count($a) && ($indexB >= count($b) || !empty($this->modifiedA[$indexA])))
307 {
308 $indexA++;
309 }
310
311 while ($indexB < count($b) && ($indexA >= count($a) || !empty($this->modifiedB[$indexB])))
312 {
313 $indexB++;
314 }
315
316 if (($startA < $indexA) || ($startB < $indexB))
317 {
318 // store a new difference-item
319 $result[] = array("startA" => $startA, "startB" => $startB, "deletedA" => $indexA - $startA, "insertedB" => $indexB - $startB);
320 }
321 }
322 }
323 return $result;
324 }
325}
getDiffScript(array $a, array $b)
Definition diff.php:89
longestCommonSubsequence(array $a, $lowerA, $upperA, array $b, $lowerB, $upperB)
Definition diff.php:120
createDiff(array $a, array $b)
Definition diff.php:287
getDiffHtml($a, $b)
Definition diff.php:25
shortestMiddleSnake(array $a, $lowerA, $upperA, array $b, $lowerB, $upperB)
Definition diff.php:179