Added Laravel project

This commit is contained in:
2017-09-17 00:35:10 +02:00
parent a3c19304d5
commit ecf605b8f5
6246 changed files with 682270 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
/.idea
/composer.lock
/vendor
/.php_cs.cache
/from.txt.orig

81
Laravel/vendor/sebastian/diff/.php_cs vendored Normal file
View File

@@ -0,0 +1,81 @@
<?php declare(strict_types=1);
$header = <<<'EOF'
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.
EOF;
return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules(
[
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => [
'align_double_arrow' => true,
'align_equals' => true
],
'blank_line_after_namespace' => true,
'blank_line_before_return' => true,
'braces' => true,
'cast_spaces' => true,
'concat_space' => ['spacing' => 'one'],
'declare_strict_types' => true,
'elseif' => true,
'encoding' => true,
'full_opening_tag' => true,
'function_declaration' => true,
'header_comment' => ['header' => $header, 'separate' => 'none'],
'indentation_type' => true,
'line_ending' => true,
'lowercase_constants' => true,
'lowercase_keywords' => true,
'method_argument_space' => true,
'native_function_invocation' => true,
'no_alias_functions' => true,
'no_blank_lines_after_class_opening' => true,
'no_blank_lines_after_phpdoc' => true,
'no_closing_tag' => true,
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'no_extra_consecutive_blank_lines' => true,
'no_leading_namespace_whitespace' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_spaces_after_function_name' => true,
'no_spaces_inside_parenthesis' => true,
'no_trailing_comma_in_list_call' => true,
'no_trailing_whitespace' => true,
'no_unused_imports' => true,
'no_whitespace_in_blank_line' => true,
'phpdoc_align' => true,
'phpdoc_indent' => true,
'phpdoc_no_access' => true,
'phpdoc_no_empty_return' => true,
'phpdoc_no_package' => true,
'phpdoc_scalar' => true,
'phpdoc_separation' => true,
'phpdoc_to_comment' => true,
'phpdoc_trim' => true,
'phpdoc_types' => true,
'phpdoc_var_without_name' => true,
'pow_to_exponentiation' => true,
'self_accessor' => true,
'simplified_null_return' => true,
'single_blank_line_at_eof' => true,
'single_import_per_statement' => true,
'single_line_after_imports' => true,
'single_quote' => true,
'ternary_operator_spaces' => true,
'trim_array_spaces' => true,
'visibility_required' => true,
]
)
->setFinder(
PhpCsFixer\Finder::create()
->files()
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests')
->name('*.php')
);

View File

@@ -0,0 +1,27 @@
language: php
php:
- 7.0
- 7.0snapshot
- 7.1
- 7.1snapshot
- master
sudo: false
before_install:
- composer self-update
- composer clear-cache
install:
- travis_retry composer update --no-interaction --no-ansi --no-progress --no-suggest --optimize-autoloader --prefer-stable
script:
- ./vendor/bin/phpunit --coverage-clover=coverage.xml
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
email: false

View File

@@ -0,0 +1,22 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [2.0.1] - 2017-08-03
### Fixed
* 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
* Implemented [#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
[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

33
Laravel/vendor/sebastian/diff/LICENSE vendored Normal file
View File

@@ -0,0 +1,33 @@
sebastian/diff
Copyright (c) 2002-2017, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Sebastian Bergmann nor the names of his
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 OWNER 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.

126
Laravel/vendor/sebastian/diff/README.md vendored Normal file
View File

@@ -0,0 +1,126 @@
# 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
The `Differ` class can be used to generate a textual representation of the difference between two strings:
```php
use SebastianBergmann\Diff\Differ;
$differ = new Differ;
print $differ->diff('foo', 'bar');
```
The code above yields the output below:
--- Original
+++ New
@@ @@
-foo
+bar
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'));
)
)
)
)
)
)

22
Laravel/vendor/sebastian/diff/build.xml vendored Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="diff" default="setup">
<target name="setup" depends="clean,composer"/>
<target name="clean" description="Cleanup build artifacts">
<delete dir="${basedir}/vendor"/>
<delete file="${basedir}/composer.lock"/>
</target>
<target name="composer" depends="clean" description="Install dependencies with Composer">
<exec executable="composer" taskname="composer">
<arg value="update"/>
<arg value="--no-interaction"/>
<arg value="--no-progress"/>
<arg value="--no-ansi"/>
<arg value="--no-suggest"/>
<arg value="--optimize-autoloader"/>
<arg value="--prefer-stable"/>
</exec>
</target>
</project>

View File

@@ -0,0 +1,33 @@
{
"name": "sebastian/diff",
"description": "Diff implementation",
"keywords": ["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"
}
],
"require": {
"php": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.2"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.2/phpunit.xsd"
bootstrap="vendor/autoload.php"
forceCoversAnnotation="true"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
verbose="true">
<testsuite>
<directory suffix="Test.php">tests</directory>
</testsuite>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,78 @@
<?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
{
/**
* @var int
*/
private $start;
/**
* @var int
*/
private $startRange;
/**
* @var int
*/
private $end;
/**
* @var int
*/
private $endRange;
/**
* @var array
*/
private $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;
}
public function getLines(): array
{
return $this->lines;
}
public function setLines(array $lines)
{
$this->lines = $lines;
}
}

View File

@@ -0,0 +1,67 @@
<?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
{
/**
* @var string
*/
private $from;
/**
* @var string
*/
private $to;
/**
* @var Chunk[]
*/
private $chunks;
/**
* @param string $from
* @param string $to
* @param 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;
}
/**
* @return Chunk[]
*/
public function getChunks(): array
{
return $this->chunks;
}
/**
* @param Chunk[] $chunks
*/
public function setChunks(array $chunks)
{
$this->chunks = $chunks;
}
}

View File

@@ -0,0 +1,321 @@
<?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 SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
/**
* Diff implementation.
*/
final class Differ
{
/**
* @var DiffOutputBuilderInterface
*/
private $outputBuilder;
/**
* @param DiffOutputBuilderInterface $outputBuilder
*
* @throws InvalidArgumentException
*/
public function __construct($outputBuilder = null)
{
if ($outputBuilder instanceof DiffOutputBuilderInterface) {
$this->outputBuilder = $outputBuilder;
} elseif (null === $outputBuilder) {
$this->outputBuilder = new UnifiedDiffOutputBuilder;
} elseif (\is_string($outputBuilder)) {
// PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support
// @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056
// @deprecated
$this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder);
} else {
throw new InvalidArgumentException(
\sprintf(
'Expected builder to be an instance of DiffOutputBuilderInterface, <null> or a string, got %s.',
\is_object($outputBuilder) ? 'instance of "' . \get_class($outputBuilder) . '"' : \gettype($outputBuilder) . ' "' . $outputBuilder . '"'
)
);
}
}
/**
* Returns the diff between two arrays or strings as string.
*
* @param array|string $from
* @param array|string $to
* @param LongestCommonSubsequenceCalculator|null $lcs
*
* @return string
*/
public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null): string
{
$from = $this->validateDiffInput($from);
$to = $this->validateDiffInput($to);
$diff = $this->diffToArray($from, $to, $lcs);
return $this->outputBuilder->getDiff($diff);
}
/**
* Casts variable to string if it is not a string or array.
*
* @param mixed $input
*
* @return string
*/
private function validateDiffInput($input): string
{
if (!\is_array($input) && !\is_string($input)) {
return (string) $input;
}
return $input;
}
/**
* Returns the diff between two arrays or strings as array.
*
* Each array element contains two elements:
* - [0] => mixed $token
* - [1] => 2|1|0
*
* - 2: REMOVED: $token was removed from $from
* - 1: ADDED: $token was added to $from
* - 0: OLD: $token is not changed in $to
*
* @param array|string $from
* @param array|string $to
* @param LongestCommonSubsequenceCalculator $lcs
*
* @return array
*/
public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null): array
{
if (\is_string($from)) {
$from = $this->splitStringByLines($from);
} elseif (!\is_array($from)) {
throw new \InvalidArgumentException('"from" must be an array or string.');
}
if (\is_string($to)) {
$to = $this->splitStringByLines($to);
} elseif (!\is_array($to)) {
throw new \InvalidArgumentException('"to" must be an array or string.');
}
list($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, 0 /* OLD */];
}
\reset($from);
\reset($to);
foreach ($common as $token) {
while (($fromToken = \reset($from)) !== $token) {
$diff[] = [\array_shift($from), 2 /* REMOVED */];
}
while (($toToken = \reset($to)) !== $token) {
$diff[] = [\array_shift($to), 1 /* ADDED */];
}
$diff[] = [$token, 0 /* OLD */];
\array_shift($from);
\array_shift($to);
}
while (($token = \array_shift($from)) !== null) {
$diff[] = [$token, 2 /* REMOVED */];
}
while (($token = \array_shift($to)) !== null) {
$diff[] = [$token, 1 /* ADDED */];
}
foreach ($end as $token) {
$diff[] = [$token, 0 /* OLD */];
}
if ($this->detectUnmatchedLineEndings($diff)) {
\array_unshift($diff, ["#Warning: Strings contain different line endings!\n", 3]);
}
return $diff;
}
/**
* Checks if input is string, if so it will split it line-by-line.
*
* @param string $input
*
* @return array
*/
private function splitStringByLines(string $input): array
{
return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
}
/**
* @param array $from
* @param array $to
*
* @return LongestCommonSubsequenceCalculator
*/
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;
}
/**
* Calculates the estimated memory footprint for the DP-based method.
*
* @param array $from
* @param array $to
*
* @return int|float
*/
private function calculateEstimatedFootprint(array $from, array $to)
{
$itemSize = PHP_INT_SIZE === 4 ? 76 : 144;
return $itemSize * \min(\count($from), \count($to)) ** 2;
}
/**
* Returns true if line ends don't match in a diff.
*
* @param array $diff
*
* @return bool
*/
private function detectUnmatchedLineEndings(array $diff): bool
{
$newLineBreaks = ['' => true];
$oldLineBreaks = ['' => true];
foreach ($diff as $entry) {
if (0 === $entry[1]) { /* OLD */
$ln = $this->getLinebreak($entry[0]);
$oldLineBreaks[$ln] = true;
$newLineBreaks[$ln] = true;
} elseif (1 === $entry[1]) { /* ADDED */
$newLineBreaks[$this->getLinebreak($entry[0])] = true;
} elseif (2 === $entry[1]) { /* REMOVED */
$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 ("\r\n" === \substr($line, -2)) {
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];
}
}

View File

@@ -0,0 +1,15 @@
<?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 Exception
{
}

View File

@@ -0,0 +1,15 @@
<?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
{
}

View File

@@ -0,0 +1,44 @@
<?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
{
const ADDED = 1;
const REMOVED = 2;
const UNCHANGED = 3;
/**
* @var int
*/
private $type;
/**
* @var string
*/
private $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;
}
}

View File

@@ -0,0 +1,24 @@
<?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.
*
* @param array $from
* @param array $to
*
* @return array
*/
public function calculate(array $from, array $to): array;
}

View File

@@ -0,0 +1,81 @@
<?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 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 {
$current[$j + 1] = \max($current[$j], $prev[$j + 1]);
}
}
}
return $current;
}
}

View File

@@ -0,0 +1,56 @@
<?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;
abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface
{
/**
* Takes input of the diff array and returns the common parts.
* Iterates through diff line by line.
*
* @param array $diff
* @param int $lineThreshold
*
* @return array
*/
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;
}
}

View File

@@ -0,0 +1,63 @@
<?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;
/**
* 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
{
/**
* @var string
*/
private $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 ("\n" !== \substr($this->header, -1, 1)) {
\fwrite($buffer, "\n");
}
}
foreach ($diff as $diffEntry) {
if ($diffEntry[1] === 1 /* ADDED */) {
\fwrite($buffer, '+' . $diffEntry[0]);
} elseif ($diffEntry[1] === 2 /* REMOVED */) {
\fwrite($buffer, '-' . $diffEntry[0]);
} elseif ($diffEntry[1] === 3 /* 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 non changs 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;
}
}

View File

@@ -0,0 +1,19 @@
<?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;
}

View File

@@ -0,0 +1,165 @@
<?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;
/**
* Builds a diff string representation in unified diff format in chunks.
*/
final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder
{
/**
* @var string
*/
private $header;
/**
* @var bool
*/
private $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 ("\n" !== \substr($this->header, -1, 1)) {
\fwrite($buffer, "\n");
}
}
$this->writeDiffChunked($buffer, $diff, $this->getCommonChunks($diff));
$diff = \stream_get_contents($buffer, -1, 0);
\fclose($buffer);
return $diff;
}
// `old` is an array with key => value pairs . Each pair represents a start and end index of `diff`
// of a list of elements all containing `same` (0) entries.
private function writeDiffChunked($output, array $diff, array $old)
{
$upperLimit = \count($diff);
$start = 0;
$fromStart = 0;
$toStart = 0;
if (\count($old)) { // no common parts, list all diff entries
\reset($old);
// iterate the diff, go from chunk to chunk skipping common chunk of lines between those
do {
$commonStart = \key($old);
$commonEnd = \current($old);
if ($commonStart !== $start) {
list($fromRange, $toRange) = $this->getChunkRange($diff, $start, $commonStart);
$this->writeChunk($output, $diff, $start, $commonStart, $fromStart, $fromRange, $toStart, $toRange);
$fromStart += $fromRange;
$toStart += $toRange;
}
$start = $commonEnd + 1;
$commonLength = $commonEnd - $commonStart + 1; // calculate number of non-change lines in the common part
$fromStart += $commonLength;
$toStart += $commonLength;
} while (false !== \next($old));
\end($old); // short cut for finding possible last `change entry`
$tmp = \key($old);
\reset($old);
if ($old[$tmp] === $upperLimit - 1) {
$upperLimit = $tmp;
}
}
if ($start < $upperLimit - 1) { // check for trailing (non) diff entries
do {
--$upperLimit;
} while (isset($diff[$upperLimit][1]) && $diff[$upperLimit][1] === 0);
++$upperLimit;
list($fromRange, $toRange) = $this->getChunkRange($diff, $start, $upperLimit);
$this->writeChunk($output, $diff, $start, $upperLimit, $fromStart, $fromRange, $toStart, $toRange);
}
}
private function writeChunk(
$output,
array $diff,
int $diffStartIndex,
int $diffEndIndex,
int $fromStart,
int $fromRange,
int $toStart,
int $toRange
) {
if ($this->addLineNumbers) {
\fwrite($output, '@@ -' . (1 + $fromStart));
if ($fromRange > 1) {
\fwrite($output, ',' . $fromRange);
}
\fwrite($output, ' +' . (1 + $toStart));
if ($toRange > 1) {
\fwrite($output, ',' . $toRange);
}
\fwrite($output, " @@\n");
} else {
\fwrite($output, "@@ @@\n");
}
for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) {
if ($diff[$i][1] === 1 /* ADDED */) {
\fwrite($output, '+' . $diff[$i][0]);
} elseif ($diff[$i][1] === 2 /* REMOVED */) {
\fwrite($output, '-' . $diff[$i][0]);
} else { /* Not changed (old) 0 or Warning 3 */
\fwrite($output, ' ' . $diff[$i][0]);
}
$lc = \substr($diff[$i][0], -1);
if ($lc !== "\n" && $lc !== "\r") {
\fwrite($output, "\n"); // \No newline at end of file
}
}
}
private function getChunkRange(array $diff, int $diffStartIndex, int $diffEndIndex): array
{
$toRange = 0;
$fromRange = 0;
for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) {
if ($diff[$i][1] === 1) { // added
++$toRange;
} elseif ($diff[$i][1] === 2) { // removed
++$fromRange;
} elseif ($diff[$i][1] === 0) { // same
++$fromRange;
++$toRange;
}
}
return [$fromRange, $toRange];
}
}

View File

@@ -0,0 +1,106 @@
<?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;
/**
* Unified diff parser.
*/
final class Parser
{
/**
* @param string $string
*
* @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('(^---\\s+(?P<file>\\S+))', $lines[$i], $fromMatch) &&
\preg_match('(^\\+\\+\\+\\s+(?P<file>\\S+))', $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)
{
$chunks = [];
$chunk = null;
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)) {
$chunk = new Chunk(
(int) $match['start'],
isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1,
(int) $match['end'],
isset($match['endrange']) ? \max(1, (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']);
if (null !== $chunk) {
$chunk->setLines($diffLines);
}
}
}
$diff->setChunks($chunks);
}
}

View File

@@ -0,0 +1,66 @@
<?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 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;
$matrix[$o] = \max(
$matrix[$o - 1],
$matrix[$o - $width],
$from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0
);
}
}
$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);
}
}

View File

@@ -0,0 +1,68 @@
<?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 PHPUnit\Framework\TestCase;
/**
* @covers SebastianBergmann\Diff\Chunk
*/
final class ChunkTest extends TestCase
{
/**
* @var Chunk
*/
private $chunk;
protected function setUp()
{
$this->chunk = new Chunk;
}
public function testCanBeCreatedWithoutArguments()
{
$this->assertInstanceOf(Chunk::class, $this->chunk);
}
public function testStartCanBeRetrieved()
{
$this->assertSame(0, $this->chunk->getStart());
}
public function testStartRangeCanBeRetrieved()
{
$this->assertSame(1, $this->chunk->getStartRange());
}
public function testEndCanBeRetrieved()
{
$this->assertSame(0, $this->chunk->getEnd());
}
public function testEndRangeCanBeRetrieved()
{
$this->assertSame(1, $this->chunk->getEndRange());
}
public function testLinesCanBeRetrieved()
{
$this->assertSame([], $this->chunk->getLines());
}
public function testLinesCanBeSet()
{
$this->assertSame([], $this->chunk->getLines());
$testValue = ['line0', 'line1'];
$this->chunk->setLines($testValue);
$this->assertSame($testValue, $this->chunk->getLines());
}
}

View File

@@ -0,0 +1,55 @@
<?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 PHPUnit\Framework\TestCase;
/**
* @covers SebastianBergmann\Diff\Diff
*
* @uses SebastianBergmann\Diff\Chunk
*/
final class DiffTest extends TestCase
{
public function testGettersAfterConstructionWithDefault()
{
$from = 'line1a';
$to = 'line2a';
$diff = new Diff($from, $to);
$this->assertSame($from, $diff->getFrom());
$this->assertSame($to, $diff->getTo());
$this->assertSame([], $diff->getChunks(), 'Expect chunks to be default value "array()".');
}
public function testGettersAfterConstructionWithChunks()
{
$from = 'line1b';
$to = 'line2b';
$chunks = [new Chunk(), new Chunk(2, 3)];
$diff = new Diff($from, $to, $chunks);
$this->assertSame($from, $diff->getFrom());
$this->assertSame($to, $diff->getTo());
$this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.');
}
public function testSetChunksAfterConstruction()
{
$diff = new Diff('line1c', 'line2c');
$this->assertSame([], $diff->getChunks(), 'Expect chunks to be default value "array()".');
$chunks = [new Chunk(), new Chunk(2, 3)];
$diff->setChunks($chunks);
$this->assertSame($chunks, $diff->getChunks(), 'Expect chunks to be passed value.');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
<?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 PHPUnit\Framework\TestCase;
/**
* @requires OS Linux
*/
final class DifferTestTest extends TestCase
{
private $fileFrom;
private $filePatch;
protected function setUp()
{
$dir = \realpath(__DIR__ . '/../') . '/';
$this->fileFrom = $dir . 'from.txt';
$this->filePatch = $dir . 'patch.txt';
}
/**
* @dataProvider provideDiffWithLineNumbers
*/
public function testTheTestProvideDiffWithLineNumbers($expected, $from, $to)
{
$this->runThisTest($expected, $from, $to);
}
public function provideDiffWithLineNumbers()
{
require_once __DIR__ . '/DifferTest.php';
$test = new DifferTest();
$tests = $test->provideDiffWithLineNumbers();
$tests = \array_filter(
$tests,
function ($key) {
return !\is_string($key) || false === \strpos($key, 'non_patch_compat');
},
ARRAY_FILTER_USE_KEY
);
return $tests;
}
private function runThisTest(string $expected, string $from, string $to)
{
$expected = \str_replace('--- Original', '--- from.txt', $expected);
$expected = \str_replace('+++ New', '+++ from.txt', $expected);
@\unlink($this->fileFrom);
@\unlink($this->filePatch);
$this->assertNotFalse(\file_put_contents($this->fileFrom, $from));
$this->assertNotFalse(\file_put_contents($this->filePatch, $expected));
$command = \sprintf(
'patch -u --verbose %s < %s', // --posix
\escapeshellarg($this->fileFrom),
\escapeshellarg($this->filePatch)
);
\exec($command, $output, $d);
$this->assertSame(0, $d, \sprintf('%s | %s', $command, \implode("\n", $output)));
$patched = \file_get_contents($this->fileFrom);
$this->assertSame($patched, $to);
@\unlink($this->fileFrom . '.orig');
@\unlink($this->fileFrom);
@\unlink($this->filePatch);
}
}

View File

@@ -0,0 +1,44 @@
<?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 PHPUnit\Framework\TestCase;
/**
* @covers SebastianBergmann\Diff\Line
*/
final class LineTest extends TestCase
{
/**
* @var Line
*/
private $line;
protected function setUp()
{
$this->line = new Line;
}
public function testCanBeCreatedWithoutArguments()
{
$this->assertInstanceOf(Line::class, $this->line);
}
public function testTypeCanBeRetrieved()
{
$this->assertSame(Line::UNCHANGED, $this->line->getType());
}
public function testContentCanBeRetrieved()
{
$this->assertSame('', $this->line->getContent());
}
}

View File

@@ -0,0 +1,201 @@
<?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 PHPUnit\Framework\TestCase;
/**
* @coversNothing
*/
abstract class LongestCommonSubsequenceTest extends TestCase
{
/**
* @var LongestCommonSubsequenceCalculator
*/
private $implementation;
/**
* @var string
*/
private $memoryLimit;
/**
* @var int[]
*/
private $stress_sizes = [1, 2, 3, 100, 500, 1000, 2000];
protected function setUp()
{
$this->memoryLimit = \ini_get('memory_limit');
\ini_set('memory_limit', '256M');
$this->implementation = $this->createImplementation();
}
/**
* @return LongestCommonSubsequenceCalculator
*/
abstract protected function createImplementation();
protected function tearDown()
{
\ini_set('memory_limit', $this->memoryLimit);
}
public function testBothEmpty()
{
$from = [];
$to = [];
$common = $this->implementation->calculate($from, $to);
$this->assertSame([], $common);
}
public function testIsStrictComparison()
{
$from = [
false, 0, 0.0, '', null, [],
true, 1, 1.0, 'foo', ['foo', 'bar'], ['foo' => 'bar']
];
$to = $from;
$common = $this->implementation->calculate($from, $to);
$this->assertSame($from, $common);
$to = [
false, false, false, false, false, false,
true, true, true, true, true, true
];
$expected = [
false,
true,
];
$common = $this->implementation->calculate($from, $to);
$this->assertSame($expected, $common);
}
public function testEqualSequences()
{
foreach ($this->stress_sizes as $size) {
$range = \range(1, $size);
$from = $range;
$to = $range;
$common = $this->implementation->calculate($from, $to);
$this->assertSame($range, $common);
}
}
public function testDistinctSequences()
{
$from = ['A'];
$to = ['B'];
$common = $this->implementation->calculate($from, $to);
$this->assertSame([], $common);
$from = ['A', 'B', 'C'];
$to = ['D', 'E', 'F'];
$common = $this->implementation->calculate($from, $to);
$this->assertSame([], $common);
foreach ($this->stress_sizes as $size) {
$from = \range(1, $size);
$to = \range($size + 1, $size * 2);
$common = $this->implementation->calculate($from, $to);
$this->assertSame([], $common);
}
}
public function testCommonSubsequence()
{
$from = ['A', 'C', 'E', 'F', 'G'];
$to = ['A', 'B', 'D', 'E', 'H'];
$expected = ['A', 'E'];
$common = $this->implementation->calculate($from, $to);
$this->assertSame($expected, $common);
$from = ['A', 'C', 'E', 'F', 'G'];
$to = ['B', 'C', 'D', 'E', 'F', 'H'];
$expected = ['C', 'E', 'F'];
$common = $this->implementation->calculate($from, $to);
$this->assertSame($expected, $common);
foreach ($this->stress_sizes as $size) {
$from = $size < 2 ? [1] : \range(1, $size + 1, 2);
$to = $size < 3 ? [1] : \range(1, $size + 1, 3);
$expected = $size < 6 ? [1] : \range(1, $size + 1, 6);
$common = $this->implementation->calculate($from, $to);
$this->assertSame($expected, $common);
}
}
public function testSingleElementSubsequenceAtStart()
{
foreach ($this->stress_sizes as $size) {
$from = \range(1, $size);
$to = \array_slice($from, 0, 1);
$common = $this->implementation->calculate($from, $to);
$this->assertSame($to, $common);
}
}
public function testSingleElementSubsequenceAtMiddle()
{
foreach ($this->stress_sizes as $size) {
$from = \range(1, $size);
$to = \array_slice($from, (int) ($size / 2), 1);
$common = $this->implementation->calculate($from, $to);
$this->assertSame($to, $common);
}
}
public function testSingleElementSubsequenceAtEnd()
{
foreach ($this->stress_sizes as $size) {
$from = \range(1, $size);
$to = \array_slice($from, $size - 1, 1);
$common = $this->implementation->calculate($from, $to);
$this->assertSame($to, $common);
}
}
public function testReversedSequences()
{
$from = ['A', 'B'];
$to = ['B', 'A'];
$expected = ['A'];
$common = $this->implementation->calculate($from, $to);
$this->assertSame($expected, $common);
foreach ($this->stress_sizes as $size) {
$from = \range(1, $size);
$to = \array_reverse($from);
$common = $this->implementation->calculate($from, $to);
$this->assertSame([1], $common);
}
}
public function testStrictTypeCalculate()
{
$diff = $this->implementation->calculate(['5'], ['05']);
$this->assertInternalType('array', $diff);
$this->assertCount(0, $diff);
}
}

View File

@@ -0,0 +1,22 @@
<?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;
/**
* @covers SebastianBergmann\Diff\MemoryEfficientLongestCommonSubsequenceCalculator
*/
final class MemoryEfficientImplementationTest extends LongestCommonSubsequenceTest
{
protected function createImplementation()
{
return new MemoryEfficientLongestCommonSubsequenceCalculator;
}
}

View File

@@ -0,0 +1,151 @@
<?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 PHPUnit\Framework\TestCase;
/**
* @covers SebastianBergmann\Diff\Parser
*
* @uses SebastianBergmann\Diff\Chunk
* @uses SebastianBergmann\Diff\Diff
* @uses SebastianBergmann\Diff\Line
*/
final class ParserTest extends TestCase
{
/**
* @var Parser
*/
private $parser;
protected function setUp()
{
$this->parser = new Parser;
}
public function testParse()
{
$content = \file_get_contents(__DIR__ . '/fixtures/patch.txt');
$diffs = $this->parser->parse($content);
$this->assertInternalType('array', $diffs);
$this->assertContainsOnlyInstancesOf(Diff::class, $diffs);
$this->assertCount(1, $diffs);
$chunks = $diffs[0]->getChunks();
$this->assertInternalType('array', $chunks);
$this->assertContainsOnlyInstancesOf(Chunk::class, $chunks);
$this->assertCount(1, $chunks);
$this->assertSame(20, $chunks[0]->getStart());
$this->assertCount(4, $chunks[0]->getLines());
}
public function testParseWithMultipleChunks()
{
$content = \file_get_contents(__DIR__ . '/fixtures/patch2.txt');
$diffs = $this->parser->parse($content);
$this->assertCount(1, $diffs);
$chunks = $diffs[0]->getChunks();
$this->assertCount(3, $chunks);
$this->assertSame(20, $chunks[0]->getStart());
$this->assertSame(320, $chunks[1]->getStart());
$this->assertSame(600, $chunks[2]->getStart());
$this->assertCount(5, $chunks[0]->getLines());
$this->assertCount(5, $chunks[1]->getLines());
$this->assertCount(4, $chunks[2]->getLines());
}
public function testParseWithRemovedLines()
{
$content = <<<A
diff --git a/Test.txt b/Test.txt
index abcdefg..abcdefh 100644
--- a/Test.txt
+++ b/Test.txt
@@ -49,9 +49,8 @@
A
-B
A;
$diffs = $this->parser->parse($content);
$this->assertInternalType('array', $diffs);
$this->assertContainsOnlyInstancesOf(Diff::class, $diffs);
$this->assertCount(1, $diffs);
$chunks = $diffs[0]->getChunks();
$this->assertInternalType('array', $chunks);
$this->assertContainsOnlyInstancesOf(Chunk::class, $chunks);
$this->assertCount(1, $chunks);
$chunk = $chunks[0];
$this->assertSame(49, $chunk->getStart());
$this->assertSame(49, $chunk->getEnd());
$this->assertSame(9, $chunk->getStartRange());
$this->assertSame(8, $chunk->getEndRange());
$lines = $chunk->getLines();
$this->assertInternalType('array', $lines);
$this->assertContainsOnlyInstancesOf(Line::class, $lines);
$this->assertCount(2, $lines);
/** @var Line $line */
$line = $lines[0];
$this->assertSame('A', $line->getContent());
$this->assertSame(Line::UNCHANGED, $line->getType());
$line = $lines[1];
$this->assertSame('B', $line->getContent());
$this->assertSame(Line::REMOVED, $line->getType());
}
public function testParseDiffForMulitpleFiles()
{
$content = <<<A
diff --git a/Test.txt b/Test.txt
index abcdefg..abcdefh 100644
--- a/Test.txt
+++ b/Test.txt
@@ -1,3 +1,2 @@
A
-B
diff --git a/Test123.txt b/Test123.txt
index abcdefg..abcdefh 100644
--- a/Test2.txt
+++ b/Test2.txt
@@ -1,2 +1,3 @@
A
+B
A;
$diffs = $this->parser->parse($content);
$this->assertCount(2, $diffs);
/** @var Diff $diff */
$diff = $diffs[0];
$this->assertSame('a/Test.txt', $diff->getFrom());
$this->assertSame('b/Test.txt', $diff->getTo());
$this->assertCount(1, $diff->getChunks());
$diff = $diffs[1];
$this->assertSame('a/Test2.txt', $diff->getFrom());
$this->assertSame('b/Test2.txt', $diff->getTo());
$this->assertCount(1, $diff->getChunks());
}
}

View File

@@ -0,0 +1,22 @@
<?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;
/**
* @covers SebastianBergmann\Diff\TimeEfficientLongestCommonSubsequenceCalculator
*/
final class TimeEfficientImplementationTest extends LongestCommonSubsequenceTest
{
protected function createImplementation()
{
return new TimeEfficientLongestCommonSubsequenceCalculator;
}
}

View File

@@ -0,0 +1,9 @@
diff --git a/Foo.php b/Foo.php
index abcdefg..abcdefh 100644
--- a/Foo.php
+++ b/Foo.php
@@ -20,4 +20,5 @@ class Foo
const ONE = 1;
const TWO = 2;
+ const THREE = 3;
const FOUR = 4;

View File

@@ -0,0 +1,21 @@
diff --git a/Foo.php b/Foo.php
index abcdefg..abcdefh 100644
--- a/Foo.php
+++ b/Foo.php
@@ -20,4 +20,5 @@ class Foo
const ONE = 1;
const TWO = 2;
+ const THREE = 3;
const FOUR = 4;
@@ -320,4 +320,5 @@ class Foo
const A = 'A';
const B = 'B';
+ const C = 'C';
const D = 'D';
@@ -600,4 +600,5 @@ class Foo
public function doSomething() {
+ return 'foo';
}