One Hat Cyber Team
Your IP:
216.73.216.176
Server IP:
198.54.114.155
Server:
Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
Server Software:
LiteSpeed
PHP Version:
5.6.40
Create File
|
Create Folder
Execute
Dir :
~
/
proc
/
self
/
cwd
/
View File Name :
diff.tar
src/LongestCommonSubsequenceCalculator.php 0000644 00000000746 15111346261 0015054 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; interface LongestCommonSubsequenceCalculator { /** * Calculates the longest common subsequence of two arrays. */ public function calculate(array $from, array $to): array; } src/Differ.php 0000644 00000015113 15111346261 0007244 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use const PHP_INT_SIZE; use const PREG_SPLIT_DELIM_CAPTURE; use const PREG_SPLIT_NO_EMPTY; use function array_shift; use function array_unshift; use function array_values; use function count; use function current; use function end; use function is_string; use function key; use function min; use function preg_split; use function prev; use function reset; use function str_ends_with; use function substr; use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; final class Differ { public const OLD = 0; public const ADDED = 1; public const REMOVED = 2; public const DIFF_LINE_END_WARNING = 3; public const NO_LINE_END_EOF_WARNING = 4; private DiffOutputBuilderInterface $outputBuilder; public function __construct(DiffOutputBuilderInterface $outputBuilder) { $this->outputBuilder = $outputBuilder; } public function diff(array|string $from, array|string $to, LongestCommonSubsequenceCalculator $lcs = null): string { $diff = $this->diffToArray($from, $to, $lcs); return $this->outputBuilder->getDiff($diff); } public function diffToArray(array|string $from, array|string $to, LongestCommonSubsequenceCalculator $lcs = null): array { if (is_string($from)) { $from = $this->splitStringByLines($from); } if (is_string($to)) { $to = $this->splitStringByLines($to); } [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); if ($lcs === null) { $lcs = $this->selectLcsImplementation($from, $to); } $common = $lcs->calculate(array_values($from), array_values($to)); $diff = []; foreach ($start as $token) { $diff[] = [$token, self::OLD]; } reset($from); reset($to); foreach ($common as $token) { while (($fromToken = reset($from)) !== $token) { $diff[] = [array_shift($from), self::REMOVED]; } while (($toToken = reset($to)) !== $token) { $diff[] = [array_shift($to), self::ADDED]; } $diff[] = [$token, self::OLD]; array_shift($from); array_shift($to); } while (($token = array_shift($from)) !== null) { $diff[] = [$token, self::REMOVED]; } while (($token = array_shift($to)) !== null) { $diff[] = [$token, self::ADDED]; } foreach ($end as $token) { $diff[] = [$token, self::OLD]; } if ($this->detectUnmatchedLineEndings($diff)) { array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); } return $diff; } private function splitStringByLines(string $input): array { return preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator { // We do not want to use the time-efficient implementation if its memory // footprint will probably exceed this value. Note that the footprint // calculation is only an estimation for the matrix and the LCS method // will typically allocate a bit more memory than this. $memoryLimit = 100 * 1024 * 1024; if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { return new MemoryEfficientLongestCommonSubsequenceCalculator; } return new TimeEfficientLongestCommonSubsequenceCalculator; } private function calculateEstimatedFootprint(array $from, array $to): float|int { $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; return $itemSize * min(count($from), count($to)) ** 2; } private function detectUnmatchedLineEndings(array $diff): bool { $newLineBreaks = ['' => true]; $oldLineBreaks = ['' => true]; foreach ($diff as $entry) { if (self::OLD === $entry[1]) { $ln = $this->getLinebreak($entry[0]); $oldLineBreaks[$ln] = true; $newLineBreaks[$ln] = true; } elseif (self::ADDED === $entry[1]) { $newLineBreaks[$this->getLinebreak($entry[0])] = true; } elseif (self::REMOVED === $entry[1]) { $oldLineBreaks[$this->getLinebreak($entry[0])] = true; } } // if either input or output is a single line without breaks than no warning should be raised if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { return false; } // two-way compare foreach ($newLineBreaks as $break => $set) { if (!isset($oldLineBreaks[$break])) { return true; } } foreach ($oldLineBreaks as $break => $set) { if (!isset($newLineBreaks[$break])) { return true; } } return false; } private function getLinebreak($line): string { if (!is_string($line)) { return ''; } $lc = substr($line, -1); if ("\r" === $lc) { return "\r"; } if ("\n" !== $lc) { return ''; } if (str_ends_with($line, "\r\n")) { return "\r\n"; } return "\n"; } private static function getArrayDiffParted(array &$from, array &$to): array { $start = []; $end = []; reset($to); foreach ($from as $k => $v) { $toK = key($to); if ($toK === $k && $v === $to[$k]) { $start[$k] = $v; unset($from[$k], $to[$k]); } else { break; } } end($from); end($to); do { $fromK = key($from); $toK = key($to); if (null === $fromK || null === $toK || current($from) !== current($to)) { break; } prev($from); prev($to); $end = [$fromK => $from[$fromK]] + $end; unset($from[$fromK], $to[$toK]); } while (true); return [$from, $to, $start, $end]; } } src/Diff.php 0000644 00000002136 15111346261 0006716 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Diff { private string $from; private string $to; /** * @psalm-var list<Chunk> */ private array $chunks; /** * @psalm-param list<Chunk> $chunks */ public function __construct(string $from, string $to, array $chunks = []) { $this->from = $from; $this->to = $to; $this->chunks = $chunks; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } /** * @psalm-return list<Chunk> */ public function getChunks(): array { return $this->chunks; } /** * @psalm-param list<Chunk> $chunks */ public function setChunks(array $chunks): void { $this->chunks = $chunks; } } src/Output/DiffOnlyOutputBuilder.php 0000644 00000004061 15111346261 0013607 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function fclose; use function fopen; use function fwrite; use function str_ends_with; use function stream_get_contents; use function substr; use SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in a loose unified diff format * listing only changes lines. Does not include line numbers. */ final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface { private string $header; public function __construct(string $header = "--- Original\n+++ New\n") { $this->header = $header; } public function getDiff(array $diff): string { $buffer = fopen('php://memory', 'r+b'); if ('' !== $this->header) { fwrite($buffer, $this->header); if (!str_ends_with($this->header, "\n")) { fwrite($buffer, "\n"); } } foreach ($diff as $diffEntry) { if ($diffEntry[1] === Differ::ADDED) { fwrite($buffer, '+' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::REMOVED) { fwrite($buffer, '-' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { fwrite($buffer, ' ' . $diffEntry[0]); continue; // Warnings should not be tested for line break, it will always be there } else { /* Not changed (old) 0 */ continue; // we didn't write the not-changed line, so do not add a line break either } $lc = substr($diffEntry[0], -1); if ($lc !== "\n" && $lc !== "\r") { fwrite($buffer, "\n"); // \No newline at end of file } } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); return $diff; } } src/Output/StrictUnifiedDiffOutputBuilder.php 0000644 00000025242 15111346261 0015446 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function array_merge; use function array_splice; use function count; use function fclose; use function fopen; use function fwrite; use function is_bool; use function is_int; use function is_string; use function max; use function min; use function sprintf; use function stream_get_contents; use function substr; use SebastianBergmann\Diff\ConfigurationException; use SebastianBergmann\Diff\Differ; /** * Strict Unified diff output builder. * * Generates (strict) Unified diff's (unidiffs) with hunks. */ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface { private static array $default = [ 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 'fromFile' => null, 'fromFileDate' => null, 'toFile' => null, 'toFileDate' => null, ]; private bool $changed; private bool $collapseRanges; /** * @psalm-var positive-int */ private int $commonLineThreshold; private string $header; /** * @psalm-var positive-int */ private int $contextLines; public function __construct(array $options = []) { $options = array_merge(self::$default, $options); if (!is_bool($options['collapseRanges'])) { throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); } if (!is_int($options['contextLines']) || $options['contextLines'] < 0) { throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); } if (!is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); } $this->assertString($options, 'fromFile'); $this->assertString($options, 'toFile'); $this->assertStringOrNull($options, 'fromFileDate'); $this->assertStringOrNull($options, 'toFileDate'); $this->header = sprintf( "--- %s%s\n+++ %s%s\n", $options['fromFile'], null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], $options['toFile'], null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'] ); $this->collapseRanges = $options['collapseRanges']; $this->commonLineThreshold = $options['commonLineThreshold']; $this->contextLines = $options['contextLines']; } public function getDiff(array $diff): string { if (0 === count($diff)) { return ''; } $this->changed = false; $buffer = fopen('php://memory', 'r+b'); fwrite($buffer, $this->header); $this->writeDiffHunks($buffer, $diff); if (!$this->changed) { fclose($buffer); return ''; } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); // If the last char is not a linebreak: add it. // This might happen when both the `from` and `to` do not have a trailing linebreak $last = substr($diff, -1); return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } private function writeDiffHunks($output, array $diff): void { // detect "No newline at end of file" and insert into `$diff` if needed $upperLimit = count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { // search back for the last `+` and `-` line, // check if it has a trailing linebreak, else add a warning under it $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; $i--) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = substr($diff[$i][0], -1); if ("\n" !== $lc) { array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!count($toFind)) { break; } } } } // write hunks to output buffer $cutOff = max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; $i = 0; /** @var int $i */ foreach ($diff as $i => $entry) { if (0 === $entry[1]) { // same if (false === $hunkCapture) { $fromStart++; $toStart++; continue; } $sameCount++; $toRange++; $fromRange++; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines; // note: $contextEndOffset = $this->contextLines; // // because we never go beyond the end of the diff. // with the cutoff/contextlines here the follow is never true; // // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { // $contextEndOffset = count($diff) - 1; // } // // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } $this->changed = true; if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { // added $toRange++; } if (Differ::REMOVED === $entry[1]) { // removed $fromRange++; } } if (false === $hunkCapture) { return; } // we end here when cutoff (commonLineThreshold) was not reached, but we were capturing a hunk, // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; // prevent trying to write out more common lines than there are in the diff _and_ // do not write more than configured through the context lines $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { fwrite($output, ',' . $fromRange); } fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { fwrite($output, ',' . $toRange); } fwrite($output, " @@\n"); for ($i = $diffStartIndex; $i < $diffEndIndex; $i++) { if ($diff[$i][1] === Differ::ADDED) { $this->changed = true; fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { $this->changed = true; fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { $this->changed = true; fwrite($output, $diff[$i][0]); } //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package // skip //} else { // unknown/invalid //} } } private function assertString(array $options, string $option): void { if (!is_string($options[$option])) { throw new ConfigurationException($option, 'a string', $options[$option]); } } private function assertStringOrNull(array $options, string $option): void { if (null !== $options[$option] && !is_string($options[$option])) { throw new ConfigurationException($option, 'a string or <null>', $options[$option]); } } } src/Output/DiffOutputBuilderInterface.php 0000644 00000001011 15111346261 0014556 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; /** * Defines how an output builder should take a generated * diff array and return a string representation of that diff. */ interface DiffOutputBuilderInterface { public function getDiff(array $diff): string; } src/Output/UnifiedDiffOutputBuilder.php 0000644 00000020323 15111346261 0014250 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function array_splice; use function count; use function fclose; use function fopen; use function fwrite; use function max; use function min; use function str_ends_with; use function stream_get_contents; use function strlen; use function substr; use SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in unified diff format in chunks. */ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder { private bool $collapseRanges = true; private int $commonLineThreshold = 6; /** * @psalm-var positive-int */ private int $contextLines = 3; private string $header; private bool $addLineNumbers; public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false) { $this->header = $header; $this->addLineNumbers = $addLineNumbers; } public function getDiff(array $diff): string { $buffer = fopen('php://memory', 'r+b'); if ('' !== $this->header) { fwrite($buffer, $this->header); if (!str_ends_with($this->header, "\n")) { fwrite($buffer, "\n"); } } if (0 !== count($diff)) { $this->writeDiffHunks($buffer, $diff); } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); // If the diff is non-empty and last char is not a linebreak: add it. // This might happen when both the `from` and `to` do not have a trailing linebreak $last = substr($diff, -1); return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } private function writeDiffHunks($output, array $diff): void { // detect "No newline at end of file" and insert into `$diff` if needed $upperLimit = count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { // search back for the last `+` and `-` line, // check if it has trailing linebreak, else add a warning under it $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; $i--) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = substr($diff[$i][0], -1); if ("\n" !== $lc) { array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!count($toFind)) { break; } } } } // write hunks to output buffer $cutOff = max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; $i = 0; /** @var int $i */ foreach ($diff as $i => $entry) { if (0 === $entry[1]) { // same if (false === $hunkCapture) { $fromStart++; $toStart++; continue; } $sameCount++; $toRange++; $fromRange++; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines; // note: $contextEndOffset = $this->contextLines; // // because we never go beyond the end of the diff. // with the cutoff/contextlines here the follow is never true; // // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { // $contextEndOffset = count($diff) - 1; // } // // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { $toRange++; } if (Differ::REMOVED === $entry[1]) { $fromRange++; } } if (false === $hunkCapture) { return; } // we end here when cutoff (commonLineThreshold) was not reached, but we were capturing a hunk, // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; // prevent trying to write out more common lines than there are in the diff _and_ // do not write more than configured through the context lines $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { if ($this->addLineNumbers) { fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { fwrite($output, ',' . $fromRange); } fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { fwrite($output, ',' . $toRange); } fwrite($output, " @@\n"); } else { fwrite($output, "@@ @@\n"); } for ($i = $diffStartIndex; $i < $diffEndIndex; $i++) { if ($diff[$i][1] === Differ::ADDED) { fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { fwrite($output, "\n"); // $diff[$i][0] } else { /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ fwrite($output, ' ' . $diff[$i][0]); } } } } src/Output/AbstractChunkOutputBuilder.php 0000644 00000003002 15111346261 0014623 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function count; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface { /** * Takes input of the diff array and returns the common parts. * Iterates through diff line by line. */ protected function getCommonChunks(array $diff, int $lineThreshold = 5): array { $diffSize = count($diff); $capturing = false; $chunkStart = 0; $chunkSize = 0; $commonChunks = []; for ($i = 0; $i < $diffSize; $i++) { if ($diff[$i][1] === 0 /* OLD */) { if ($capturing === false) { $capturing = true; $chunkStart = $i; $chunkSize = 0; } else { $chunkSize++; } } elseif ($capturing !== false) { if ($chunkSize >= $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } $capturing = false; } } if ($capturing !== false && $chunkSize >= $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } return $commonChunks; } } src/TimeEfficientLongestCommonSubsequenceCalculator.php 0000644 00000004500 15111346261 0017500 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_reverse; use function count; use function max; use SplFixedArray; final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * @inheritDoc */ public function calculate(array $from, array $to): array { $common = []; $fromLength = count($from); $toLength = count($to); $width = $fromLength + 1; $matrix = new SplFixedArray($width * ($toLength + 1)); for ($i = 0; $i <= $fromLength; $i++) { $matrix[$i] = 0; } for ($j = 0; $j <= $toLength; $j++) { $matrix[$j * $width] = 0; } for ($i = 1; $i <= $fromLength; $i++) { for ($j = 1; $j <= $toLength; $j++) { $o = ($j * $width) + $i; // don't use max() to avoid function call overhead $firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0; if ($matrix[$o - 1] > $matrix[$o - $width]) { if ($firstOrLast > $matrix[$o - 1]) { $matrix[$o] = $firstOrLast; } else { $matrix[$o] = $matrix[$o - 1]; } } else { if ($firstOrLast > $matrix[$o - $width]) { $matrix[$o] = $firstOrLast; } else { $matrix[$o] = $matrix[$o - $width]; } } } } $i = $fromLength; $j = $toLength; while ($i > 0 && $j > 0) { if ($from[$i - 1] === $to[$j - 1]) { $common[] = $from[$i - 1]; $i--; $j--; } else { $o = ($j * $width) + $i; if ($matrix[$o - $width] > $matrix[$o - 1]) { $j--; } else { $i--; } } } return array_reverse($common); } } src/Chunk.php 0000644 00000002757 15111346261 0007127 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Chunk { private int $start; private int $startRange; private int $end; private int $endRange; private array $lines; public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) { $this->start = $start; $this->startRange = $startRange; $this->end = $end; $this->endRange = $endRange; $this->lines = $lines; } public function getStart(): int { return $this->start; } public function getStartRange(): int { return $this->startRange; } public function getEnd(): int { return $this->end; } public function getEndRange(): int { return $this->endRange; } /** * @psalm-return list<Line> */ public function getLines(): array { return $this->lines; } /** * @psalm-param list<Line> $lines */ public function setLines(array $lines): void { foreach ($lines as $line) { if (!$line instanceof Line) { throw new InvalidArgumentException; } } $this->lines = $lines; } } src/Exception/Exception.php 0000644 00000000541 15111346261 0011740 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use Throwable; interface Exception extends Throwable { } src/Exception/ConfigurationException.php 0000644 00000001676 15111346261 0014502 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function gettype; use function is_object; use function sprintf; use Exception; final class ConfigurationException extends InvalidArgumentException { public function __construct( string $option, string $expected, $value, int $code = 0, Exception $previous = null ) { parent::__construct( sprintf( 'Option "%s" must be %s, got "%s".', $option, $expected, is_object($value) ? $value::class : (null === $value ? '<null>' : gettype($value) . '#' . $value) ), $code, $previous ); } } src/Exception/InvalidArgumentException.php 0000644 00000000601 15111346261 0014747 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; class InvalidArgumentException extends \InvalidArgumentException implements Exception { } src/Parser.php 0000644 00000005761 15111346261 0007311 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_pop; use function count; use function max; use function preg_match; use function preg_split; /** * Unified diff parser. */ final class Parser { /** * @return Diff[] */ public function parse(string $string): array { $lines = preg_split('(\r\n|\r|\n)', $string); if (!empty($lines) && $lines[count($lines) - 1] === '') { array_pop($lines); } $lineCount = count($lines); $diffs = []; $diff = null; $collected = []; for ($i = 0; $i < $lineCount; $i++) { if (preg_match('#^---\h+"?(?P<file>[^\\v\\t"]+)#', $lines[$i], $fromMatch) && preg_match('#^\\+\\+\\+\\h+"?(?P<file>[^\\v\\t"]+)#', $lines[$i + 1], $toMatch)) { if ($diff !== null) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; $collected = []; } $diff = new Diff($fromMatch['file'], $toMatch['file']); $i++; } else { if (preg_match('/^(?:diff --git |index [\da-f.]+|[+-]{3} [ab])/', $lines[$i])) { continue; } $collected[] = $lines[$i]; } } if ($diff !== null && count($collected)) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; } return $diffs; } private function parseFileDiff(Diff $diff, array $lines): void { $chunks = []; $chunk = null; $diffLines = []; foreach ($lines as $line) { if (preg_match('/^@@\s+-(?P<start>\d+)(?:,\s*(?P<startrange>\d+))?\s+\+(?P<end>\d+)(?:,\s*(?P<endrange>\d+))?\s+@@/', $line, $match, PREG_UNMATCHED_AS_NULL)) { $chunk = new Chunk( (int) $match['start'], isset($match['startrange']) ? max(0, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? max(0, (int) $match['endrange']) : 1 ); $chunks[] = $chunk; $diffLines = []; continue; } if (preg_match('/^(?P<type>[+ -])?(?P<line>.*)/', $line, $match)) { $type = Line::UNCHANGED; if ($match['type'] === '+') { $type = Line::ADDED; } elseif ($match['type'] === '-') { $type = Line::REMOVED; } $diffLines[] = new Line($type, $match['line']); $chunk?->setLines($diffLines); } } $diff->setChunks($chunks); } } src/error_log 0000644 00000017471 15111346261 0007262 0 ustar 00 [18-Nov-2025 17:52:40 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [18-Nov-2025 18:37:29 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [19-Nov-2025 01:57:24 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [19-Nov-2025 02:34:41 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [20-Nov-2025 07:27:37 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 07:37:58 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [20-Nov-2025 08:24:00 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [20-Nov-2025 08:27:08 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 09:00:55 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 10:09:23 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 10:29:52 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 12:22:47 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [20-Nov-2025 12:23:48 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 13:39:34 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 13:47:53 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [20-Nov-2025 14:19:58 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 [20-Nov-2025 14:23:09 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [25-Nov-2025 02:56:39 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php on line 20 [25-Nov-2025 03:32:23 UTC] PHP Fatal error: Uncaught Error: Interface "SebastianBergmann\Diff\LongestCommonSubsequenceCalculator" not found in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php on line 17 src/Line.php 0000644 00000001424 15111346261 0006734 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Line { public const ADDED = 1; public const REMOVED = 2; public const UNCHANGED = 3; private int $type; private string $content; public function __construct(int $type = self::UNCHANGED, string $content = '') { $this->type = $type; $this->content = $content; } public function getContent(): string { return $this->content; } public function getType(): int { return $this->type; } } src/MemoryEfficientLongestCommonSubsequenceCalculator.php 0000644 00000004707 15111346261 0020063 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of sebastian/diff. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_fill; use function array_merge; use function array_reverse; use function array_slice; use function count; use function in_array; use function max; final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * @inheritDoc */ public function calculate(array $from, array $to): array { $cFrom = count($from); $cTo = count($to); if ($cFrom === 0) { return []; } if ($cFrom === 1) { if (in_array($from[0], $to, true)) { return [$from[0]]; } return []; } $i = (int) ($cFrom / 2); $fromStart = array_slice($from, 0, $i); $fromEnd = array_slice($from, $i); $llB = $this->length($fromStart, $to); $llE = $this->length(array_reverse($fromEnd), array_reverse($to)); $jMax = 0; $max = 0; for ($j = 0; $j <= $cTo; $j++) { $m = $llB[$j] + $llE[$cTo - $j]; if ($m >= $max) { $max = $m; $jMax = $j; } } $toStart = array_slice($to, 0, $jMax); $toEnd = array_slice($to, $jMax); return array_merge( $this->calculate($fromStart, $toStart), $this->calculate($fromEnd, $toEnd) ); } private function length(array $from, array $to): array { $current = array_fill(0, count($to) + 1, 0); $cFrom = count($from); $cTo = count($to); for ($i = 0; $i < $cFrom; $i++) { $prev = $current; for ($j = 0; $j < $cTo; $j++) { if ($from[$i] === $to[$j]) { $current[$j + 1] = $prev[$j] + 1; } else { // don't use max() to avoid function call overhead if ($current[$j] > $prev[$j + 1]) { $current[$j + 1] = $current[$j]; } else { $current[$j + 1] = $prev[$j + 1]; } } } } return $current; } } README.md 0000644 00000020170 15111346261 0006023 0 ustar 00 [](https://packagist.org/packages/sebastian/diff) [](https://github.com/sebastianbergmann/diff/actions) [](https://shepherd.dev/github/sebastianbergmann/diff) [](https://codecov.io/gh/sebastianbergmann/diff) # sebastian/diff Diff implementation for PHP, factored out of PHPUnit into a stand-alone component. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/diff ``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: ``` composer require --dev sebastian/diff ``` ### Usage #### Generating diff The `Differ` class can be used to generate a textual representation of the difference between two strings: ```php <?php use SebastianBergmann\Diff\Differ; $differ = new Differ; print $differ->diff('foo', 'bar'); ``` The code above yields the output below: ```diff --- Original +++ New @@ @@ -foo +bar ``` There are three output builders available in this package: #### UnifiedDiffOutputBuilder This is default builder, which generates the output close to udiff and is used by PHPUnit. ```php <?php use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; $builder = new UnifiedDiffOutputBuilder( "--- Original\n+++ New\n", // custom header false // do not add line numbers to the diff ); $differ = new Differ($builder); print $differ->diff('foo', 'bar'); ``` #### StrictUnifiedDiffOutputBuilder Generates (strict) Unified diff's (unidiffs) with hunks, similar to `diff -u` and compatible with `patch` and `git apply`. ```php <?php use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder; $builder = new StrictUnifiedDiffOutputBuilder([ 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 'fromFile' => '', 'fromFileDate' => null, 'toFile' => '', 'toFileDate' => null, ]); $differ = new Differ($builder); print $differ->diff('foo', 'bar'); ``` #### DiffOnlyOutputBuilder Output only the lines that differ. ```php <?php use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\DiffOnlyOutputBuilder; $builder = new DiffOnlyOutputBuilder( "--- Original\n+++ New\n" ); $differ = new Differ($builder); print $differ->diff('foo', 'bar'); ``` #### DiffOutputBuilderInterface You can pass any output builder to the `Differ` class as longs as it implements the `DiffOutputBuilderInterface`. #### Parsing diff The `Parser` class can be used to parse a unified diff into an object graph: ```php use SebastianBergmann\Diff\Parser; use SebastianBergmann\Git; $git = new Git('/usr/local/src/money'); $diff = $git->getDiff( '948a1a07768d8edd10dcefa8315c1cbeffb31833', 'c07a373d2399f3e686234c4f7f088d635eb9641b' ); $parser = new Parser; print_r($parser->parse($diff)); ``` The code above yields the output below: Array ( [0] => SebastianBergmann\Diff\Diff Object ( [from:SebastianBergmann\Diff\Diff:private] => a/tests/MoneyTest.php [to:SebastianBergmann\Diff\Diff:private] => b/tests/MoneyTest.php [chunks:SebastianBergmann\Diff\Diff:private] => Array ( [0] => SebastianBergmann\Diff\Chunk Object ( [start:SebastianBergmann\Diff\Chunk:private] => 87 [startRange:SebastianBergmann\Diff\Chunk:private] => 7 [end:SebastianBergmann\Diff\Chunk:private] => 87 [endRange:SebastianBergmann\Diff\Chunk:private] => 7 [lines:SebastianBergmann\Diff\Chunk:private] => Array ( [0] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::add ) [1] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::newMoney ) [2] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => */ ) [3] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 2 [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyWithSameCurrencyObjectCanBeAdded() ) [4] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 1 [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyObjectWithSameCurrencyCanBeAdded() ) [5] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => { ) [6] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => $a = new Money(1, new Currency('EUR')); ) [7] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => $b = new Money(2, new Currency('EUR')); ) ) ) ) ) ) Note: If the chunk size is 0 lines, i.e., `getStartRange()` or `getEndRange()` return 0, the number of line returned by `getStart()` or `getEnd()` is one lower than one would expect. It is the line number after which the chunk should be inserted or deleted; in all other cases, it gives the first line number of the replaced range of lines. LICENSE 0000644 00000002773 15111346261 0005562 0 ustar 00 BSD 3-Clause License Copyright (c) 2002-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ChangeLog.md 0000644 00000006705 15111346261 0006725 0 ustar 00 # ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [5.0.3] - 2023-05-01 ### Changed * [#119](https://github.com/sebastianbergmann/diff/pull/119): Improve performance of `TimeEfficientLongestCommonSubsequenceCalculator` ## [5.0.2] - 2023-05-01 ### Changed * [#118](https://github.com/sebastianbergmann/diff/pull/118): Improve performance of `MemoryEfficientLongestCommonSubsequenceCalculator` ## [5.0.1] - 2023-03-23 ### Fixed * [#115](https://github.com/sebastianbergmann/diff/pull/115): `Parser::parseFileDiff()` does not handle diffs correctly that only add lines or only remove lines ## [5.0.0] - 2023-02-03 ### Changed * Passing a `DiffOutputBuilderInterface` instance to `Differ::__construct()` is no longer optional ### Removed * Removed support for PHP 7.3, PHP 7.4, and PHP 8.0 ## [4.0.4] - 2020-10-26 ### Fixed * `SebastianBergmann\Diff\Exception` now correctly extends `\Throwable` ## [4.0.3] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [4.0.2] - 2020-06-30 ### Added * This component is now supported on PHP 8 ## [4.0.1] - 2020-05-08 ### Fixed * [#99](https://github.com/sebastianbergmann/diff/pull/99): Regression in unified diff output of identical strings ## [4.0.0] - 2020-02-07 ### Removed * Removed support for PHP 7.1 and PHP 7.2 ## [3.0.2] - 2019-02-04 ### Changed * `Chunk::setLines()` now ensures that the `$lines` array only contains `Line` objects ## [3.0.1] - 2018-06-10 ### Fixed * Removed `"minimum-stability": "dev",` from `composer.json` ## [3.0.0] - 2018-02-01 * The `StrictUnifiedDiffOutputBuilder` implementation of the `DiffOutputBuilderInterface` was added ### Changed * The default `DiffOutputBuilderInterface` implementation now generates context lines (unchanged lines) ### Removed * Removed support for PHP 7.0 ### Fixed * [#70](https://github.com/sebastianbergmann/diff/issues/70): Diffing of arrays no longer works ## [2.0.1] - 2017-08-03 ### Fixed * [#66](https://github.com/sebastianbergmann/diff/pull/66): Restored backwards compatibility for PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 ## [2.0.0] - 2017-07-11 [YANKED] ### Added * [#64](https://github.com/sebastianbergmann/diff/pull/64): Show line numbers for chunks of a diff ### Removed * This component is no longer supported on PHP 5.6 [5.0.3]: https://github.com/sebastianbergmann/diff/compare/5.0.2...5.0.3 [5.0.2]: https://github.com/sebastianbergmann/diff/compare/5.0.1...5.0.2 [5.0.1]: https://github.com/sebastianbergmann/diff/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/sebastianbergmann/diff/compare/4.0.4...5.0.0 [4.0.4]: https://github.com/sebastianbergmann/diff/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/diff/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/diff/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/diff/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/diff/compare/3.0.2...4.0.0 [3.0.2]: https://github.com/sebastianbergmann/diff/compare/3.0.1...3.0.2 [3.0.1]: https://github.com/sebastianbergmann/diff/compare/3.0.0...3.0.1 [3.0.0]: https://github.com/sebastianbergmann/diff/compare/2.0...3.0.0 [2.0.1]: https://github.com/sebastianbergmann/diff/compare/c341c98ce083db77f896a0aa64f5ee7652915970...2.0.1 [2.0.0]: https://github.com/sebastianbergmann/diff/compare/1.4...c341c98ce083db77f896a0aa64f5ee7652915970 composer.json 0000644 00000002276 15111346261 0007275 0 ustar 00 { "name": "sebastian/diff", "description": "Diff implementation", "keywords": ["diff", "udiff", "unidiff", "unified diff"], "homepage": "https://github.com/sebastianbergmann/diff", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" } ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy" }, "prefer-stable": true, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0", "symfony/process": "^4.2 || ^5" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/" ] }, "extra": { "branch-alias": { "dev-main": "5.0-dev" } } } SECURITY.md 0000644 00000003565 15111346261 0006346 0 ustar 00 # Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes.