Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like HtmlDiff often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use HtmlDiff, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
10 | class HtmlDiff extends AbstractDiff |
||
11 | { |
||
12 | /** |
||
13 | * @var array |
||
14 | */ |
||
15 | protected $wordIndices; |
||
16 | /** |
||
17 | * @var array |
||
18 | */ |
||
19 | protected $oldTables; |
||
20 | /** |
||
21 | * @var array |
||
22 | */ |
||
23 | protected $newTables; |
||
24 | /** |
||
25 | * @var array |
||
26 | */ |
||
27 | protected $newIsolatedDiffTags; |
||
28 | /** |
||
29 | * @var array |
||
30 | */ |
||
31 | protected $oldIsolatedDiffTags; |
||
32 | |||
33 | /** |
||
34 | * @param string $oldText |
||
35 | * @param string $newText |
||
36 | * @param HtmlDiffConfig|null $config |
||
37 | * |
||
38 | * @return self |
||
39 | */ |
||
40 | 14 | View Code Duplication | public static function create($oldText, $newText, HtmlDiffConfig $config = null) |
50 | |||
51 | /** |
||
52 | * @param $bool |
||
53 | * |
||
54 | * @return $this |
||
55 | * |
||
56 | * @deprecated since 0.1.0 |
||
57 | */ |
||
58 | public function setUseTableDiffing($bool) |
||
64 | |||
65 | /** |
||
66 | * @param bool $boolean |
||
67 | * |
||
68 | * @return HtmlDiff |
||
69 | * |
||
70 | * @deprecated since 0.1.0 |
||
71 | */ |
||
72 | public function setInsertSpaceInReplace($boolean) |
||
78 | |||
79 | /** |
||
80 | * @return bool |
||
81 | * |
||
82 | * @deprecated since 0.1.0 |
||
83 | */ |
||
84 | public function getInsertSpaceInReplace() |
||
88 | |||
89 | /** |
||
90 | * @return string |
||
91 | */ |
||
92 | 17 | public function build() |
|
93 | { |
||
94 | 17 | $this->prepare(); |
|
95 | |||
96 | 17 | View Code Duplication | if ($this->hasDiffCache() && $this->getDiffCache()->contains($this->oldText, $this->newText)) { |
97 | $this->content = $this->getDiffCache()->fetch($this->oldText, $this->newText); |
||
98 | |||
99 | return $this->content; |
||
100 | } |
||
101 | |||
102 | // Pre-processing Optimizations |
||
103 | |||
104 | // 1. Equality |
||
105 | 17 | if ($this->oldText == $this->newText) { |
|
106 | 11 | return $this->newText; |
|
107 | } |
||
108 | |||
109 | 17 | $this->splitInputsToWords(); |
|
110 | 17 | $this->replaceIsolatedDiffTags(); |
|
111 | 17 | $this->indexNewWords(); |
|
112 | |||
113 | 17 | $operations = $this->operations(); |
|
114 | |||
115 | 17 | foreach ($operations as $item) { |
|
116 | 17 | $this->performOperation($item); |
|
117 | } |
||
118 | |||
119 | 17 | if ($this->hasDiffCache()) { |
|
120 | $this->getDiffCache()->save($this->oldText, $this->newText, $this->content); |
||
121 | } |
||
122 | |||
123 | 17 | return $this->content; |
|
124 | } |
||
125 | |||
126 | 17 | protected function indexNewWords() |
|
140 | |||
141 | 17 | protected function replaceIsolatedDiffTags() |
|
146 | |||
147 | /** |
||
148 | * @param array $words |
||
149 | * |
||
150 | * @return array |
||
151 | */ |
||
152 | 17 | protected function createIsolatedDiffTagPlaceholders(&$words) |
|
196 | |||
197 | /** |
||
198 | * @param string $item |
||
199 | * @param null|string $currentIsolatedDiffTag |
||
200 | * |
||
201 | * @return false|string |
||
202 | */ |
||
203 | 17 | View Code Duplication | protected function isOpeningIsolatedDiffTag($item, $currentIsolatedDiffTag = null) |
217 | |||
218 | 15 | protected function isSelfClosingTag($text) |
|
222 | |||
223 | /** |
||
224 | * @param string $item |
||
225 | * @param null|string $currentIsolatedDiffTag |
||
226 | * |
||
227 | * @return false|string |
||
228 | */ |
||
229 | 15 | View Code Duplication | protected function isClosingIsolatedDiffTag($item, $currentIsolatedDiffTag = null) |
243 | |||
244 | /** |
||
245 | * @param Operation $operation |
||
246 | */ |
||
247 | 17 | protected function performOperation($operation) |
|
266 | |||
267 | /** |
||
268 | * @param Operation $operation |
||
269 | */ |
||
270 | 10 | protected function processReplaceOperation($operation) |
|
275 | |||
276 | /** |
||
277 | * @param Operation $operation |
||
278 | * @param string $cssClass |
||
279 | */ |
||
280 | 14 | View Code Duplication | protected function processInsertOperation($operation, $cssClass) |
281 | { |
||
282 | 14 | $text = array(); |
|
283 | 14 | foreach ($this->newWords as $pos => $s) { |
|
284 | 14 | if ($pos >= $operation->startInNew && $pos < $operation->endInNew) { |
|
285 | 14 | if ($this->config->isIsolatedDiffTagPlaceholder($s) && isset($this->newIsolatedDiffTags[$pos])) { |
|
286 | 4 | foreach ($this->newIsolatedDiffTags[$pos] as $word) { |
|
287 | 4 | $text[] = $word; |
|
288 | } |
||
289 | } else { |
||
290 | 14 | $text[] = $s; |
|
291 | } |
||
292 | } |
||
293 | } |
||
294 | |||
295 | 14 | $this->insertTag('ins', $cssClass, $text); |
|
296 | 14 | } |
|
297 | |||
298 | /** |
||
299 | * @param Operation $operation |
||
300 | * @param string $cssClass |
||
301 | */ |
||
302 | 12 | View Code Duplication | protected function processDeleteOperation($operation, $cssClass) |
303 | { |
||
304 | 12 | $text = array(); |
|
305 | 12 | foreach ($this->oldWords as $pos => $s) { |
|
306 | 12 | if ($pos >= $operation->startInOld && $pos < $operation->endInOld) { |
|
307 | 12 | if ($this->config->isIsolatedDiffTagPlaceholder($s) && isset($this->oldIsolatedDiffTags[$pos])) { |
|
308 | 6 | foreach ($this->oldIsolatedDiffTags[$pos] as $word) { |
|
309 | 6 | $text[] = $word; |
|
310 | } |
||
311 | } else { |
||
312 | 12 | $text[] = $s; |
|
313 | } |
||
314 | } |
||
315 | } |
||
316 | 12 | $this->insertTag('del', $cssClass, $text); |
|
317 | 12 | } |
|
318 | |||
319 | /** |
||
320 | * @param Operation $operation |
||
321 | * @param int $pos |
||
322 | * @param string $placeholder |
||
323 | * @param bool $stripWrappingTags |
||
324 | * |
||
325 | * @return string |
||
326 | */ |
||
327 | 14 | protected function diffIsolatedPlaceholder($operation, $pos, $placeholder, $stripWrappingTags = true) |
|
328 | { |
||
329 | 14 | $oldText = implode('', $this->findIsolatedDiffTagsInOld($operation, $pos)); |
|
330 | 14 | $newText = implode('', $this->newIsolatedDiffTags[$pos]); |
|
331 | |||
332 | 14 | if ($this->isListPlaceholder($placeholder)) { |
|
333 | 8 | return $this->diffList($oldText, $newText); |
|
334 | 10 | } elseif ($this->config->isUseTableDiffing() && $this->isTablePlaceholder($placeholder)) { |
|
335 | 1 | return $this->diffTables($oldText, $newText); |
|
336 | 9 | } elseif ($this->isLinkPlaceholder($placeholder)) { |
|
337 | 1 | return $this->diffElementsByAttribute($oldText, $newText, 'href', 'a'); |
|
338 | 8 | } elseif ($this->isImagePlaceholder($placeholder)) { |
|
339 | return $this->diffElementsByAttribute($oldText, $newText, 'src', 'img'); |
||
340 | } |
||
341 | |||
342 | 8 | return $this->diffElements($oldText, $newText, $stripWrappingTags); |
|
343 | } |
||
344 | |||
345 | /** |
||
346 | * @param string $oldText |
||
347 | * @param string $newText |
||
348 | * @param bool $stripWrappingTags |
||
349 | * |
||
350 | * @return string |
||
351 | */ |
||
352 | 9 | protected function diffElements($oldText, $newText, $stripWrappingTags = true) |
|
353 | { |
||
354 | 9 | $wrapStart = ''; |
|
355 | 9 | $wrapEnd = ''; |
|
356 | |||
357 | 9 | if ($stripWrappingTags) { |
|
358 | 9 | $pattern = '/(^<[^>]+>)|(<\/[^>]+>$)/iu'; |
|
359 | 9 | $matches = array(); |
|
360 | |||
361 | 9 | if (preg_match_all($pattern, $newText, $matches)) { |
|
362 | 9 | $wrapStart = isset($matches[0][0]) ? $matches[0][0] : ''; |
|
363 | 9 | $wrapEnd = isset($matches[0][1]) ? $matches[0][1] : ''; |
|
364 | } |
||
365 | 9 | $oldText = preg_replace($pattern, '', $oldText); |
|
366 | 9 | $newText = preg_replace($pattern, '', $newText); |
|
367 | } |
||
368 | |||
369 | 9 | $diff = self::create($oldText, $newText, $this->config); |
|
370 | |||
371 | 9 | return $wrapStart.$diff->build().$wrapEnd; |
|
372 | } |
||
373 | |||
374 | /** |
||
375 | * @param string $oldText |
||
376 | * @param string $newText |
||
377 | * |
||
378 | * @return string |
||
379 | */ |
||
380 | 8 | protected function diffList($oldText, $newText) |
|
381 | { |
||
382 | 8 | $diff = ListDiffLines::create($oldText, $newText, $this->config); |
|
383 | |||
384 | 8 | return $diff->build(); |
|
385 | } |
||
386 | |||
387 | /** |
||
388 | * @param string $oldText |
||
389 | * @param string $newText |
||
390 | * |
||
391 | * @return string |
||
392 | */ |
||
393 | 1 | protected function diffTables($oldText, $newText) |
|
394 | { |
||
395 | 1 | $diff = TableDiff::create($oldText, $newText, $this->config); |
|
396 | |||
397 | 1 | return $diff->build(); |
|
398 | } |
||
399 | |||
400 | 1 | protected function diffElementsByAttribute($oldText, $newText, $attribute, $element) |
|
401 | { |
||
402 | 1 | $oldAttribute = $this->getAttributeFromTag($oldText, $attribute); |
|
403 | 1 | $newAttribute = $this->getAttributeFromTag($newText, $attribute); |
|
404 | |||
405 | 1 | if ($oldAttribute !== $newAttribute) { |
|
406 | 1 | $diffClass = sprintf('diffmod diff%s diff%s', $element, $attribute); |
|
407 | |||
408 | 1 | return sprintf( |
|
409 | 1 | '%s%s', |
|
410 | 1 | $this->wrapText($oldText, 'del', $diffClass), |
|
411 | 1 | $this->wrapText($newText, 'ins', $diffClass) |
|
412 | ); |
||
413 | } |
||
414 | |||
415 | 1 | return $this->diffElements($oldText, $newText); |
|
416 | } |
||
417 | |||
418 | /** |
||
419 | * @param Operation $operation |
||
420 | */ |
||
421 | 17 | View Code Duplication | protected function processEqualOperation($operation) |
422 | { |
||
423 | 17 | $result = array(); |
|
424 | 17 | foreach ($this->newWords as $pos => $s) { |
|
425 | 17 | if ($pos >= $operation->startInNew && $pos < $operation->endInNew) { |
|
426 | 17 | if ($this->config->isIsolatedDiffTagPlaceholder($s) && isset($this->newIsolatedDiffTags[$pos])) { |
|
427 | 14 | $result[] = $this->diffIsolatedPlaceholder($operation, $pos, $s); |
|
428 | } else { |
||
429 | 13 | $result[] = $s; |
|
430 | } |
||
431 | } |
||
432 | } |
||
433 | 17 | $this->content .= implode('', $result); |
|
434 | 17 | } |
|
435 | |||
436 | /** |
||
437 | * @param string $text |
||
438 | * @param string $attribute |
||
439 | * |
||
440 | * @return null|string |
||
441 | */ |
||
442 | 1 | protected function getAttributeFromTag($text, $attribute) |
|
443 | { |
||
444 | 1 | $matches = array(); |
|
445 | 1 | if (preg_match(sprintf('/<[^>]*\b%s\s*=\s*([\'"])(.*)\1[^>]*>/iu', $attribute), $text, $matches)) { |
|
446 | 1 | return htmlspecialchars_decode($matches[2]); |
|
447 | } |
||
448 | |||
449 | return; |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * @param string $text |
||
454 | * |
||
455 | * @return bool |
||
456 | */ |
||
457 | 14 | protected function isListPlaceholder($text) |
|
458 | { |
||
459 | 14 | return $this->isPlaceholderType($text, array('ol', 'dl', 'ul')); |
|
460 | } |
||
461 | |||
462 | /** |
||
463 | * @param string $text |
||
464 | * |
||
465 | * @return bool |
||
466 | */ |
||
467 | 9 | public function isLinkPlaceholder($text) |
|
468 | { |
||
469 | 9 | return $this->isPlaceholderType($text, 'a'); |
|
470 | } |
||
471 | |||
472 | /** |
||
473 | * @param string $text |
||
474 | * |
||
475 | * @return bool |
||
476 | */ |
||
477 | 8 | public function isImagePlaceholder($text) |
|
478 | { |
||
479 | 8 | return $this->isPlaceholderType($text, 'img'); |
|
480 | } |
||
481 | |||
482 | /** |
||
483 | * @param string $text |
||
484 | * @param array|string $types |
||
485 | * @param bool $strict |
||
486 | * |
||
487 | * @return bool |
||
488 | */ |
||
489 | 14 | protected function isPlaceholderType($text, $types, $strict = true) |
|
490 | { |
||
491 | 14 | if (!is_array($types)) { |
|
492 | 10 | $types = array($types); |
|
493 | } |
||
494 | |||
495 | 14 | $criteria = array(); |
|
496 | 14 | foreach ($types as $type) { |
|
497 | 14 | if ($this->config->isIsolatedDiffTag($type)) { |
|
498 | 14 | $criteria[] = $this->config->getIsolatedDiffTagPlaceholder($type); |
|
499 | } else { |
||
500 | $criteria[] = $type; |
||
501 | } |
||
502 | } |
||
503 | |||
504 | 14 | return in_array($text, $criteria, $strict); |
|
505 | } |
||
506 | |||
507 | /** |
||
508 | * @param string $text |
||
509 | * |
||
510 | * @return bool |
||
511 | */ |
||
512 | 10 | protected function isTablePlaceholder($text) |
|
513 | { |
||
514 | 10 | return $this->isPlaceholderType($text, 'table'); |
|
515 | } |
||
516 | |||
517 | /** |
||
518 | * @param Operation $operation |
||
519 | * @param int $posInNew |
||
520 | * |
||
521 | * @return array |
||
522 | */ |
||
523 | 14 | protected function findIsolatedDiffTagsInOld($operation, $posInNew) |
|
524 | { |
||
525 | 14 | $offset = $posInNew - $operation->startInNew; |
|
526 | |||
527 | 14 | return $this->oldIsolatedDiffTags[$operation->startInOld + $offset]; |
|
528 | } |
||
529 | |||
530 | /** |
||
531 | * @param string $tag |
||
532 | * @param string $cssClass |
||
533 | * @param array $words |
||
534 | */ |
||
535 | 14 | protected function insertTag($tag, $cssClass, &$words) |
|
536 | { |
||
537 | 14 | while (true) { |
|
538 | 14 | if (count($words) === 0) { |
|
539 | 9 | break; |
|
540 | } |
||
541 | |||
542 | 14 | $nonTags = $this->extractConsecutiveWords($words, 'noTag'); |
|
543 | |||
544 | 14 | $specialCaseTagInjection = ''; |
|
545 | 14 | $specialCaseTagInjectionIsBefore = false; |
|
546 | |||
547 | 14 | if (count($nonTags) !== 0) { |
|
548 | 14 | $this->content .= $this->wrapText(implode('', $nonTags), $tag, $cssClass); |
|
549 | } else { |
||
550 | 7 | $firstOrDefault = false; |
|
551 | 7 | foreach ($this->config->getSpecialCaseOpeningTags() as $x) { |
|
552 | if (preg_match($x, $words[ 0 ])) { |
||
553 | $firstOrDefault = $x; |
||
554 | break; |
||
555 | } |
||
556 | } |
||
557 | 7 | if ($firstOrDefault) { |
|
558 | $specialCaseTagInjection = '<ins class="mod">'; |
||
559 | if ($tag === 'del') { |
||
560 | unset($words[ 0 ]); |
||
561 | } |
||
562 | 7 | } elseif (array_search($words[ 0 ], $this->config->getSpecialCaseClosingTags()) !== false) { |
|
563 | $specialCaseTagInjection = '</ins>'; |
||
564 | $specialCaseTagInjectionIsBefore = true; |
||
565 | if ($tag === 'del') { |
||
566 | unset($words[ 0 ]); |
||
567 | } |
||
568 | } |
||
569 | } |
||
570 | 14 | if (count($words) == 0 && $this->stringUtil->strlen($specialCaseTagInjection) == 0) { |
|
571 | 13 | break; |
|
572 | } |
||
573 | 9 | if ($specialCaseTagInjectionIsBefore) { |
|
574 | $this->content .= $specialCaseTagInjection . implode('', $this->extractConsecutiveWords($words, 'tag')); |
||
575 | } else { |
||
576 | 9 | $workTag = $this->extractConsecutiveWords($words, 'tag'); |
|
577 | |||
578 | if ( |
||
579 | 9 | isset($workTag[0]) === true && |
|
580 | 9 | $this->isOpeningTag($workTag[0]) === true && |
|
581 | 9 | $this->isClosingTag($workTag[0]) === false |
|
582 | ) { |
||
583 | 9 | if ($this->stringUtil->strpos($workTag[0], 'class=')) { |
|
584 | 2 | $workTag[0] = str_replace('class="', 'class="diffmod ', $workTag[0]); |
|
585 | } else { |
||
586 | 9 | $isSelfClosing = $this->stringUtil->strpos($workTag[0], '/>') !== false; |
|
587 | |||
588 | 9 | if ($isSelfClosing === true) { |
|
589 | 5 | $workTag[0] = str_replace('/>', ' class="diffmod" />', $workTag[0]); |
|
590 | } else { |
||
591 | 8 | $workTag[0] = str_replace('>', ' class="diffmod">', $workTag[0]); |
|
592 | } |
||
593 | } |
||
594 | } |
||
595 | |||
596 | 9 | $appendContent = implode('', $workTag) . $specialCaseTagInjection; |
|
597 | |||
598 | 9 | if (isset($workTag[0]) === true && $this->stringUtil->stripos($workTag[0], '<img') !== false) { |
|
599 | $appendContent = $this->wrapText($appendContent, $tag, $cssClass); |
||
600 | } |
||
601 | |||
602 | 9 | $this->content .= $appendContent; |
|
603 | } |
||
604 | } |
||
605 | 14 | } |
|
606 | |||
607 | /** |
||
608 | * @param string $word |
||
609 | * @param string $condition |
||
610 | * |
||
611 | * @return bool |
||
612 | */ |
||
613 | 14 | protected function checkCondition($word, $condition) |
|
614 | { |
||
615 | 14 | return $condition == 'tag' ? $this->isTag($word) : !$this->isTag($word); |
|
616 | } |
||
617 | |||
618 | 15 | protected function wrapText(string $text, string $tagName, string $cssClass) : string |
|
619 | { |
||
620 | 15 | if (trim($text) === '') { |
|
621 | 7 | return ''; |
|
622 | } |
||
623 | |||
624 | 15 | return sprintf('<%1$s class="%2$s">%3$s</%1$s>', $tagName, $cssClass, $text); |
|
625 | } |
||
626 | |||
627 | /** |
||
628 | * @param array $words |
||
629 | * @param string $condition |
||
630 | * |
||
631 | * @return array |
||
632 | */ |
||
633 | 14 | protected function extractConsecutiveWords(&$words, $condition) |
|
634 | { |
||
635 | 14 | $indexOfFirstTag = null; |
|
636 | 14 | $words = array_values($words); |
|
637 | 14 | foreach ($words as $i => $word) { |
|
638 | 14 | if (!$this->checkCondition($word, $condition)) { |
|
639 | 9 | $indexOfFirstTag = $i; |
|
640 | 9 | break; |
|
641 | } |
||
642 | } |
||
643 | 14 | if ($indexOfFirstTag !== null) { |
|
644 | 9 | $items = array(); |
|
645 | 9 | View Code Duplication | foreach ($words as $pos => $s) { |
646 | 9 | if ($pos >= 0 && $pos < $indexOfFirstTag) { |
|
647 | 9 | $items[] = $s; |
|
648 | } |
||
649 | } |
||
650 | 9 | if ($indexOfFirstTag > 0) { |
|
651 | 9 | array_splice($words, 0, $indexOfFirstTag); |
|
652 | } |
||
653 | |||
654 | 9 | return $items; |
|
655 | } else { |
||
656 | 14 | $items = array(); |
|
657 | 14 | View Code Duplication | foreach ($words as $pos => $s) { |
658 | 14 | if ($pos >= 0 && $pos <= count($words)) { |
|
659 | 14 | $items[] = $s; |
|
660 | } |
||
661 | } |
||
662 | 14 | array_splice($words, 0, count($words)); |
|
663 | |||
664 | 14 | return $items; |
|
665 | } |
||
666 | } |
||
667 | |||
668 | /** |
||
669 | * @param string $item |
||
670 | * |
||
671 | * @return bool |
||
672 | */ |
||
673 | 17 | protected function isTag($item) |
|
677 | |||
678 | 17 | protected function isOpeningTag($item) : bool |
|
679 | { |
||
680 | 17 | return preg_match('#<[^>]+>\\s*#iUu', $item) === 1; |
|
682 | |||
683 | 17 | protected function isClosingTag($item) : bool |
|
687 | |||
688 | /** |
||
689 | * @return Operation[] |
||
690 | */ |
||
691 | 17 | protected function operations() |
|
692 | { |
||
693 | 17 | $positionInOld = 0; |
|
694 | 17 | $positionInNew = 0; |
|
695 | 17 | $operations = array(); |
|
696 | |||
697 | 17 | $matches = $this->matchingBlocks(); |
|
698 | 17 | $matches[] = new MatchingBlock(count($this->oldWords), count($this->newWords), 0); |
|
699 | |||
700 | 17 | foreach ($matches as $match) { |
|
701 | 17 | $matchStartsAtCurrentPositionInOld = ($positionInOld === $match->startInOld); |
|
702 | 17 | $matchStartsAtCurrentPositionInNew = ($positionInNew === $match->startInNew); |
|
703 | |||
728 | |||
729 | /** |
||
730 | * @return MatchingBlock[] |
||
731 | */ |
||
732 | 17 | protected function matchingBlocks() |
|
739 | |||
740 | /** |
||
741 | * @param int $startInOld |
||
742 | * @param int $endInOld |
||
743 | * @param int $startInNew |
||
744 | * @param int $endInNew |
||
745 | * @param array $matchingBlocks |
||
746 | */ |
||
747 | 17 | protected function findMatchingBlocks($startInOld, $endInOld, $startInNew, $endInNew, &$matchingBlocks) |
|
763 | |||
764 | /** |
||
765 | * @param string $word |
||
766 | * |
||
767 | * @return string |
||
768 | */ |
||
769 | 9 | protected function stripTagAttributes($word) |
|
779 | |||
780 | /** |
||
781 | * @param int $startInOld |
||
782 | * @param int $endInOld |
||
783 | * @param int $startInNew |
||
784 | * @param int $endInNew |
||
785 | * |
||
786 | * @return MatchingBlock|null |
||
787 | */ |
||
788 | 17 | protected function findMatch($startInOld, $endInOld, $startInNew, $endInNew) |
|
844 | |||
845 | /** |
||
846 | * @param string $str |
||
847 | * |
||
848 | * @return bool |
||
849 | */ |
||
850 | 17 | protected function isOnlyWhitespace($str) |
|
855 | |||
856 | /** |
||
857 | * Special array_slice function that caches its last request. |
||
858 | * |
||
859 | * The diff algorithm seems to request the same information many times in a row. |
||
860 | * by returning the previous answer the algorithm preforms way faster. |
||
861 | * |
||
862 | * The result is a string instead of an array, this way we safe on the amount of |
||
863 | * memory intensive implode() calls. |
||
864 | * |
||
865 | * @param array &$array |
||
866 | * @param integer $offset |
||
867 | * @param integer|null $length |
||
868 | * |
||
869 | * @return string |
||
870 | */ |
||
871 | 17 | protected function array_slice_cached(&$array, $offset, $length = null) |
|
900 | } |
||
901 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.