$old * @param list $new * @return list " kept", "- removed", "+ added"; "…" between changes more than 2 × $context lines apart. */ public static function lines(array $old, array $new, int $context = 2): array { $operations = self::operations($old, $new); $keep = []; foreach ($operations as $i => [$op]) { if ($op === ' ') { continue; } for ($j = max(0, $i - $context); $j <= min(count($operations) - 1, $i + $context); ++$j) { $keep[$j] = true; } } $lines = []; $last = null; foreach ($operations as $i => [$op, $line]) { if (! isset($keep[$i])) { continue; } if ($last !== null && $i > $last + 1) { $lines[] = '…'; } $lines[] = $op . ' ' . $line; $last = $i; } return $lines; } /** * @param list $a * @param list $b * @return list */ private static function operations(array $a, array $b): array { $n = count($a); $m = count($b); $lcs = array_fill(0, $n + 1, array_fill(0, $m + 1, 0)); for ($i = $n - 1; $i >= 0; --$i) { for ($j = $m - 1; $j >= 0; --$j) { $lcs[$i][$j] = $a[$i] === $b[$j] ? $lcs[$i + 1][$j + 1] + 1 : max($lcs[$i + 1][$j], $lcs[$i][$j + 1]); } } $operations = []; $i = 0; $j = 0; while ($i < $n && $j < $m) { if ($a[$i] === $b[$j]) { $operations[] = [' ', $a[$i]]; ++$i; ++$j; } elseif ($lcs[$i + 1][$j] >= $lcs[$i][$j + 1]) { $operations[] = ['-', $a[$i++]]; } else { $operations[] = ['+', $b[$j++]]; } } while ($i < $n) { $operations[] = ['-', $a[$i++]]; } while ($j < $m) { $operations[] = ['+', $b[$j++]]; } return $operations; } }