Mercurial > hg > isophonics-drupal-site
comparison vendor/sebastian/diff/src/Differ.php @ 12:7a779792577d
Update Drupal core to v8.4.5 (via Composer)
author | Chris Cannam |
---|---|
date | Fri, 23 Feb 2018 15:52:07 +0000 |
parents | 4c8ae668cc8c |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
11:bfffd8d7479a | 12:7a779792577d |
---|---|
1 <?php | 1 <?php |
2 /* | 2 /* |
3 * This file is part of the Diff package. | 3 * This file is part of sebastian/diff. |
4 * | 4 * |
5 * (c) Sebastian Bergmann <sebastian@phpunit.de> | 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> |
6 * | 6 * |
7 * For the full copyright and license information, please view the LICENSE | 7 * For the full copyright and license information, please view the LICENSE |
8 * file that was distributed with this source code. | 8 * file that was distributed with this source code. |
29 */ | 29 */ |
30 private $showNonDiffLines; | 30 private $showNonDiffLines; |
31 | 31 |
32 /** | 32 /** |
33 * @param string $header | 33 * @param string $header |
34 * @param bool $showNonDiffLines | |
34 */ | 35 */ |
35 public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true) | 36 public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true) |
36 { | 37 { |
37 $this->header = $header; | 38 $this->header = $header; |
38 $this->showNonDiffLines = $showNonDiffLines; | 39 $this->showNonDiffLines = $showNonDiffLines; |
47 * | 48 * |
48 * @return string | 49 * @return string |
49 */ | 50 */ |
50 public function diff($from, $to, LongestCommonSubsequence $lcs = null) | 51 public function diff($from, $to, LongestCommonSubsequence $lcs = null) |
51 { | 52 { |
52 if (!is_array($from) && !is_string($from)) { | 53 $from = $this->validateDiffInput($from); |
53 $from = (string) $from; | 54 $to = $this->validateDiffInput($to); |
54 } | 55 $diff = $this->diffToArray($from, $to, $lcs); |
55 | 56 $old = $this->checkIfDiffInOld($diff); |
56 if (!is_array($to) && !is_string($to)) { | 57 $start = isset($old[0]) ? $old[0] : 0; |
57 $to = (string) $to; | 58 $end = \count($diff); |
58 } | 59 |
59 | 60 if ($tmp = \array_search($end, $old)) { |
60 $buffer = $this->header; | 61 $end = $tmp; |
61 $diff = $this->diffToArray($from, $to, $lcs); | 62 } |
62 | 63 |
64 return $this->getBuffer($diff, $old, $start, $end); | |
65 } | |
66 | |
67 /** | |
68 * Casts variable to string if it is not a string or array. | |
69 * | |
70 * @param mixed $input | |
71 * | |
72 * @return string | |
73 */ | |
74 private function validateDiffInput($input) | |
75 { | |
76 if (!\is_array($input) && !\is_string($input)) { | |
77 return (string) $input; | |
78 } | |
79 | |
80 return $input; | |
81 } | |
82 | |
83 /** | |
84 * Takes input of the diff array and returns the old array. | |
85 * Iterates through diff line by line, | |
86 * | |
87 * @param array $diff | |
88 * | |
89 * @return array | |
90 */ | |
91 private function checkIfDiffInOld(array $diff) | |
92 { | |
63 $inOld = false; | 93 $inOld = false; |
64 $i = 0; | 94 $i = 0; |
65 $old = array(); | 95 $old = array(); |
66 | 96 |
67 foreach ($diff as $line) { | 97 foreach ($diff as $line) { |
68 if ($line[1] === 0 /* OLD */) { | 98 if ($line[1] === 0 /* OLD */) { |
69 if ($inOld === false) { | 99 if ($inOld === false) { |
70 $inOld = $i; | 100 $inOld = $i; |
71 } | 101 } |
72 } elseif ($inOld !== false) { | 102 } elseif ($inOld !== false) { |
73 if (($i - $inOld) > 5) { | 103 if (($i - $inOld) > 5) { |
78 } | 108 } |
79 | 109 |
80 ++$i; | 110 ++$i; |
81 } | 111 } |
82 | 112 |
83 $start = isset($old[0]) ? $old[0] : 0; | 113 return $old; |
84 $end = count($diff); | 114 } |
85 | 115 |
86 if ($tmp = array_search($end, $old)) { | 116 /** |
87 $end = $tmp; | 117 * Generates buffer in string format, returning the patch. |
88 } | 118 * |
89 | 119 * @param array $diff |
90 $newChunk = true; | 120 * @param array $old |
121 * @param int $start | |
122 * @param int $end | |
123 * | |
124 * @return string | |
125 */ | |
126 private function getBuffer(array $diff, array $old, $start, $end) | |
127 { | |
128 $buffer = $this->header; | |
129 | |
130 if (!isset($old[$start])) { | |
131 $buffer = $this->getDiffBufferElementNew($diff, $buffer, $start); | |
132 ++$start; | |
133 } | |
91 | 134 |
92 for ($i = $start; $i < $end; $i++) { | 135 for ($i = $start; $i < $end; $i++) { |
93 if (isset($old[$i])) { | 136 if (isset($old[$i])) { |
94 $buffer .= "\n"; | 137 $i = $old[$i]; |
95 $newChunk = true; | 138 $buffer = $this->getDiffBufferElementNew($diff, $buffer, $i); |
96 $i = $old[$i]; | 139 } else { |
97 } | 140 $buffer = $this->getDiffBufferElement($diff, $buffer, $i); |
98 | |
99 if ($newChunk) { | |
100 if ($this->showNonDiffLines === true) { | |
101 $buffer .= "@@ @@\n"; | |
102 } | |
103 $newChunk = false; | |
104 } | |
105 | |
106 if ($diff[$i][1] === 1 /* ADDED */) { | |
107 $buffer .= '+' . $diff[$i][0] . "\n"; | |
108 } elseif ($diff[$i][1] === 2 /* REMOVED */) { | |
109 $buffer .= '-' . $diff[$i][0] . "\n"; | |
110 } elseif ($this->showNonDiffLines === true) { | |
111 $buffer .= ' ' . $diff[$i][0] . "\n"; | |
112 } | 141 } |
113 } | 142 } |
114 | 143 |
115 return $buffer; | 144 return $buffer; |
116 } | 145 } |
117 | 146 |
118 /** | 147 /** |
148 * Gets individual buffer element. | |
149 * | |
150 * @param array $diff | |
151 * @param string $buffer | |
152 * @param int $diffIndex | |
153 * | |
154 * @return string | |
155 */ | |
156 private function getDiffBufferElement(array $diff, $buffer, $diffIndex) | |
157 { | |
158 if ($diff[$diffIndex][1] === 1 /* ADDED */) { | |
159 $buffer .= '+' . $diff[$diffIndex][0] . "\n"; | |
160 } elseif ($diff[$diffIndex][1] === 2 /* REMOVED */) { | |
161 $buffer .= '-' . $diff[$diffIndex][0] . "\n"; | |
162 } elseif ($this->showNonDiffLines === true) { | |
163 $buffer .= ' ' . $diff[$diffIndex][0] . "\n"; | |
164 } | |
165 | |
166 return $buffer; | |
167 } | |
168 | |
169 /** | |
170 * Gets individual buffer element with opening. | |
171 * | |
172 * @param array $diff | |
173 * @param string $buffer | |
174 * @param int $diffIndex | |
175 * | |
176 * @return string | |
177 */ | |
178 private function getDiffBufferElementNew(array $diff, $buffer, $diffIndex) | |
179 { | |
180 if ($this->showNonDiffLines === true) { | |
181 $buffer .= "@@ @@\n"; | |
182 } | |
183 | |
184 return $this->getDiffBufferElement($diff, $buffer, $diffIndex); | |
185 } | |
186 | |
187 /** | |
119 * Returns the diff between two arrays or strings as array. | 188 * Returns the diff between two arrays or strings as array. |
120 * | 189 * |
121 * Each array element contains two elements: | 190 * Each array element contains two elements: |
122 * - [0] => string $token | 191 * - [0] => mixed $token |
123 * - [1] => 2|1|0 | 192 * - [1] => 2|1|0 |
124 * | 193 * |
125 * - 2: REMOVED: $token was removed from $from | 194 * - 2: REMOVED: $token was removed from $from |
126 * - 1: ADDED: $token was added to $from | 195 * - 1: ADDED: $token was added to $from |
127 * - 0: OLD: $token is not changed in $to | 196 * - 0: OLD: $token is not changed in $to |
132 * | 201 * |
133 * @return array | 202 * @return array |
134 */ | 203 */ |
135 public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) | 204 public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) |
136 { | 205 { |
137 preg_match_all('(\r\n|\r|\n)', $from, $fromMatches); | 206 if (\is_string($from)) { |
138 preg_match_all('(\r\n|\r|\n)', $to, $toMatches); | 207 $fromMatches = $this->getNewLineMatches($from); |
139 | 208 $from = $this->splitStringByLines($from); |
140 if (is_string($from)) { | 209 } elseif (\is_array($from)) { |
141 $from = preg_split('(\r\n|\r|\n)', $from); | 210 $fromMatches = array(); |
142 } | 211 } else { |
143 | 212 throw new \InvalidArgumentException('"from" must be an array or string.'); |
144 if (is_string($to)) { | 213 } |
145 $to = preg_split('(\r\n|\r|\n)', $to); | 214 |
146 } | 215 if (\is_string($to)) { |
147 | 216 $toMatches = $this->getNewLineMatches($to); |
148 $start = array(); | 217 $to = $this->splitStringByLines($to); |
149 $end = array(); | 218 } elseif (\is_array($to)) { |
150 $fromLength = count($from); | 219 $toMatches = array(); |
151 $toLength = count($to); | 220 } else { |
152 $length = min($fromLength, $toLength); | 221 throw new \InvalidArgumentException('"to" must be an array or string.'); |
153 | 222 } |
154 for ($i = 0; $i < $length; ++$i) { | 223 |
155 if ($from[$i] === $to[$i]) { | 224 list($from, $to, $start, $end) = self::getArrayDiffParted($from, $to); |
156 $start[] = $from[$i]; | |
157 unset($from[$i], $to[$i]); | |
158 } else { | |
159 break; | |
160 } | |
161 } | |
162 | |
163 $length -= $i; | |
164 | |
165 for ($i = 1; $i < $length; ++$i) { | |
166 if ($from[$fromLength - $i] === $to[$toLength - $i]) { | |
167 array_unshift($end, $from[$fromLength - $i]); | |
168 unset($from[$fromLength - $i], $to[$toLength - $i]); | |
169 } else { | |
170 break; | |
171 } | |
172 } | |
173 | 225 |
174 if ($lcs === null) { | 226 if ($lcs === null) { |
175 $lcs = $this->selectLcsImplementation($from, $to); | 227 $lcs = $this->selectLcsImplementation($from, $to); |
176 } | 228 } |
177 | 229 |
178 $common = $lcs->calculate(array_values($from), array_values($to)); | 230 $common = $lcs->calculate(\array_values($from), \array_values($to)); |
179 $diff = array(); | 231 $diff = array(); |
180 | 232 |
181 if (isset($fromMatches[0]) && $toMatches[0] && | 233 if ($this->detectUnmatchedLineEndings($fromMatches, $toMatches)) { |
182 count($fromMatches[0]) === count($toMatches[0]) && | |
183 $fromMatches[0] !== $toMatches[0]) { | |
184 $diff[] = array( | 234 $diff[] = array( |
185 '#Warning: Strings contain different line endings!', 0 | 235 '#Warning: Strings contain different line endings!', |
236 0 | |
186 ); | 237 ); |
187 } | 238 } |
188 | 239 |
189 foreach ($start as $token) { | 240 foreach ($start as $token) { |
190 $diff[] = array($token, 0 /* OLD */); | 241 $diff[] = array($token, 0 /* OLD */); |
191 } | 242 } |
192 | 243 |
193 reset($from); | 244 \reset($from); |
194 reset($to); | 245 \reset($to); |
195 | 246 |
196 foreach ($common as $token) { | 247 foreach ($common as $token) { |
197 while ((($fromToken = reset($from)) !== $token)) { | 248 while (($fromToken = \reset($from)) !== $token) { |
198 $diff[] = array(array_shift($from), 2 /* REMOVED */); | 249 $diff[] = array(\array_shift($from), 2 /* REMOVED */); |
199 } | 250 } |
200 | 251 |
201 while ((($toToken = reset($to)) !== $token)) { | 252 while (($toToken = \reset($to)) !== $token) { |
202 $diff[] = array(array_shift($to), 1 /* ADDED */); | 253 $diff[] = array(\array_shift($to), 1 /* ADDED */); |
203 } | 254 } |
204 | 255 |
205 $diff[] = array($token, 0 /* OLD */); | 256 $diff[] = array($token, 0 /* OLD */); |
206 | 257 |
207 array_shift($from); | 258 \array_shift($from); |
208 array_shift($to); | 259 \array_shift($to); |
209 } | 260 } |
210 | 261 |
211 while (($token = array_shift($from)) !== null) { | 262 while (($token = \array_shift($from)) !== null) { |
212 $diff[] = array($token, 2 /* REMOVED */); | 263 $diff[] = array($token, 2 /* REMOVED */); |
213 } | 264 } |
214 | 265 |
215 while (($token = array_shift($to)) !== null) { | 266 while (($token = \array_shift($to)) !== null) { |
216 $diff[] = array($token, 1 /* ADDED */); | 267 $diff[] = array($token, 1 /* ADDED */); |
217 } | 268 } |
218 | 269 |
219 foreach ($end as $token) { | 270 foreach ($end as $token) { |
220 $diff[] = array($token, 0 /* OLD */); | 271 $diff[] = array($token, 0 /* OLD */); |
221 } | 272 } |
222 | 273 |
223 return $diff; | 274 return $diff; |
275 } | |
276 | |
277 /** | |
278 * Get new strings denoting new lines from a given string. | |
279 * | |
280 * @param string $string | |
281 * | |
282 * @return array | |
283 */ | |
284 private function getNewLineMatches($string) | |
285 { | |
286 \preg_match_all('(\r\n|\r|\n)', $string, $stringMatches); | |
287 | |
288 return $stringMatches; | |
289 } | |
290 | |
291 /** | |
292 * Checks if input is string, if so it will split it line-by-line. | |
293 * | |
294 * @param string $input | |
295 * | |
296 * @return array | |
297 */ | |
298 private function splitStringByLines($input) | |
299 { | |
300 return \preg_split('(\r\n|\r|\n)', $input); | |
224 } | 301 } |
225 | 302 |
226 /** | 303 /** |
227 * @param array $from | 304 * @param array $from |
228 * @param array $to | 305 * @param array $to |
248 * Calculates the estimated memory footprint for the DP-based method. | 325 * Calculates the estimated memory footprint for the DP-based method. |
249 * | 326 * |
250 * @param array $from | 327 * @param array $from |
251 * @param array $to | 328 * @param array $to |
252 * | 329 * |
253 * @return int | 330 * @return int|float |
254 */ | 331 */ |
255 private function calculateEstimatedFootprint(array $from, array $to) | 332 private function calculateEstimatedFootprint(array $from, array $to) |
256 { | 333 { |
257 $itemSize = PHP_INT_SIZE == 4 ? 76 : 144; | 334 $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; |
258 | 335 |
259 return $itemSize * pow(min(count($from), count($to)), 2); | 336 return $itemSize * \pow(\min(\count($from), \count($to)), 2); |
337 } | |
338 | |
339 /** | |
340 * Returns true if line ends don't match on fromMatches and toMatches. | |
341 * | |
342 * @param array $fromMatches | |
343 * @param array $toMatches | |
344 * | |
345 * @return bool | |
346 */ | |
347 private function detectUnmatchedLineEndings(array $fromMatches, array $toMatches) | |
348 { | |
349 return isset($fromMatches[0], $toMatches[0]) && | |
350 \count($fromMatches[0]) === \count($toMatches[0]) && | |
351 $fromMatches[0] !== $toMatches[0]; | |
352 } | |
353 | |
354 /** | |
355 * @param array $from | |
356 * @param array $to | |
357 * | |
358 * @return array | |
359 */ | |
360 private static function getArrayDiffParted(array &$from, array &$to) | |
361 { | |
362 $start = array(); | |
363 $end = array(); | |
364 | |
365 \reset($to); | |
366 | |
367 foreach ($from as $k => $v) { | |
368 $toK = \key($to); | |
369 | |
370 if ($toK === $k && $v === $to[$k]) { | |
371 $start[$k] = $v; | |
372 | |
373 unset($from[$k], $to[$k]); | |
374 } else { | |
375 break; | |
376 } | |
377 } | |
378 | |
379 \end($from); | |
380 \end($to); | |
381 | |
382 do { | |
383 $fromK = \key($from); | |
384 $toK = \key($to); | |
385 | |
386 if (null === $fromK || null === $toK || \current($from) !== \current($to)) { | |
387 break; | |
388 } | |
389 | |
390 \prev($from); | |
391 \prev($to); | |
392 | |
393 $end = array($fromK => $from[$fromK]) + $end; | |
394 unset($from[$fromK], $to[$toK]); | |
395 } while (true); | |
396 | |
397 return array($from, $to, $start, $end); | |
260 } | 398 } |
261 } | 399 } |