conteneurisation de l'appli

This commit is contained in:
Rudy
2023-01-07 22:07:15 +01:00
parent b79e8502b1
commit b21f2bc04b
8725 changed files with 1271447 additions and 676 deletions

View File

@ -0,0 +1,15 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [1.0.1] - 2020-09-28
### Changed
* Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3`
## [1.0.0] - 2020-08-12
* Initial release
[1.0.0]: https://github.com/sebastianbergmann/cli-parser/compare/bb7bb3297957927962b0a3335befe7b66f7462e9...1.0.0

33
vendor/sebastian/cli-parser/LICENSE vendored Normal file
View File

@ -0,0 +1,33 @@
sebastian/cli-parser
Copyright (c) 2020, 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.

17
vendor/sebastian/cli-parser/README.md vendored Normal file
View File

@ -0,0 +1,17 @@
# sebastian/cli-parser
Library for parsing `$_SERVER['argv']`, extracted from `phpunit/phpunit`.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
```
composer require sebastian/cli-parser
```
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/cli-parser
```

View File

@ -0,0 +1,41 @@
{
"name": "sebastian/cli-parser",
"description": "Library for parsing CLI options",
"type": "library",
"homepage": "https://github.com/sebastianbergmann/cli-parser",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues"
},
"prefer-stable": true,
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"config": {
"platform": {
"php": "7.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
}
}

View File

@ -0,0 +1,12 @@
{
"source": {
"directories": [
"src"
]
},
"mutators": {
"@default": true
},
"minMsi": 100,
"minCoveredMsi": 100
}

View File

@ -0,0 +1,204 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/cli-parser.
*
* (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\CliParser;
use function array_map;
use function array_merge;
use function array_shift;
use function array_slice;
use function assert;
use function count;
use function current;
use function explode;
use function is_array;
use function is_int;
use function is_string;
use function key;
use function next;
use function preg_replace;
use function reset;
use function sort;
use function strlen;
use function strpos;
use function strstr;
use function substr;
final class Parser
{
/**
* @psalm-param list<string> $argv
* @psalm-param list<string> $longOptions
*
* @throws AmbiguousOptionException
* @throws RequiredOptionArgumentMissingException
* @throws OptionDoesNotAllowArgumentException
* @throws UnknownOptionException
*/
public function parse(array $argv, string $shortOptions, array $longOptions = null): array
{
if (empty($argv)) {
return [[], []];
}
$options = [];
$nonOptions = [];
if ($longOptions) {
sort($longOptions);
}
if (isset($argv[0][0]) && $argv[0][0] !== '-') {
array_shift($argv);
}
reset($argv);
$argv = array_map('trim', $argv);
while (false !== $arg = current($argv)) {
$i = key($argv);
assert(is_int($i));
next($argv);
if ($arg === '') {
continue;
}
if ($arg === '--') {
$nonOptions = array_merge($nonOptions, array_slice($argv, $i + 1));
break;
}
if ($arg[0] !== '-' || (strlen($arg) > 1 && $arg[1] === '-' && !$longOptions)) {
$nonOptions[] = $arg;
continue;
}
if (strlen($arg) > 1 && $arg[1] === '-' && is_array($longOptions)) {
$this->parseLongOption(
substr($arg, 2),
$longOptions,
$options,
$argv
);
} else {
$this->parseShortOption(
substr($arg, 1),
$shortOptions,
$options,
$argv
);
}
}
return [$options, $nonOptions];
}
/**
* @throws RequiredOptionArgumentMissingException
*/
private function parseShortOption(string $arg, string $shortOptions, array &$opts, array &$args): void
{
$argLength = strlen($arg);
for ($i = 0; $i < $argLength; $i++) {
$option = $arg[$i];
$optionArgument = null;
if ($arg[$i] === ':' || ($spec = strstr($shortOptions, $option)) === false) {
throw new UnknownOptionException('-' . $option);
}
assert(is_string($spec));
if (strlen($spec) > 1 && $spec[1] === ':') {
if ($i + 1 < $argLength) {
$opts[] = [$option, substr($arg, $i + 1)];
break;
}
if (!(strlen($spec) > 2 && $spec[2] === ':')) {
$optionArgument = current($args);
if (!$optionArgument) {
throw new RequiredOptionArgumentMissingException('-' . $option);
}
assert(is_string($optionArgument));
next($args);
}
}
$opts[] = [$option, $optionArgument];
}
}
/**
* @psalm-param list<string> $longOptions
*
* @throws AmbiguousOptionException
* @throws RequiredOptionArgumentMissingException
* @throws OptionDoesNotAllowArgumentException
* @throws UnknownOptionException
*/
private function parseLongOption(string $arg, array $longOptions, array &$opts, array &$args): void
{
$count = count($longOptions);
$list = explode('=', $arg);
$option = $list[0];
$optionArgument = null;
if (count($list) > 1) {
$optionArgument = $list[1];
}
$optionLength = strlen($option);
foreach ($longOptions as $i => $longOption) {
$opt_start = substr($longOption, 0, $optionLength);
if ($opt_start !== $option) {
continue;
}
$opt_rest = substr($longOption, $optionLength);
if ($opt_rest !== '' && $i + 1 < $count && $option[0] !== '=' && strpos($longOptions[$i + 1], $option) === 0) {
throw new AmbiguousOptionException('--' . $option);
}
if (substr($longOption, -1) === '=') {
/* @noinspection StrlenInEmptyStringCheckContextInspection */
if (substr($longOption, -2) !== '==' && !strlen((string) $optionArgument)) {
if (false === $optionArgument = current($args)) {
throw new RequiredOptionArgumentMissingException('--' . $option);
}
next($args);
}
} elseif ($optionArgument) {
throw new OptionDoesNotAllowArgumentException('--' . $option);
}
$fullOption = '--' . preg_replace('/={1,2}$/', '', $longOption);
$opts[] = [$fullOption, $optionArgument];
return;
}
throw new UnknownOptionException('--' . $option);
}
}

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/cli-parser.
*
* (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\CliParser;
use function sprintf;
use RuntimeException;
final class AmbiguousOptionException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Option "%s" is ambiguous',
$option
)
);
}
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/cli-parser.
*
* (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\CliParser;
use Throwable;
interface Exception extends Throwable
{
}

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/cli-parser.
*
* (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\CliParser;
use function sprintf;
use RuntimeException;
final class OptionDoesNotAllowArgumentException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Option "%s" does not allow an argument',
$option
)
);
}
}

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/cli-parser.
*
* (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\CliParser;
use function sprintf;
use RuntimeException;
final class RequiredOptionArgumentMissingException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Required argument for option "%s" is missing',
$option
)
);
}
}

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/cli-parser.
*
* (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\CliParser;
use function sprintf;
use RuntimeException;
final class UnknownOptionException extends RuntimeException implements Exception
{
public function __construct(string $option)
{
parent::__construct(
sprintf(
'Unknown option "%s"',
$option
)
);
}
}

View File

@ -0,0 +1,38 @@
# Change Log
All notable changes to `sebastianbergmann/code-unit-reverse-lookup` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [2.0.3] - 2020-09-28
### Changed
* Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3`
## [2.0.2] - 2020-06-26
### Added
* This component is now supported on PHP 8
## [2.0.1] - 2020-06-15
### Changed
* Tests etc. are now ignored for archive exports
## 2.0.0 - 2020-02-07
### Removed
* This component is no longer supported on PHP 5.6, PHP 7.0, PHP 7.1, and PHP 7.2
## 1.0.0 - 2016-02-13
### Added
* Initial release
[2.0.3]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/2.0.2...2.0.3
[2.0.2]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/2.0.1...2.0.2
[2.0.1]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/1.0.0...2.0.0

View File

@ -0,0 +1,33 @@
code-unit-reverse-lookup
Copyright (c) 2016-2020, 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.

View File

@ -0,0 +1,20 @@
# sebastian/code-unit-reverse-lookup
[![CI Status](https://github.com/sebastianbergmann/code-unit-reverse-lookup/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/code-unit-reverse-lookup/actions)
[![Type Coverage](https://shepherd.dev/github/sebastianbergmann/code-unit-reverse-lookup/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/code-unit-reverse-lookup)
Looks up which function or method a line of code belongs to.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
```
composer require sebastian/code-unit-reverse-lookup
```
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/code-unit-reverse-lookup
```

View File

@ -0,0 +1,36 @@
{
"name": "sebastian/code-unit-reverse-lookup",
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"prefer-stable": true,
"config": {
"platform": {
"php": "7.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
}
}

View File

@ -0,0 +1,125 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit-reverse-lookup.
*
* (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\CodeUnitReverseLookup;
use function array_merge;
use function assert;
use function get_declared_classes;
use function get_declared_traits;
use function get_defined_functions;
use function is_array;
use function range;
use ReflectionClass;
use ReflectionFunction;
use ReflectionFunctionAbstract;
use ReflectionMethod;
/**
* @since Class available since Release 1.0.0
*/
class Wizard
{
/**
* @var array
*/
private $lookupTable = [];
/**
* @var array
*/
private $processedClasses = [];
/**
* @var array
*/
private $processedFunctions = [];
/**
* @param string $filename
* @param int $lineNumber
*
* @return string
*/
public function lookup($filename, $lineNumber)
{
if (!isset($this->lookupTable[$filename][$lineNumber])) {
$this->updateLookupTable();
}
if (isset($this->lookupTable[$filename][$lineNumber])) {
return $this->lookupTable[$filename][$lineNumber];
}
return $filename . ':' . $lineNumber;
}
private function updateLookupTable(): void
{
$this->processClassesAndTraits();
$this->processFunctions();
}
private function processClassesAndTraits(): void
{
$classes = get_declared_classes();
$traits = get_declared_traits();
assert(is_array($classes));
assert(is_array($traits));
foreach (array_merge($classes, $traits) as $classOrTrait) {
if (isset($this->processedClasses[$classOrTrait])) {
continue;
}
$reflector = new ReflectionClass($classOrTrait);
foreach ($reflector->getMethods() as $method) {
$this->processFunctionOrMethod($method);
}
$this->processedClasses[$classOrTrait] = true;
}
}
private function processFunctions(): void
{
foreach (get_defined_functions()['user'] as $function) {
if (isset($this->processedFunctions[$function])) {
continue;
}
$this->processFunctionOrMethod(new ReflectionFunction($function));
$this->processedFunctions[$function] = true;
}
}
private function processFunctionOrMethod(ReflectionFunctionAbstract $functionOrMethod): void
{
if ($functionOrMethod->isInternal()) {
return;
}
$name = $functionOrMethod->getName();
if ($functionOrMethod instanceof ReflectionMethod) {
$name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name;
}
if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) {
$this->lookupTable[$functionOrMethod->getFileName()] = [];
}
foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) {
$this->lookupTable[$functionOrMethod->getFileName()][$line] = $name;
}
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.0.1@b1e2e30026936ef8d5bf6a354d1c3959b6231f44">
<file src="src/Mapper.php">
<ArgumentTypeCoercion occurrences="16">
<code>$firstPart</code>
<code>$firstPart</code>
<code>$firstPart</code>
<code>$firstPart</code>
<code>$firstPart</code>
<code>$firstPart</code>
<code>$firstPart</code>
<code>$firstPart</code>
<code>$firstPart</code>
<code>$secondPart</code>
<code>$unit</code>
<code>$unit</code>
<code>$unit</code>
<code>$unit</code>
<code>$unit</code>
<code>$unit</code>
</ArgumentTypeCoercion>
</file>
</files>

View File

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<psalm
totallyTyped="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
resolveFromConfigFile="false"
errorBaseline=".psalm/baseline.xml"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

65
vendor/sebastian/code-unit/ChangeLog.md vendored Normal file
View File

@ -0,0 +1,65 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [1.0.8] - 2020-10-26
### Fixed
* `SebastianBergmann\CodeUnit\Exception` now correctly extends `\Throwable`
## [1.0.7] - 2020-10-02
### Fixed
* `SebastianBergmann\CodeUnit\Mapper::stringToCodeUnits()` no longer attempts to create `CodeUnit` objects for code units that are not declared in userland
## [1.0.6] - 2020-09-28
### Changed
* Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3`
## [1.0.5] - 2020-06-26
### Fixed
* [#3](https://github.com/sebastianbergmann/code-unit/issues/3): Regression in 1.0.4
## [1.0.4] - 2020-06-26
### Added
* This component is now supported on PHP 8
## [1.0.3] - 2020-06-15
### Changed
* Tests etc. are now ignored for archive exports
## [1.0.2] - 2020-04-30
### Fixed
* `Mapper::stringToCodeUnits()` raised the wrong exception for `Class::method` when a class named `Class` exists but does not have a method named `method`
## [1.0.1] - 2020-04-27
### Fixed
* [#2](https://github.com/sebastianbergmann/code-unit/issues/2): `Mapper::stringToCodeUnits()` breaks when `ClassName<extended>` is used for class that extends built-in class
## [1.0.0] - 2020-03-30
* Initial release
[1.0.8]: https://github.com/sebastianbergmann/code-unit/compare/1.0.7...1.0.8
[1.0.7]: https://github.com/sebastianbergmann/code-unit/compare/1.0.6...1.0.7
[1.0.6]: https://github.com/sebastianbergmann/code-unit/compare/1.0.5...1.0.6
[1.0.5]: https://github.com/sebastianbergmann/code-unit/compare/1.0.4...1.0.5
[1.0.4]: https://github.com/sebastianbergmann/code-unit/compare/1.0.3...1.0.4
[1.0.3]: https://github.com/sebastianbergmann/code-unit/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/sebastianbergmann/code-unit/compare/1.0.1...1.0.2
[1.0.1]: https://github.com/sebastianbergmann/code-unit/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/sebastianbergmann/code-unit/compare/530c3900e5db9bcb8516da545bef0d62536cedaa...1.0.0

33
vendor/sebastian/code-unit/LICENSE vendored Normal file
View File

@ -0,0 +1,33 @@
sebastian/code-unit
Copyright (c) 2020, 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.

17
vendor/sebastian/code-unit/README.md vendored Normal file
View File

@ -0,0 +1,17 @@
# sebastian/code-unit
Collection of value objects that represent the PHP code units.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
```
composer require sebastian/code-unit
```
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/code-unit
```

View File

@ -0,0 +1,50 @@
{
"name": "sebastian/code-unit",
"description": "Collection of value objects that represent the PHP code units",
"type": "library",
"homepage": "https://github.com/sebastianbergmann/code-unit",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit/issues"
},
"prefer-stable": true,
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"config": {
"platform": {
"php": "7.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/_fixture"
],
"files": [
"tests/_fixture/file_with_multiple_code_units.php",
"tests/_fixture/function.php"
]
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
/**
* @psalm-immutable
*/
final class ClassMethodUnit extends CodeUnit
{
/**
* @psalm-assert-if-true ClassMethodUnit $this
*/
public function isClassMethod(): bool
{
return true;
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
/**
* @psalm-immutable
*/
final class ClassUnit extends CodeUnit
{
/**
* @psalm-assert-if-true ClassUnit $this
*/
public function isClass(): bool
{
return true;
}
}

View File

@ -0,0 +1,445 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use function range;
use function sprintf;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
/**
* @psalm-immutable
*/
abstract class CodeUnit
{
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $sourceFileName;
/**
* @var array
* @psalm-var list<int>
*/
private $sourceLines;
/**
* @psalm-param class-string $className
*
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public static function forClass(string $className): ClassUnit
{
self::ensureUserDefinedClass($className);
$reflector = self::reflectorForClass($className);
return new ClassUnit(
$className,
$reflector->getFileName(),
range(
$reflector->getStartLine(),
$reflector->getEndLine()
)
);
}
/**
* @psalm-param class-string $className
*
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public static function forClassMethod(string $className, string $methodName): ClassMethodUnit
{
self::ensureUserDefinedClass($className);
$reflector = self::reflectorForClassMethod($className, $methodName);
return new ClassMethodUnit(
$className . '::' . $methodName,
$reflector->getFileName(),
range(
$reflector->getStartLine(),
$reflector->getEndLine()
)
);
}
/**
* @psalm-param class-string $interfaceName
*
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public static function forInterface(string $interfaceName): InterfaceUnit
{
self::ensureUserDefinedInterface($interfaceName);
$reflector = self::reflectorForClass($interfaceName);
return new InterfaceUnit(
$interfaceName,
$reflector->getFileName(),
range(
$reflector->getStartLine(),
$reflector->getEndLine()
)
);
}
/**
* @psalm-param class-string $interfaceName
*
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public static function forInterfaceMethod(string $interfaceName, string $methodName): InterfaceMethodUnit
{
self::ensureUserDefinedInterface($interfaceName);
$reflector = self::reflectorForClassMethod($interfaceName, $methodName);
return new InterfaceMethodUnit(
$interfaceName . '::' . $methodName,
$reflector->getFileName(),
range(
$reflector->getStartLine(),
$reflector->getEndLine()
)
);
}
/**
* @psalm-param class-string $traitName
*
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public static function forTrait(string $traitName): TraitUnit
{
self::ensureUserDefinedTrait($traitName);
$reflector = self::reflectorForClass($traitName);
return new TraitUnit(
$traitName,
$reflector->getFileName(),
range(
$reflector->getStartLine(),
$reflector->getEndLine()
)
);
}
/**
* @psalm-param class-string $traitName
*
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public static function forTraitMethod(string $traitName, string $methodName): TraitMethodUnit
{
self::ensureUserDefinedTrait($traitName);
$reflector = self::reflectorForClassMethod($traitName, $methodName);
return new TraitMethodUnit(
$traitName . '::' . $methodName,
$reflector->getFileName(),
range(
$reflector->getStartLine(),
$reflector->getEndLine()
)
);
}
/**
* @psalm-param callable-string $functionName
*
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public static function forFunction(string $functionName): FunctionUnit
{
$reflector = self::reflectorForFunction($functionName);
if (!$reflector->isUserDefined()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is not a user-defined function',
$functionName
)
);
}
return new FunctionUnit(
$functionName,
$reflector->getFileName(),
range(
$reflector->getStartLine(),
$reflector->getEndLine()
)
);
}
/**
* @psalm-param list<int> $sourceLines
*/
private function __construct(string $name, string $sourceFileName, array $sourceLines)
{
$this->name = $name;
$this->sourceFileName = $sourceFileName;
$this->sourceLines = $sourceLines;
}
public function name(): string
{
return $this->name;
}
public function sourceFileName(): string
{
return $this->sourceFileName;
}
/**
* @psalm-return list<int>
*/
public function sourceLines(): array
{
return $this->sourceLines;
}
public function isClass(): bool
{
return false;
}
public function isClassMethod(): bool
{
return false;
}
public function isInterface(): bool
{
return false;
}
public function isInterfaceMethod(): bool
{
return false;
}
public function isTrait(): bool
{
return false;
}
public function isTraitMethod(): bool
{
return false;
}
public function isFunction(): bool
{
return false;
}
/**
* @psalm-param class-string $className
*
* @throws InvalidCodeUnitException
*/
private static function ensureUserDefinedClass(string $className): void
{
try {
$reflector = new ReflectionClass($className);
if ($reflector->isInterface()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is an interface and not a class',
$className
)
);
}
if ($reflector->isTrait()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is a trait and not a class',
$className
)
);
}
if (!$reflector->isUserDefined()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is not a user-defined class',
$className
)
);
}
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @psalm-param class-string $interfaceName
*
* @throws InvalidCodeUnitException
*/
private static function ensureUserDefinedInterface(string $interfaceName): void
{
try {
$reflector = new ReflectionClass($interfaceName);
if (!$reflector->isInterface()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is not an interface',
$interfaceName
)
);
}
if (!$reflector->isUserDefined()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is not a user-defined interface',
$interfaceName
)
);
}
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @psalm-param class-string $traitName
*
* @throws InvalidCodeUnitException
*/
private static function ensureUserDefinedTrait(string $traitName): void
{
try {
$reflector = new ReflectionClass($traitName);
if (!$reflector->isTrait()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is not a trait',
$traitName
)
);
}
// @codeCoverageIgnoreStart
if (!$reflector->isUserDefined()) {
throw new InvalidCodeUnitException(
sprintf(
'"%s" is not a user-defined trait',
$traitName
)
);
}
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private static function reflectorForClass(string $className): ReflectionClass
{
try {
return new ReflectionClass($className);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private static function reflectorForClassMethod(string $className, string $methodName): ReflectionMethod
{
try {
return new ReflectionMethod($className, $methodName);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @psalm-param callable-string $functionName
*
* @throws ReflectionException
*/
private static function reflectorForFunction(string $functionName): ReflectionFunction
{
try {
return new ReflectionFunction($functionName);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
}

View File

@ -0,0 +1,84 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use function array_merge;
use function count;
use Countable;
use IteratorAggregate;
final class CodeUnitCollection implements Countable, IteratorAggregate
{
/**
* @psalm-var list<CodeUnit>
*/
private $codeUnits = [];
/**
* @psalm-param list<CodeUnit> $items
*/
public static function fromArray(array $items): self
{
$collection = new self;
foreach ($items as $item) {
$collection->add($item);
}
return $collection;
}
public static function fromList(CodeUnit ...$items): self
{
return self::fromArray($items);
}
private function __construct()
{
}
/**
* @psalm-return list<CodeUnit>
*/
public function asArray(): array
{
return $this->codeUnits;
}
public function getIterator(): CodeUnitCollectionIterator
{
return new CodeUnitCollectionIterator($this);
}
public function count(): int
{
return count($this->codeUnits);
}
public function isEmpty(): bool
{
return empty($this->codeUnits);
}
public function mergeWith(self $other): self
{
return self::fromArray(
array_merge(
$this->asArray(),
$other->asArray()
)
);
}
private function add(CodeUnit $item): void
{
$this->codeUnits[] = $item;
}
}

View File

@ -0,0 +1,55 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use Iterator;
final class CodeUnitCollectionIterator implements Iterator
{
/**
* @psalm-var list<CodeUnit>
*/
private $codeUnits;
/**
* @var int
*/
private $position = 0;
public function __construct(CodeUnitCollection $collection)
{
$this->codeUnits = $collection->asArray();
}
public function rewind(): void
{
$this->position = 0;
}
public function valid(): bool
{
return isset($this->codeUnits[$this->position]);
}
public function key(): int
{
return $this->position;
}
public function current(): CodeUnit
{
return $this->codeUnits[$this->position];
}
public function next(): void
{
$this->position++;
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
/**
* @psalm-immutable
*/
final class FunctionUnit extends CodeUnit
{
/**
* @psalm-assert-if-true FunctionUnit $this
*/
public function isFunction(): bool
{
return true;
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
/**
* @psalm-immutable
*/
final class InterfaceMethodUnit extends CodeUnit
{
/**
* @psalm-assert-if-true InterfaceMethod $this
*/
public function isInterfaceMethod(): bool
{
return true;
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
/**
* @psalm-immutable
*/
final class InterfaceUnit extends CodeUnit
{
/**
* @psalm-assert-if-true InterfaceUnit $this
*/
public function isInterface(): bool
{
return true;
}
}

View File

@ -0,0 +1,414 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use function array_keys;
use function array_merge;
use function array_unique;
use function array_values;
use function class_exists;
use function explode;
use function function_exists;
use function interface_exists;
use function ksort;
use function method_exists;
use function sort;
use function sprintf;
use function str_replace;
use function strpos;
use function trait_exists;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
final class Mapper
{
/**
* @psalm-return array<string,list<int>>
*/
public function codeUnitsToSourceLines(CodeUnitCollection $codeUnits): array
{
$result = [];
foreach ($codeUnits as $codeUnit) {
$sourceFileName = $codeUnit->sourceFileName();
if (!isset($result[$sourceFileName])) {
$result[$sourceFileName] = [];
}
$result[$sourceFileName] = array_merge($result[$sourceFileName], $codeUnit->sourceLines());
}
foreach (array_keys($result) as $sourceFileName) {
$result[$sourceFileName] = array_values(array_unique($result[$sourceFileName]));
sort($result[$sourceFileName]);
}
ksort($result);
return $result;
}
/**
* @throws InvalidCodeUnitException
* @throws ReflectionException
*/
public function stringToCodeUnits(string $unit): CodeUnitCollection
{
if (strpos($unit, '::') !== false) {
[$firstPart, $secondPart] = explode('::', $unit);
if (empty($firstPart) && $this->isUserDefinedFunction($secondPart)) {
return CodeUnitCollection::fromList(CodeUnit::forFunction($secondPart));
}
if ($this->isUserDefinedClass($firstPart)) {
if ($secondPart === '<public>') {
return $this->publicMethodsOfClass($firstPart);
}
if ($secondPart === '<!public>') {
return $this->protectedAndPrivateMethodsOfClass($firstPart);
}
if ($secondPart === '<protected>') {
return $this->protectedMethodsOfClass($firstPart);
}
if ($secondPart === '<!protected>') {
return $this->publicAndPrivateMethodsOfClass($firstPart);
}
if ($secondPart === '<private>') {
return $this->privateMethodsOfClass($firstPart);
}
if ($secondPart === '<!private>') {
return $this->publicAndProtectedMethodsOfClass($firstPart);
}
if ($this->isUserDefinedMethod($firstPart, $secondPart)) {
return CodeUnitCollection::fromList(CodeUnit::forClassMethod($firstPart, $secondPart));
}
}
if ($this->isUserDefinedInterface($firstPart)) {
return CodeUnitCollection::fromList(CodeUnit::forInterfaceMethod($firstPart, $secondPart));
}
if ($this->isUserDefinedTrait($firstPart)) {
return CodeUnitCollection::fromList(CodeUnit::forTraitMethod($firstPart, $secondPart));
}
} else {
if ($this->isUserDefinedClass($unit)) {
$units = [CodeUnit::forClass($unit)];
foreach ($this->reflectorForClass($unit)->getTraits() as $trait) {
if (!$trait->isUserDefined()) {
// @codeCoverageIgnoreStart
continue;
// @codeCoverageIgnoreEnd
}
$units[] = CodeUnit::forTrait($trait->getName());
}
return CodeUnitCollection::fromArray($units);
}
if ($this->isUserDefinedInterface($unit)) {
return CodeUnitCollection::fromList(CodeUnit::forInterface($unit));
}
if ($this->isUserDefinedTrait($unit)) {
return CodeUnitCollection::fromList(CodeUnit::forTrait($unit));
}
if ($this->isUserDefinedFunction($unit)) {
return CodeUnitCollection::fromList(CodeUnit::forFunction($unit));
}
$unit = str_replace('<extended>', '', $unit);
if ($this->isUserDefinedClass($unit)) {
return $this->classAndParentClassesAndTraits($unit);
}
}
throw new InvalidCodeUnitException(
sprintf(
'"%s" is not a valid code unit',
$unit
)
);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function publicMethodsOfClass(string $className): CodeUnitCollection
{
return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function publicAndProtectedMethodsOfClass(string $className): CodeUnitCollection
{
return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function publicAndPrivateMethodsOfClass(string $className): CodeUnitCollection
{
return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PRIVATE);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function protectedMethodsOfClass(string $className): CodeUnitCollection
{
return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function protectedAndPrivateMethodsOfClass(string $className): CodeUnitCollection
{
return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PRIVATE);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function privateMethodsOfClass(string $className): CodeUnitCollection
{
return $this->methodsOfClass($className, ReflectionMethod::IS_PRIVATE);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function methodsOfClass(string $className, int $filter): CodeUnitCollection
{
$units = [];
foreach ($this->reflectorForClass($className)->getMethods($filter) as $method) {
if (!$method->isUserDefined()) {
continue;
}
$units[] = CodeUnit::forClassMethod($className, $method->getName());
}
return CodeUnitCollection::fromArray($units);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function classAndParentClassesAndTraits(string $className): CodeUnitCollection
{
$units = [CodeUnit::forClass($className)];
$reflector = $this->reflectorForClass($className);
foreach ($this->reflectorForClass($className)->getTraits() as $trait) {
if (!$trait->isUserDefined()) {
// @codeCoverageIgnoreStart
continue;
// @codeCoverageIgnoreEnd
}
$units[] = CodeUnit::forTrait($trait->getName());
}
while ($reflector = $reflector->getParentClass()) {
if (!$reflector->isUserDefined()) {
break;
}
$units[] = CodeUnit::forClass($reflector->getName());
foreach ($reflector->getTraits() as $trait) {
if (!$trait->isUserDefined()) {
// @codeCoverageIgnoreStart
continue;
// @codeCoverageIgnoreEnd
}
$units[] = CodeUnit::forTrait($trait->getName());
}
}
return CodeUnitCollection::fromArray($units);
}
/**
* @psalm-param class-string $className
*
* @throws ReflectionException
*/
private function reflectorForClass(string $className): ReflectionClass
{
try {
return new ReflectionClass($className);
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @throws ReflectionException
*/
private function isUserDefinedFunction(string $functionName): bool
{
if (!function_exists($functionName)) {
return false;
}
try {
return (new ReflectionFunction($functionName))->isUserDefined();
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @throws ReflectionException
*/
private function isUserDefinedClass(string $className): bool
{
if (!class_exists($className)) {
return false;
}
try {
return (new ReflectionClass($className))->isUserDefined();
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @throws ReflectionException
*/
private function isUserDefinedInterface(string $interfaceName): bool
{
if (!interface_exists($interfaceName)) {
return false;
}
try {
return (new ReflectionClass($interfaceName))->isUserDefined();
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @throws ReflectionException
*/
private function isUserDefinedTrait(string $traitName): bool
{
if (!trait_exists($traitName)) {
return false;
}
try {
return (new ReflectionClass($traitName))->isUserDefined();
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
/**
* @throws ReflectionException
*/
private function isUserDefinedMethod(string $className, string $methodName): bool
{
if (!class_exists($className)) {
// @codeCoverageIgnoreStart
return false;
// @codeCoverageIgnoreEnd
}
if (!method_exists($className, $methodName)) {
// @codeCoverageIgnoreStart
return false;
// @codeCoverageIgnoreEnd
}
try {
return (new ReflectionMethod($className, $methodName))->isUserDefined();
// @codeCoverageIgnoreStart
} catch (\ReflectionException $e) {
throw new ReflectionException(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
// @codeCoverageIgnoreEnd
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
/**
* @psalm-immutable
*/
final class TraitMethodUnit extends CodeUnit
{
/**
* @psalm-assert-if-true TraitMethodUnit $this
*/
public function isTraitMethod(): bool
{
return true;
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
/**
* @psalm-immutable
*/
final class TraitUnit extends CodeUnit
{
/**
* @psalm-assert-if-true TraitUnit $this
*/
public function isTrait(): bool
{
return true;
}
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use Throwable;
interface Exception extends Throwable
{
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use RuntimeException;
final class InvalidCodeUnitException extends RuntimeException implements Exception
{
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use RuntimeException;
final class NoTraitException extends RuntimeException implements Exception
{
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/code-unit.
*
* (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\CodeUnit;
use RuntimeException;
final class ReflectionException extends RuntimeException implements Exception
{
}

143
vendor/sebastian/comparator/ChangeLog.md vendored Normal file
View File

@ -0,0 +1,143 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [4.0.8] - 2022-09-14
### Fixed
* [#102](https://github.com/sebastianbergmann/comparator/pull/102): Fix `float` comparison precision
## [4.0.7] - 2022-09-14
### Fixed
* [#99](https://github.com/sebastianbergmann/comparator/pull/99): Fix weak comparison between `'0'` and `false`
## [4.0.6] - 2020-10-26
### Fixed
* `SebastianBergmann\Comparator\Exception` now correctly extends `\Throwable`
## [4.0.5] - 2020-09-30
### Fixed
* [#89](https://github.com/sebastianbergmann/comparator/pull/89): Handle PHP 8 `ValueError`
## [4.0.4] - 2020-09-28
### Changed
* Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3`
## [4.0.3] - 2020-06-26
### Added
* This component is now supported on PHP 8
## [4.0.2] - 2020-06-15
### Fixed
* [#85](https://github.com/sebastianbergmann/comparator/issues/85): Version 4.0.1 breaks backward compatibility
## [4.0.1] - 2020-06-15
### Changed
* Tests etc. are now ignored for archive exports
## [4.0.0] - 2020-02-07
### Removed
* Removed support for PHP 7.1 and PHP 7.2
## [3.0.5] - 2022-09-14
### Fixed
* [#102](https://github.com/sebastianbergmann/comparator/pull/102): Fix `float` comparison precision
## [3.0.4] - 2022-09-14
### Fixed
* [#99](https://github.com/sebastianbergmann/comparator/pull/99): Fix weak comparison between `'0'` and `false`
## [3.0.3] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.1` to `>=7.1`
## [3.0.2] - 2018-07-12
### Changed
* By default, `MockObjectComparator` is now tried before all other (default) comparators
## [3.0.1] - 2018-06-14
### Fixed
* [#53](https://github.com/sebastianbergmann/comparator/pull/53): `DOMNodeComparator` ignores `$ignoreCase` parameter
* [#58](https://github.com/sebastianbergmann/comparator/pull/58): `ScalarComparator` does not handle extremely ugly string comparison edge cases
## [3.0.0] - 2018-04-18
### Fixed
* [#48](https://github.com/sebastianbergmann/comparator/issues/48): `DateTimeComparator` does not support fractional second deltas
### Removed
* Removed support for PHP 7.0
## [2.1.3] - 2018-02-01
### Changed
* This component is now compatible with version 3 of `sebastian/diff`
## [2.1.2] - 2018-01-12
### Fixed
* Fix comparison of `DateTimeImmutable` objects
## [2.1.1] - 2017-12-22
### Fixed
* [phpunit/#2923](https://github.com/sebastianbergmann/phpunit/issues/2923): Unexpected failed date matching
## [2.1.0] - 2017-11-03
### Added
* Added `SebastianBergmann\Comparator\Factory::reset()` to unregister all non-default comparators
* Added support for `phpunit/phpunit-mock-objects` version `^5.0`
[4.0.8]: https://github.com/sebastianbergmann/comparator/compare/4.0.7...4.0.8
[4.0.7]: https://github.com/sebastianbergmann/comparator/compare/4.0.6...4.0.7
[4.0.6]: https://github.com/sebastianbergmann/comparator/compare/4.0.5...4.0.6
[4.0.5]: https://github.com/sebastianbergmann/comparator/compare/4.0.4...4.0.5
[4.0.4]: https://github.com/sebastianbergmann/comparator/compare/4.0.3...4.0.4
[4.0.3]: https://github.com/sebastianbergmann/comparator/compare/4.0.2...4.0.3
[4.0.2]: https://github.com/sebastianbergmann/comparator/compare/4.0.1...4.0.2
[4.0.1]: https://github.com/sebastianbergmann/comparator/compare/4.0.0...4.0.1
[4.0.0]: https://github.com/sebastianbergmann/comparator/compare/3.0.5...4.0.0
[3.0.5]: https://github.com/sebastianbergmann/comparator/compare/3.0.4...3.0.5
[3.0.4]: https://github.com/sebastianbergmann/comparator/compare/3.0.3...3.0.4
[3.0.3]: https://github.com/sebastianbergmann/comparator/compare/3.0.2...3.0.3
[3.0.2]: https://github.com/sebastianbergmann/comparator/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/sebastianbergmann/comparator/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/sebastianbergmann/comparator/compare/2.1.3...3.0.0
[2.1.3]: https://github.com/sebastianbergmann/comparator/compare/2.1.2...2.1.3
[2.1.2]: https://github.com/sebastianbergmann/comparator/compare/2.1.1...2.1.2
[2.1.1]: https://github.com/sebastianbergmann/comparator/compare/2.1.0...2.1.1
[2.1.0]: https://github.com/sebastianbergmann/comparator/compare/2.0.2...2.1.0

33
vendor/sebastian/comparator/LICENSE vendored Normal file
View File

@ -0,0 +1,33 @@
Comparator
Copyright (c) 2002-2020, 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.

41
vendor/sebastian/comparator/README.md vendored Normal file
View File

@ -0,0 +1,41 @@
# sebastian/comparator
[![CI Status](https://github.com/sebastianbergmann/comparator/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/comparator/actions)
[![Type Coverage](https://shepherd.dev/github/sebastianbergmann/comparator/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/comparator)
This component provides the functionality to compare PHP values for equality.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
```
composer require sebastian/comparator
```
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/comparator
```
## Usage
```php
<?php
use SebastianBergmann\Comparator\Factory;
use SebastianBergmann\Comparator\ComparisonFailure;
$date1 = new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York'));
$date2 = new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/Chicago'));
$factory = new Factory;
$comparator = $factory->getComparatorFor($date1, $date2);
try {
$comparator->assertEquals($date1, $date2);
print "Dates match";
} catch (ComparisonFailure $failure) {
print "Dates don't match";
}
```

View File

@ -0,0 +1,57 @@
{
"name": "sebastian/comparator",
"description": "Provides the functionality to compare PHP values for equality",
"keywords": ["comparator","compare","equality"],
"homepage": "https://github.com/sebastianbergmann/comparator",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
}
],
"prefer-stable": true,
"require": {
"php": ">=7.3",
"sebastian/diff": "^4.0",
"sebastian/exporter": "^4.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"config": {
"platform": {
"php": "7.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/_fixture"
]
},
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
}
}
}

View File

@ -0,0 +1,141 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function array_key_exists;
use function is_array;
use function sort;
use function sprintf;
use function str_replace;
use function trim;
/**
* Compares arrays for equality.
*
* Arrays are equal if they contain the same key-value pairs.
* The order of the keys does not matter.
* The types of key-value pairs do not matter.
*/
class ArrayComparator extends Comparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return is_array($expected) && is_array($actual);
}
/**
* Asserts that two arrays are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
* @param array $processed List of already processed elements (used to prevent infinite recursion)
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])/*: void*/
{
if ($canonicalize) {
sort($expected);
sort($actual);
}
$remaining = $actual;
$actualAsString = "Array (\n";
$expectedAsString = "Array (\n";
$equal = true;
foreach ($expected as $key => $value) {
unset($remaining[$key]);
if (!array_key_exists($key, $actual)) {
$expectedAsString .= sprintf(
" %s => %s\n",
$this->exporter->export($key),
$this->exporter->shortenedExport($value)
);
$equal = false;
continue;
}
try {
$comparator = $this->factory->getComparatorFor($value, $actual[$key]);
$comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed);
$expectedAsString .= sprintf(
" %s => %s\n",
$this->exporter->export($key),
$this->exporter->shortenedExport($value)
);
$actualAsString .= sprintf(
" %s => %s\n",
$this->exporter->export($key),
$this->exporter->shortenedExport($actual[$key])
);
} catch (ComparisonFailure $e) {
$expectedAsString .= sprintf(
" %s => %s\n",
$this->exporter->export($key),
$e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected())
);
$actualAsString .= sprintf(
" %s => %s\n",
$this->exporter->export($key),
$e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual())
);
$equal = false;
}
}
foreach ($remaining as $key => $value) {
$actualAsString .= sprintf(
" %s => %s\n",
$this->exporter->export($key),
$this->exporter->shortenedExport($value)
);
$equal = false;
}
$expectedAsString .= ')';
$actualAsString .= ')';
if (!$equal) {
throw new ComparisonFailure(
$expected,
$actual,
$expectedAsString,
$actualAsString,
false,
'Failed asserting that two arrays are equal.'
);
}
}
protected function indent($lines)
{
return trim(str_replace("\n", "\n ", $lines));
}
}

View File

@ -0,0 +1,61 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use SebastianBergmann\Exporter\Exporter;
/**
* Abstract base class for comparators which compare values for equality.
*/
abstract class Comparator
{
/**
* @var Factory
*/
protected $factory;
/**
* @var Exporter
*/
protected $exporter;
public function __construct()
{
$this->exporter = new Exporter;
}
public function setFactory(Factory $factory)/*: void*/
{
$this->factory = $factory;
}
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
abstract public function accepts($expected, $actual);
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
*
* @throws ComparisonFailure
*/
abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false);
}

View File

@ -0,0 +1,129 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use RuntimeException;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
/**
* Thrown when an assertion for string equality failed.
*/
class ComparisonFailure extends RuntimeException
{
/**
* Expected value of the retrieval which does not match $actual.
*
* @var mixed
*/
protected $expected;
/**
* Actually retrieved value which does not match $expected.
*
* @var mixed
*/
protected $actual;
/**
* The string representation of the expected value.
*
* @var string
*/
protected $expectedAsString;
/**
* The string representation of the actual value.
*
* @var string
*/
protected $actualAsString;
/**
* @var bool
*/
protected $identical;
/**
* Optional message which is placed in front of the first line
* returned by toString().
*
* @var string
*/
protected $message;
/**
* Initialises with the expected value and the actual value.
*
* @param mixed $expected expected value retrieved
* @param mixed $actual actual value retrieved
* @param string $expectedAsString
* @param string $actualAsString
* @param bool $identical
* @param string $message a string which is prefixed on all returned lines
* in the difference output
*/
public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '')
{
$this->expected = $expected;
$this->actual = $actual;
$this->expectedAsString = $expectedAsString;
$this->actualAsString = $actualAsString;
$this->message = $message;
}
public function getActual()
{
return $this->actual;
}
public function getExpected()
{
return $this->expected;
}
/**
* @return string
*/
public function getActualAsString()
{
return $this->actualAsString;
}
/**
* @return string
*/
public function getExpectedAsString()
{
return $this->expectedAsString;
}
/**
* @return string
*/
public function getDiff()
{
if (!$this->actualAsString && !$this->expectedAsString) {
return '';
}
$differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n"));
return $differ->diff($this->expectedAsString, $this->actualAsString);
}
/**
* @return string
*/
public function toString()
{
return $this->message . $this->getDiff();
}
}

View File

@ -0,0 +1,93 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function sprintf;
use function strtolower;
use DOMDocument;
use DOMNode;
use ValueError;
/**
* Compares DOMNode instances for equality.
*/
class DOMNodeComparator extends ObjectComparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return $expected instanceof DOMNode && $actual instanceof DOMNode;
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
* @param array $processed List of already processed elements (used to prevent infinite recursion)
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])/*: void*/
{
$expectedAsString = $this->nodeToText($expected, true, $ignoreCase);
$actualAsString = $this->nodeToText($actual, true, $ignoreCase);
if ($expectedAsString !== $actualAsString) {
$type = $expected instanceof DOMDocument ? 'documents' : 'nodes';
throw new ComparisonFailure(
$expected,
$actual,
$expectedAsString,
$actualAsString,
false,
sprintf("Failed asserting that two DOM %s are equal.\n", $type)
);
}
}
/**
* Returns the normalized, whitespace-cleaned, and indented textual
* representation of a DOMNode.
*/
private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string
{
if ($canonicalize) {
$document = new DOMDocument;
try {
@$document->loadXML($node->C14N());
} catch (ValueError $e) {
}
$node = $document;
}
$document = $node instanceof DOMDocument ? $node : $node->ownerDocument;
$document->formatOutput = true;
$document->normalizeDocument();
$text = $node instanceof DOMDocument ? $node->saveXML() : $document->saveXML($node);
return $ignoreCase ? strtolower($text) : $text;
}
}

View File

@ -0,0 +1,95 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function abs;
use function floor;
use function sprintf;
use DateInterval;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Exception;
/**
* Compares DateTimeInterface instances for equality.
*/
class DateTimeComparator extends ObjectComparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return ($expected instanceof DateTime || $expected instanceof DateTimeInterface) &&
($actual instanceof DateTime || $actual instanceof DateTimeInterface);
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
* @param array $processed List of already processed elements (used to prevent infinite recursion)
*
* @throws Exception
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])/*: void*/
{
/** @var DateTimeInterface $expected */
/** @var DateTimeInterface $actual */
$absDelta = abs($delta);
$delta = new DateInterval(sprintf('PT%dS', $absDelta));
$delta->f = $absDelta - floor($absDelta);
$actualClone = (clone $actual)
->setTimezone(new DateTimeZone('UTC'));
$expectedLower = (clone $expected)
->setTimezone(new DateTimeZone('UTC'))
->sub($delta);
$expectedUpper = (clone $expected)
->setTimezone(new DateTimeZone('UTC'))
->add($delta);
if ($actualClone < $expectedLower || $actualClone > $expectedUpper) {
throw new ComparisonFailure(
$expected,
$actual,
$this->dateTimeToString($expected),
$this->dateTimeToString($actual),
false,
'Failed asserting that two DateTime objects are equal.'
);
}
}
/**
* Returns an ISO 8601 formatted string representation of a datetime or
* 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly
* initialized.
*/
private function dateTimeToString(DateTimeInterface $datetime): string
{
$string = $datetime->format('Y-m-d\TH:i:s.uO');
return $string ?: 'Invalid DateTimeInterface object';
}
}

View File

@ -0,0 +1,61 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function is_float;
use function is_numeric;
/**
* Compares doubles for equality.
*
* @deprecated since v3.0.5 and v4.0.8
*/
class DoubleComparator extends NumericComparator
{
/**
* Smallest value available in PHP.
*
* @var float
*/
public const EPSILON = 0.0000000001;
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return (is_float($expected) || is_float($actual)) && is_numeric($expected) && is_numeric($actual);
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)/*: void*/
{
if ($delta == 0) {
$delta = self::EPSILON;
}
parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase);
}
}

View File

@ -0,0 +1,54 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use Exception;
/**
* Compares Exception instances for equality.
*/
class ExceptionComparator extends ObjectComparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return $expected instanceof Exception && $actual instanceof Exception;
}
/**
* Converts an object to an array containing all of its private, protected
* and public properties.
*
* @param object $object
*
* @return array
*/
protected function toArray($object)
{
$array = parent::toArray($object);
unset(
$array['file'],
$array['line'],
$array['trace'],
$array['string'],
$array['xdebug_message']
);
return $array;
}
}

View File

@ -0,0 +1,141 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function array_unshift;
/**
* Factory for comparators which compare values for equality.
*/
class Factory
{
/**
* @var Factory
*/
private static $instance;
/**
* @var Comparator[]
*/
private $customComparators = [];
/**
* @var Comparator[]
*/
private $defaultComparators = [];
/**
* @return Factory
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self; // @codeCoverageIgnore
}
return self::$instance;
}
/**
* Constructs a new factory.
*/
public function __construct()
{
$this->registerDefaultComparators();
}
/**
* Returns the correct comparator for comparing two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return Comparator
*/
public function getComparatorFor($expected, $actual)
{
foreach ($this->customComparators as $comparator) {
if ($comparator->accepts($expected, $actual)) {
return $comparator;
}
}
foreach ($this->defaultComparators as $comparator) {
if ($comparator->accepts($expected, $actual)) {
return $comparator;
}
}
throw new RuntimeException('No suitable Comparator implementation found');
}
/**
* Registers a new comparator.
*
* This comparator will be returned by getComparatorFor() if its accept() method
* returns TRUE for the compared values. It has higher priority than the
* existing comparators, meaning that its accept() method will be invoked
* before those of the other comparators.
*
* @param Comparator $comparator The comparator to be registered
*/
public function register(Comparator $comparator)/*: void*/
{
array_unshift($this->customComparators, $comparator);
$comparator->setFactory($this);
}
/**
* Unregisters a comparator.
*
* This comparator will no longer be considered by getComparatorFor().
*
* @param Comparator $comparator The comparator to be unregistered
*/
public function unregister(Comparator $comparator)/*: void*/
{
foreach ($this->customComparators as $key => $_comparator) {
if ($comparator === $_comparator) {
unset($this->customComparators[$key]);
}
}
}
/**
* Unregisters all non-default comparators.
*/
public function reset()/*: void*/
{
$this->customComparators = [];
}
private function registerDefaultComparators(): void
{
$this->registerDefaultComparator(new MockObjectComparator);
$this->registerDefaultComparator(new DateTimeComparator);
$this->registerDefaultComparator(new DOMNodeComparator);
$this->registerDefaultComparator(new SplObjectStorageComparator);
$this->registerDefaultComparator(new ExceptionComparator);
$this->registerDefaultComparator(new ObjectComparator);
$this->registerDefaultComparator(new ResourceComparator);
$this->registerDefaultComparator(new ArrayComparator);
$this->registerDefaultComparator(new NumericComparator);
$this->registerDefaultComparator(new ScalarComparator);
$this->registerDefaultComparator(new TypeComparator);
}
private function registerDefaultComparator(Comparator $comparator): void
{
$this->defaultComparators[] = $comparator;
$comparator->setFactory($this);
}
}

View File

@ -0,0 +1,48 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use PHPUnit\Framework\MockObject\MockObject;
/**
* Compares PHPUnit\Framework\MockObject\MockObject instances for equality.
*/
class MockObjectComparator extends ObjectComparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return $expected instanceof MockObject && $actual instanceof MockObject;
}
/**
* Converts an object to an array containing all of its private, protected
* and public properties.
*
* @param object $object
*
* @return array
*/
protected function toArray($object)
{
$array = parent::toArray($object);
unset($array['__phpunit_invocationMocker']);
return $array;
}
}

View File

@ -0,0 +1,84 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function abs;
use function is_float;
use function is_infinite;
use function is_nan;
use function is_numeric;
use function is_string;
use function sprintf;
/**
* Compares numerical values for equality.
*/
class NumericComparator extends ScalarComparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
// all numerical values, but not if both of them are strings
return is_numeric($expected) && is_numeric($actual) &&
!(is_string($expected) && is_string($actual));
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)/*: void*/
{
if ($this->isInfinite($actual) && $this->isInfinite($expected)) {
return;
}
if (($this->isInfinite($actual) xor $this->isInfinite($expected)) ||
($this->isNan($actual) || $this->isNan($expected)) ||
abs($actual - $expected) > $delta) {
throw new ComparisonFailure(
$expected,
$actual,
'',
'',
false,
sprintf(
'Failed asserting that %s matches expected %s.',
$this->exporter->export($actual),
$this->exporter->export($expected)
)
);
}
}
private function isInfinite($value): bool
{
return is_float($value) && is_infinite($value);
}
private function isNan($value): bool
{
return is_float($value) && is_nan($value);
}
}

View File

@ -0,0 +1,112 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function get_class;
use function in_array;
use function is_object;
use function sprintf;
use function substr_replace;
/**
* Compares objects for equality.
*/
class ObjectComparator extends ArrayComparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return is_object($expected) && is_object($actual);
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
* @param array $processed List of already processed elements (used to prevent infinite recursion)
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = [])/*: void*/
{
if (get_class($actual) !== get_class($expected)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
sprintf(
'%s is not instance of expected class "%s".',
$this->exporter->export($actual),
get_class($expected)
)
);
}
// don't compare twice to allow for cyclic dependencies
if (in_array([$actual, $expected], $processed, true) ||
in_array([$expected, $actual], $processed, true)) {
return;
}
$processed[] = [$actual, $expected];
// don't compare objects if they are identical
// this helps to avoid the error "maximum function nesting level reached"
// CAUTION: this conditional clause is not tested
if ($actual !== $expected) {
try {
parent::assertEquals(
$this->toArray($expected),
$this->toArray($actual),
$delta,
$canonicalize,
$ignoreCase,
$processed
);
} catch (ComparisonFailure $e) {
throw new ComparisonFailure(
$expected,
$actual,
// replace "Array" with "MyClass object"
substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5),
substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5),
false,
'Failed asserting that two objects are equal.'
);
}
}
}
/**
* Converts an object to an array containing all of its private, protected
* and public properties.
*
* @param object $object
*
* @return array
*/
protected function toArray($object)
{
return $this->exporter->toArray($object);
}
}

View File

@ -0,0 +1,54 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function is_resource;
/**
* Compares resources for equality.
*/
class ResourceComparator extends Comparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return is_resource($expected) && is_resource($actual);
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)/*: void*/
{
if ($actual != $expected) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual)
);
}
}
}

View File

@ -0,0 +1,99 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function is_bool;
use function is_object;
use function is_scalar;
use function is_string;
use function method_exists;
use function sprintf;
use function strtolower;
/**
* Compares scalar or NULL values for equality.
*/
class ScalarComparator extends Comparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*
* @since Method available since Release 3.6.0
*/
public function accepts($expected, $actual)
{
return ((is_scalar($expected) xor null === $expected) &&
(is_scalar($actual) xor null === $actual))
// allow comparison between strings and objects featuring __toString()
|| (is_string($expected) && is_object($actual) && method_exists($actual, '__toString'))
|| (is_object($expected) && method_exists($expected, '__toString') && is_string($actual));
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)/*: void*/
{
$expectedToCompare = $expected;
$actualToCompare = $actual;
// always compare as strings to avoid strange behaviour
// otherwise 0 == 'Foobar'
if ((is_string($expected) && !is_bool($actual)) || (is_string($actual) && !is_bool($expected))) {
$expectedToCompare = (string) $expectedToCompare;
$actualToCompare = (string) $actualToCompare;
if ($ignoreCase) {
$expectedToCompare = strtolower($expectedToCompare);
$actualToCompare = strtolower($actualToCompare);
}
}
if ($expectedToCompare !== $actualToCompare && is_string($expected) && is_string($actual)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
'Failed asserting that two strings are equal.'
);
}
if ($expectedToCompare != $actualToCompare) {
throw new ComparisonFailure(
$expected,
$actual,
// no diff is required
'',
'',
false,
sprintf(
'Failed asserting that %s matches expected %s.',
$this->exporter->export($actual),
$this->exporter->export($expected)
)
);
}
}
}

View File

@ -0,0 +1,71 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use SplObjectStorage;
/**
* Compares \SplObjectStorage instances for equality.
*/
class SplObjectStorageComparator extends Comparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return $expected instanceof SplObjectStorage && $actual instanceof SplObjectStorage;
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)/*: void*/
{
foreach ($actual as $object) {
if (!$expected->contains($object)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
'Failed asserting that two objects are equal.'
);
}
}
foreach ($expected as $object) {
if (!$actual->contains($object)) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual),
false,
'Failed asserting that two objects are equal.'
);
}
}
}
}

View File

@ -0,0 +1,62 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use function gettype;
use function sprintf;
/**
* Compares values for type equality.
*/
class TypeComparator extends Comparator
{
/**
* Returns whether the comparator can compare two values.
*
* @param mixed $expected The first value to compare
* @param mixed $actual The second value to compare
*
* @return bool
*/
public function accepts($expected, $actual)
{
return true;
}
/**
* Asserts that two values are equal.
*
* @param mixed $expected First value to compare
* @param mixed $actual Second value to compare
* @param float $delta Allowed numerical distance between two values to consider them equal
* @param bool $canonicalize Arrays are sorted before comparison when set to true
* @param bool $ignoreCase Case is ignored when set to true
*
* @throws ComparisonFailure
*/
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)/*: void*/
{
if (gettype($expected) != gettype($actual)) {
throw new ComparisonFailure(
$expected,
$actual,
// we don't need a diff
'',
'',
false,
sprintf(
'%s does not match expected type "%s".',
$this->exporter->shortenedExport($actual),
gettype($expected)
)
);
}
}
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
use Throwable;
interface Exception extends Throwable
{
}

View File

@ -0,0 +1,14 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/comparator.
*
* (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\Comparator;
final class RuntimeException extends \RuntimeException implements Exception
{
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.0.1@b1e2e30026936ef8d5bf6a354d1c3959b6231f44"/>

View File

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<psalm
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
resolveFromConfigFile="false"
errorBaseline=".psalm/baseline.xml"
totallyTyped="true"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

View File

@ -0,0 +1,30 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [2.0.2] - 2020-10-26
### Fixed
* `SebastianBergmann\Complexity\Exception` now correctly extends `\Throwable`
## [2.0.1] - 2020-09-28
### Changed
* Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3`
## [2.0.0] - 2020-07-25
### Removed
* The `ParentConnectingVisitor` has been removed (it should have been marked as `@internal`)
## [1.0.0] - 2020-07-22
* Initial release
[2.0.2]: https://github.com/sebastianbergmann/complexity/compare/2.0.1...2.0.2
[2.0.1]: https://github.com/sebastianbergmann/complexity/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/sebastianbergmann/complexity/compare/1.0.0...2.0.0
[1.0.0]: https://github.com/sebastianbergmann/complexity/compare/70ee0ad32d9e2be3f85beffa3e2eb474193f2487...1.0.0

33
vendor/sebastian/complexity/LICENSE vendored Normal file
View File

@ -0,0 +1,33 @@
sebastian/complexity
Copyright (c) 2020, 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.

22
vendor/sebastian/complexity/README.md vendored Normal file
View File

@ -0,0 +1,22 @@
# sebastian/complexity
Library for calculating the complexity of PHP code units.
[![Latest Stable Version](https://img.shields.io/packagist/v/sebastian/complexity.svg?style=flat-square)](https://packagist.org/packages/sebastian/complexity)
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.3-8892BF.svg?style=flat-square)](https://php.net/)
[![CI Status](https://github.com/sebastianbergmann/complexity/workflows/CI/badge.svg?branch=master&event=push)](https://phpunit.de/build-status.html)
[![Type Coverage](https://shepherd.dev/github/sebastianbergmann/complexity/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/complexity)
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
```
composer require sebastian/complexity
```
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/complexity
```

View File

@ -0,0 +1,41 @@
{
"name": "sebastian/complexity",
"description": "Library for calculating the complexity of PHP code units",
"type": "library",
"homepage": "https://github.com/sebastianbergmann/complexity",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues"
},
"require": {
"php": ">=7.3",
"nikic/php-parser": "^4.7"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"config": {
"platform": {
"php": "7.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
}
}

View File

@ -0,0 +1,88 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
use PhpParser\Error;
use PhpParser\Lexer;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\NodeVisitor\ParentConnectingVisitor;
use PhpParser\Parser;
use PhpParser\ParserFactory;
final class Calculator
{
/**
* @throws RuntimeException
*/
public function calculateForSourceFile(string $sourceFile): ComplexityCollection
{
return $this->calculateForSourceString(file_get_contents($sourceFile));
}
/**
* @throws RuntimeException
*/
public function calculateForSourceString(string $source): ComplexityCollection
{
try {
$nodes = $this->parser()->parse($source);
assert($nodes !== null);
return $this->calculateForAbstractSyntaxTree($nodes);
// @codeCoverageIgnoreStart
} catch (Error $error) {
throw new RuntimeException(
$error->getMessage(),
(int) $error->getCode(),
$error
);
}
// @codeCoverageIgnoreEnd
}
/**
* @param Node[] $nodes
*
* @throws RuntimeException
*/
public function calculateForAbstractSyntaxTree(array $nodes): ComplexityCollection
{
$traverser = new NodeTraverser;
$complexityCalculatingVisitor = new ComplexityCalculatingVisitor(true);
$traverser->addVisitor(new NameResolver);
$traverser->addVisitor(new ParentConnectingVisitor);
$traverser->addVisitor($complexityCalculatingVisitor);
try {
/* @noinspection UnusedFunctionResultInspection */
$traverser->traverse($nodes);
// @codeCoverageIgnoreStart
} catch (Error $error) {
throw new RuntimeException(
$error->getMessage(),
(int) $error->getCode(),
$error
);
}
// @codeCoverageIgnoreEnd
return $complexityCalculatingVisitor->result();
}
private function parser(): Parser
{
return (new ParserFactory)->create(ParserFactory::PREFER_PHP7, new Lexer);
}
}

View File

@ -0,0 +1,42 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
/**
* @psalm-immutable
*/
final class Complexity
{
/**
* @var string
*/
private $name;
/**
* @var int
*/
private $cyclomaticComplexity;
public function __construct(string $name, int $cyclomaticComplexity)
{
$this->name = $name;
$this->cyclomaticComplexity = $cyclomaticComplexity;
}
public function name(): string
{
return $this->name;
}
public function cyclomaticComplexity(): int
{
return $this->cyclomaticComplexity;
}
}

View File

@ -0,0 +1,72 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
use function count;
use Countable;
use IteratorAggregate;
/**
* @psalm-immutable
*/
final class ComplexityCollection implements Countable, IteratorAggregate
{
/**
* @psalm-var list<Complexity>
*/
private $items = [];
public static function fromList(Complexity ...$items): self
{
return new self($items);
}
/**
* @psalm-param list<Complexity> $items
*/
private function __construct(array $items)
{
$this->items = $items;
}
/**
* @psalm-return list<Complexity>
*/
public function asArray(): array
{
return $this->items;
}
public function getIterator(): ComplexityCollectionIterator
{
return new ComplexityCollectionIterator($this);
}
public function count(): int
{
return count($this->items);
}
public function isEmpty(): bool
{
return empty($this->items);
}
public function cyclomaticComplexity(): int
{
$cyclomaticComplexity = 0;
foreach ($this as $item) {
$cyclomaticComplexity += $item->cyclomaticComplexity();
}
return $cyclomaticComplexity;
}
}

View File

@ -0,0 +1,55 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
use Iterator;
final class ComplexityCollectionIterator implements Iterator
{
/**
* @psalm-var list<Complexity>
*/
private $items;
/**
* @var int
*/
private $position = 0;
public function __construct(ComplexityCollection $items)
{
$this->items = $items->asArray();
}
public function rewind(): void
{
$this->position = 0;
}
public function valid(): bool
{
return isset($this->items[$this->position]);
}
public function key(): int
{
return $this->position;
}
public function current(): Complexity
{
return $this->items[$this->position];
}
public function next(): void
{
$this->position++;
}
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
use Throwable;
interface Exception extends Throwable
{
}

View File

@ -0,0 +1,14 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
final class RuntimeException extends \RuntimeException implements Exception
{
}

View File

@ -0,0 +1,109 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
use function assert;
use function is_array;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
final class ComplexityCalculatingVisitor extends NodeVisitorAbstract
{
/**
* @psalm-var list<Complexity>
*/
private $result = [];
/**
* @var bool
*/
private $shortCircuitTraversal;
public function __construct(bool $shortCircuitTraversal)
{
$this->shortCircuitTraversal = $shortCircuitTraversal;
}
public function enterNode(Node $node): ?int
{
if (!$node instanceof ClassMethod && !$node instanceof Function_) {
return null;
}
if ($node instanceof ClassMethod) {
$name = $this->classMethodName($node);
} else {
$name = $this->functionName($node);
}
$statements = $node->getStmts();
assert(is_array($statements));
$this->result[] = new Complexity(
$name,
$this->cyclomaticComplexity($statements)
);
if ($this->shortCircuitTraversal) {
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}
return null;
}
public function result(): ComplexityCollection
{
return ComplexityCollection::fromList(...$this->result);
}
/**
* @param Stmt[] $statements
*/
private function cyclomaticComplexity(array $statements): int
{
$traverser = new NodeTraverser;
$cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor;
$traverser->addVisitor($cyclomaticComplexityCalculatingVisitor);
/* @noinspection UnusedFunctionResultInspection */
$traverser->traverse($statements);
return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity();
}
private function classMethodName(ClassMethod $node): string
{
$parent = $node->getAttribute('parent');
assert($parent instanceof Class_ || $parent instanceof Trait_);
assert(isset($parent->namespacedName));
assert($parent->namespacedName instanceof Name);
return $parent->namespacedName->toString() . '::' . $node->name->toString();
}
private function functionName(Function_ $node): string
{
assert(isset($node->namespacedName));
assert($node->namespacedName instanceof Name);
return $node->namespacedName->toString();
}
}

View File

@ -0,0 +1,59 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/complexity.
*
* (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\Complexity;
use function get_class;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
use PhpParser\Node\Expr\BinaryOp\LogicalAnd;
use PhpParser\Node\Expr\BinaryOp\LogicalOr;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Stmt\Case_;
use PhpParser\Node\Stmt\Catch_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\While_;
use PhpParser\NodeVisitorAbstract;
final class CyclomaticComplexityCalculatingVisitor extends NodeVisitorAbstract
{
/**
* @var int
*/
private $cyclomaticComplexity = 1;
public function enterNode(Node $node): void
{
/* @noinspection GetClassMissUseInspection */
switch (get_class($node)) {
case BooleanAnd::class:
case BooleanOr::class:
case Case_::class:
case Catch_::class:
case ElseIf_::class:
case For_::class:
case Foreach_::class:
case If_::class:
case LogicalAnd::class:
case LogicalOr::class:
case Ternary::class:
case While_::class:
$this->cyclomaticComplexity++;
}
}
public function cyclomaticComplexity(): int
{
return $this->cyclomaticComplexity;
}
}

88
vendor/sebastian/diff/ChangeLog.md vendored Normal file
View File

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

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

@ -0,0 +1,33 @@
sebastian/diff
Copyright (c) 2002-2020, 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.

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

@ -0,0 +1,202 @@
# sebastian/diff
[![CI Status](https://github.com/sebastianbergmann/diff/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/diff/actions)
[![Type Coverage](https://shepherd.dev/github/sebastianbergmann/diff/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/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' => null,
'fromFileDate' => null,
'toFile' => null,
'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'));
)
)
)
)
)
)

47
vendor/sebastian/diff/composer.json vendored Normal file
View File

@ -0,0 +1,47 @@
{
"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"
}
],
"prefer-stable": true,
"config": {
"platform": {
"php": "7.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
"symfony/process": "^4.2 || ^5"
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/"
]
},
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
}
}
}

89
vendor/sebastian/diff/src/Chunk.php vendored Normal file
View File

@ -0,0 +1,89 @@
<?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 Line[]
*/
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;
}
/**
* @return Line[]
*/
public function getLines(): array
{
return $this->lines;
}
/**
* @param Line[] $lines
*/
public function setLines(array $lines): void
{
foreach ($lines as $line) {
if (!$line instanceof Line) {
throw new InvalidArgumentException;
}
}
$this->lines = $lines;
}
}

64
vendor/sebastian/diff/src/Diff.php vendored Normal file
View File

@ -0,0 +1,64 @@
<?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 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): void
{
$this->chunks = $chunks;
}
}

327
vendor/sebastian/diff/src/Differ.php vendored Normal file
View File

@ -0,0 +1,327 @@
<?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 get_class;
use function gettype;
use function is_array;
use function is_object;
use function is_string;
use function key;
use function min;
use function preg_split;
use function prev;
use function reset;
use function sprintf;
use function substr;
use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
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;
/**
* @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
*/
public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null): string
{
$diff = $this->diffToArray(
$this->normalizeDiffInput($from),
$this->normalizeDiffInput($to),
$lcs
);
return $this->outputBuilder->getDiff($diff);
}
/**
* 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
*/
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.');
}
[$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;
}
/**
* Casts variable to string if it is not a string or array.
*
* @return array|string
*/
private function normalizeDiffInput($input)
{
if (!is_array($input) && !is_string($input)) {
return (string) $input;
}
return $input;
}
/**
* Checks if input is string, if so it will split it line-by-line.
*/
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;
}
/**
* Calculates the estimated memory footprint for the DP-based method.
*
* @return float|int
*/
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.
*/
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 ("\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,38 @@
<?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 get_class;
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) ? get_class($value) : (null === $value ? '<null>' : gettype($value) . '#' . $value)
),
$code,
$previous
);
}
}

View File

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

View File

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

45
vendor/sebastian/diff/src/Line.php vendored Normal file
View File

@ -0,0 +1,45 @@
<?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;
/**
* @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,18 @@
<?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;
}

View File

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

View File

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

View File

@ -0,0 +1,72 @@
<?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 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
{
/**
* @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] === 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 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,338 @@
<?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 $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,
];
/**
* @var bool
*/
private $changed;
/**
* @var bool
*/
private $collapseRanges;
/**
* @var int >= 0
*/
private $commonLineThreshold;
/**
* @var string
*/
private $header;
/**
* @var int >= 0
*/
private $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 has trailing linebreak, else add under it 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 where 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]);
}
}
}

View File

@ -0,0 +1,272 @@
<?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 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
{
/**
* @var bool
*/
private $collapseRanges = true;
/**
* @var int >= 0
*/
private $commonLineThreshold = 6;
/**
* @var int >= 0
*/
private $contextLines = 3;
/**
* @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");
}
}
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 has trailing linebreak, else add under it 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 where 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]);
}
}
}
}

110
vendor/sebastian/diff/src/Parser.php vendored Normal file
View File

@ -0,0 +1,110 @@
<?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)) {
$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,70 @@
<?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;
$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,176 @@
# Changes in sebastianbergmann/environment
All notable changes in `sebastianbergmann/environment` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [5.1.4] - 2022-04-03
### Fixed
* [#63](https://github.com/sebastianbergmann/environment/pull/63): `Runtime::getCurrentSettings()` does not correctly process INI settings
## [5.1.3] - 2020-09-28
### Changed
* Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3`
## [5.1.2] - 2020-06-26
### Added
* This component is now supported on PHP 8
## [5.1.1] - 2020-06-15
### Changed
* Tests etc. are now ignored for archive exports
## [5.1.0] - 2020-04-14
### Added
* `Runtime::performsJustInTimeCompilation()` returns `true` if PHP 8's JIT is active, `false` otherwise
## [5.0.2] - 2020-03-31
### Fixed
* [#55](https://github.com/sebastianbergmann/environment/issues/55): `stty` command is executed even if no tty is available
## [5.0.1] - 2020-02-19
### Changed
* `Runtime::getNameWithVersionAndCodeCoverageDriver()` now prioritizes PCOV over Xdebug when both extensions are loaded (just like php-code-coverage does)
## [5.0.0] - 2020-02-07
### Removed
* This component is no longer supported on PHP 7.1 and PHP 7.2
## [4.2.3] - 2019-11-20
### Changed
* [#50](https://github.com/sebastianbergmann/environment/pull/50): Windows improvements to console capabilities
### Fixed
* [#49](https://github.com/sebastianbergmann/environment/issues/49): Detection how OpCache handles docblocks does not work correctly when PHPDBG is used
## [4.2.2] - 2019-05-05
### Fixed
* [#44](https://github.com/sebastianbergmann/environment/pull/44): `TypeError` in `Console::getNumberOfColumnsInteractive()`
## [4.2.1] - 2019-04-25
### Fixed
* Fixed an issue in `Runtime::getCurrentSettings()`
## [4.2.0] - 2019-04-25
### Added
* [#36](https://github.com/sebastianbergmann/environment/pull/36): `Runtime::getCurrentSettings()`
## [4.1.0] - 2019-02-01
### Added
* Implemented `Runtime::getNameWithVersionAndCodeCoverageDriver()` method
* [#34](https://github.com/sebastianbergmann/environment/pull/34): Support for PCOV extension
## [4.0.2] - 2019-01-28
### Fixed
* [#33](https://github.com/sebastianbergmann/environment/issues/33): `Runtime::discardsComments()` returns true too eagerly
### Removed
* Removed support for Zend Optimizer+ in `Runtime::discardsComments()`
## [4.0.1] - 2018-11-25
### Fixed
* [#31](https://github.com/sebastianbergmann/environment/issues/31): Regressions in `Console` class
## [4.0.0] - 2018-10-23 [YANKED]
### Fixed
* [#25](https://github.com/sebastianbergmann/environment/pull/25): `Console::hasColorSupport()` does not work on Windows
### Removed
* This component is no longer supported on PHP 7.0
## [3.1.0] - 2017-07-01
### Added
* [#21](https://github.com/sebastianbergmann/environment/issues/21): Equivalent of `PHP_OS_FAMILY` (for PHP < 7.2)
## [3.0.4] - 2017-06-20
### Fixed
* [#20](https://github.com/sebastianbergmann/environment/pull/20): PHP 7 mode of HHVM not forced
## [3.0.3] - 2017-05-18
### Fixed
* [#18](https://github.com/sebastianbergmann/environment/issues/18): `Uncaught TypeError: preg_match() expects parameter 2 to be string, null given`
## [3.0.2] - 2017-04-21
### Fixed
* [#17](https://github.com/sebastianbergmann/environment/issues/17): `Uncaught TypeError: trim() expects parameter 1 to be string, boolean given`
## [3.0.1] - 2017-04-21
### Fixed
* Fixed inverted logic in `Runtime::discardsComments()`
## [3.0.0] - 2017-04-21
### Added
* Implemented `Runtime::discardsComments()` for querying whether the PHP runtime discards annotations
### Removed
* This component is no longer supported on PHP 5.6
[5.1.4]: https://github.com/sebastianbergmann/environment/compare/5.1.3...5.1.4
[5.1.3]: https://github.com/sebastianbergmann/environment/compare/5.1.2...5.1.3
[5.1.2]: https://github.com/sebastianbergmann/environment/compare/5.1.1...5.1.2
[5.1.1]: https://github.com/sebastianbergmann/environment/compare/5.1.0...5.1.1
[5.1.0]: https://github.com/sebastianbergmann/environment/compare/5.0.2...5.1.0
[5.0.2]: https://github.com/sebastianbergmann/environment/compare/5.0.1...5.0.2
[5.0.1]: https://github.com/sebastianbergmann/environment/compare/5.0.0...5.0.1
[5.0.0]: https://github.com/sebastianbergmann/environment/compare/4.2.3...5.0.0
[4.2.3]: https://github.com/sebastianbergmann/environment/compare/4.2.2...4.2.3
[4.2.2]: https://github.com/sebastianbergmann/environment/compare/4.2.1...4.2.2
[4.2.1]: https://github.com/sebastianbergmann/environment/compare/4.2.0...4.2.1
[4.2.0]: https://github.com/sebastianbergmann/environment/compare/4.1.0...4.2.0
[4.1.0]: https://github.com/sebastianbergmann/environment/compare/4.0.2...4.1.0
[4.0.2]: https://github.com/sebastianbergmann/environment/compare/4.0.1...4.0.2
[4.0.1]: https://github.com/sebastianbergmann/environment/compare/66691f8e2dc4641909166b275a9a4f45c0e89092...4.0.1
[4.0.0]: https://github.com/sebastianbergmann/environment/compare/3.1.0...66691f8e2dc4641909166b275a9a4f45c0e89092
[3.1.0]: https://github.com/sebastianbergmann/environment/compare/3.0...3.1.0
[3.0.4]: https://github.com/sebastianbergmann/environment/compare/3.0.3...3.0.4
[3.0.3]: https://github.com/sebastianbergmann/environment/compare/3.0.2...3.0.3
[3.0.2]: https://github.com/sebastianbergmann/environment/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/sebastianbergmann/environment/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/sebastianbergmann/environment/compare/2.0...3.0.0

33
vendor/sebastian/environment/LICENSE vendored Normal file
View File

@ -0,0 +1,33 @@
sebastian/environment
Copyright (c) 2014-2022, 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.

21
vendor/sebastian/environment/README.md vendored Normal file
View File

@ -0,0 +1,21 @@
# sebastian/environment
[![Latest Stable Version](https://img.shields.io/packagist/v/sebastian/environment.svg?style=flat-square)](https://packagist.org/packages/sebastian/environment)
[![CI Status](https://github.com/sebastianbergmann/environment/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/environment/actions)
[![Type Coverage](https://shepherd.dev/github/sebastianbergmann/environment/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/environment)
This component provides functionality that helps writing PHP code that has runtime-specific (PHP / HHVM) execution paths.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
```
composer require sebastian/environment
```
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/environment
```

View File

@ -0,0 +1,40 @@
{
"name": "sebastian/environment",
"description": "Provides functionality to handle HHVM/PHP environments",
"keywords": ["environment","hhvm","xdebug"],
"homepage": "http://www.github.com/sebastianbergmann/environment",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"config": {
"platform": {
"php": "7.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"prefer-stable": true,
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-posix": "*"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "5.1-dev"
}
}
}

View File

@ -0,0 +1,189 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/environment.
*
* (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\Environment;
use const DIRECTORY_SEPARATOR;
use const STDIN;
use const STDOUT;
use function defined;
use function fclose;
use function fstat;
use function function_exists;
use function getenv;
use function is_resource;
use function is_string;
use function posix_isatty;
use function preg_match;
use function proc_close;
use function proc_open;
use function sapi_windows_vt100_support;
use function shell_exec;
use function stream_get_contents;
use function stream_isatty;
use function trim;
final class Console
{
/**
* @var int
*/
public const STDIN = 0;
/**
* @var int
*/
public const STDOUT = 1;
/**
* @var int
*/
public const STDERR = 2;
/**
* Returns true if STDOUT supports colorization.
*
* This code has been copied and adapted from
* Symfony\Component\Console\Output\StreamOutput.
*/
public function hasColorSupport(): bool
{
if ('Hyper' === getenv('TERM_PROGRAM')) {
return true;
}
if ($this->isWindows()) {
// @codeCoverageIgnoreStart
return (defined('STDOUT') && function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) ||
false !== getenv('ANSICON') ||
'ON' === getenv('ConEmuANSI') ||
'xterm' === getenv('TERM');
// @codeCoverageIgnoreEnd
}
if (!defined('STDOUT')) {
// @codeCoverageIgnoreStart
return false;
// @codeCoverageIgnoreEnd
}
return $this->isInteractive(STDOUT);
}
/**
* Returns the number of columns of the terminal.
*
* @codeCoverageIgnore
*/
public function getNumberOfColumns(): int
{
if (!$this->isInteractive(defined('STDIN') ? STDIN : self::STDIN)) {
return 80;
}
if ($this->isWindows()) {
return $this->getNumberOfColumnsWindows();
}
return $this->getNumberOfColumnsInteractive();
}
/**
* Returns if the file descriptor is an interactive terminal or not.
*
* Normally, we want to use a resource as a parameter, yet sadly it's not always awailable,
* eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined.
*
* @param int|resource $fileDescriptor
*/
public function isInteractive($fileDescriptor = self::STDOUT): bool
{
if (is_resource($fileDescriptor)) {
// These functions require a descriptor that is a real resource, not a numeric ID of it
if (function_exists('stream_isatty') && @stream_isatty($fileDescriptor)) {
return true;
}
// Check if formatted mode is S_IFCHR
if (function_exists('fstat') && @stream_isatty($fileDescriptor)) {
$stat = @fstat(STDOUT);
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
}
return false;
}
return function_exists('posix_isatty') && @posix_isatty($fileDescriptor);
}
private function isWindows(): bool
{
return DIRECTORY_SEPARATOR === '\\';
}
/**
* @codeCoverageIgnore
*/
private function getNumberOfColumnsInteractive(): int
{
if (function_exists('shell_exec') && preg_match('#\d+ (\d+)#', shell_exec('stty size') ?: '', $match) === 1) {
if ((int) $match[1] > 0) {
return (int) $match[1];
}
}
if (function_exists('shell_exec') && preg_match('#columns = (\d+);#', shell_exec('stty') ?: '', $match) === 1) {
if ((int) $match[1] > 0) {
return (int) $match[1];
}
}
return 80;
}
/**
* @codeCoverageIgnore
*/
private function getNumberOfColumnsWindows(): int
{
$ansicon = getenv('ANSICON');
$columns = 80;
if (is_string($ansicon) && preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim($ansicon), $matches)) {
$columns = (int) $matches[1];
} elseif (function_exists('proc_open')) {
$process = proc_open(
'mode CON',
[
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
],
$pipes,
null,
null,
['suppress_errors' => true]
);
if (is_resource($process)) {
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
$columns = (int) $matches[2];
}
}
}
return $columns - 1;
}
}

View File

@ -0,0 +1,53 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/environment.
*
* (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\Environment;
use const DIRECTORY_SEPARATOR;
use const PHP_OS;
use const PHP_OS_FAMILY;
use function defined;
final class OperatingSystem
{
/**
* Returns PHP_OS_FAMILY (if defined (which it is on PHP >= 7.2)).
* Returns a string (compatible with PHP_OS_FAMILY) derived from PHP_OS otherwise.
*/
public function getFamily(): string
{
if (defined('PHP_OS_FAMILY')) {
return PHP_OS_FAMILY;
}
if (DIRECTORY_SEPARATOR === '\\') {
return 'Windows';
}
switch (PHP_OS) {
case 'Darwin':
return 'Darwin';
case 'DragonFly':
case 'FreeBSD':
case 'NetBSD':
case 'OpenBSD':
return 'BSD';
case 'Linux':
return 'Linux';
case 'SunOS':
return 'Solaris';
default:
return 'Unknown';
}
}
}

View File

@ -0,0 +1,321 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/environment.
*
* (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\Environment;
use const PHP_BINARY;
use const PHP_BINDIR;
use const PHP_MAJOR_VERSION;
use const PHP_SAPI;
use const PHP_VERSION;
use function array_map;
use function array_merge;
use function defined;
use function escapeshellarg;
use function explode;
use function extension_loaded;
use function getenv;
use function ini_get;
use function is_readable;
use function parse_ini_file;
use function php_ini_loaded_file;
use function php_ini_scanned_files;
use function phpversion;
use function sprintf;
use function strpos;
/**
* Utility class for HHVM/PHP environment handling.
*/
final class Runtime
{
/**
* @var string
*/
private static $binary;
/**
* Returns true when Xdebug or PCOV is available or
* the runtime used is PHPDBG.
*/
public function canCollectCodeCoverage(): bool
{
return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage();
}
/**
* Returns true when Zend OPcache is loaded, enabled,
* and is configured to discard comments.
*/
public function discardsComments(): bool
{
if (!$this->isOpcacheActive()) {
return false;
}
if (ini_get('opcache.save_comments') !== '0') {
return false;
}
return true;
}
/**
* Returns true when Zend OPcache is loaded, enabled,
* and is configured to perform just-in-time compilation.
*/
public function performsJustInTimeCompilation(): bool
{
if (PHP_MAJOR_VERSION < 8) {
return false;
}
if (!$this->isOpcacheActive()) {
return false;
}
if (strpos(ini_get('opcache.jit'), '0') === 0) {
return false;
}
return true;
}
/**
* Returns the path to the binary of the current runtime.
* Appends ' --php' to the path when the runtime is HHVM.
*/
public function getBinary(): string
{
// HHVM
if (self::$binary === null && $this->isHHVM()) {
// @codeCoverageIgnoreStart
if ((self::$binary = getenv('PHP_BINARY')) === false) {
self::$binary = PHP_BINARY;
}
self::$binary = escapeshellarg(self::$binary) . ' --php' .
' -d hhvm.php7.all=1';
// @codeCoverageIgnoreEnd
}
if (self::$binary === null && PHP_BINARY !== '') {
self::$binary = escapeshellarg(PHP_BINARY);
}
if (self::$binary === null) {
// @codeCoverageIgnoreStart
$possibleBinaryLocations = [
PHP_BINDIR . '/php',
PHP_BINDIR . '/php-cli.exe',
PHP_BINDIR . '/php.exe',
];
foreach ($possibleBinaryLocations as $binary) {
if (is_readable($binary)) {
self::$binary = escapeshellarg($binary);
break;
}
}
// @codeCoverageIgnoreEnd
}
if (self::$binary === null) {
// @codeCoverageIgnoreStart
self::$binary = 'php';
// @codeCoverageIgnoreEnd
}
return self::$binary;
}
public function getNameWithVersion(): string
{
return $this->getName() . ' ' . $this->getVersion();
}
public function getNameWithVersionAndCodeCoverageDriver(): string
{
if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) {
return $this->getNameWithVersion();
}
if ($this->hasPCOV()) {
return sprintf(
'%s with PCOV %s',
$this->getNameWithVersion(),
phpversion('pcov')
);
}
if ($this->hasXdebug()) {
return sprintf(
'%s with Xdebug %s',
$this->getNameWithVersion(),
phpversion('xdebug')
);
}
}
public function getName(): string
{
if ($this->isHHVM()) {
// @codeCoverageIgnoreStart
return 'HHVM';
// @codeCoverageIgnoreEnd
}
if ($this->isPHPDBG()) {
// @codeCoverageIgnoreStart
return 'PHPDBG';
// @codeCoverageIgnoreEnd
}
return 'PHP';
}
public function getVendorUrl(): string
{
if ($this->isHHVM()) {
// @codeCoverageIgnoreStart
return 'http://hhvm.com/';
// @codeCoverageIgnoreEnd
}
return 'https://secure.php.net/';
}
public function getVersion(): string
{
if ($this->isHHVM()) {
// @codeCoverageIgnoreStart
return HHVM_VERSION;
// @codeCoverageIgnoreEnd
}
return PHP_VERSION;
}
/**
* Returns true when the runtime used is PHP and Xdebug is loaded.
*/
public function hasXdebug(): bool
{
return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug');
}
/**
* Returns true when the runtime used is HHVM.
*/
public function isHHVM(): bool
{
return defined('HHVM_VERSION');
}
/**
* Returns true when the runtime used is PHP without the PHPDBG SAPI.
*/
public function isPHP(): bool
{
return !$this->isHHVM() && !$this->isPHPDBG();
}
/**
* Returns true when the runtime used is PHP with the PHPDBG SAPI.
*/
public function isPHPDBG(): bool
{
return PHP_SAPI === 'phpdbg' && !$this->isHHVM();
}
/**
* Returns true when the runtime used is PHP with the PHPDBG SAPI
* and the phpdbg_*_oplog() functions are available (PHP >= 7.0).
*/
public function hasPHPDBGCodeCoverage(): bool
{
return $this->isPHPDBG();
}
/**
* Returns true when the runtime used is PHP with PCOV loaded and enabled.
*/
public function hasPCOV(): bool
{
return $this->isPHP() && extension_loaded('pcov') && ini_get('pcov.enabled');
}
/**
* Parses the loaded php.ini file (if any) as well as all
* additional php.ini files from the additional ini dir for
* a list of all configuration settings loaded from files
* at startup. Then checks for each php.ini setting passed
* via the `$values` parameter whether this setting has
* been changed at runtime. Returns an array of strings
* where each string has the format `key=value` denoting
* the name of a changed php.ini setting with its new value.
*
* @return string[]
*/
public function getCurrentSettings(array $values): array
{
$diff = [];
$files = [];
if ($file = php_ini_loaded_file()) {
$files[] = $file;
}
if ($scanned = php_ini_scanned_files()) {
$files = array_merge(
$files,
array_map(
'trim',
explode(",\n", $scanned)
)
);
}
foreach ($files as $ini) {
$config = parse_ini_file($ini, true);
foreach ($values as $value) {
$set = ini_get($value);
if (empty($set)) {
continue;
}
if ((!isset($config[$value]) || ($set !== $config[$value]))) {
$diff[$value] = sprintf('%s=%s', $value, $set);
}
}
}
return $diff;
}
private function isOpcacheActive(): bool
{
if (!extension_loaded('Zend OPcache')) {
return false;
}
if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ini_get('opcache.enable_cli') === '1') {
return true;
}
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && ini_get('opcache.enable') === '1') {
return true;
}
return false;
}
}

78
vendor/sebastian/exporter/ChangeLog.md vendored Normal file
View File

@ -0,0 +1,78 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [4.0.5] - 2022-09-14
### Fixed
* [#47](https://github.com/sebastianbergmann/exporter/pull/47): Fix `float` export precision
## [4.0.4] - 2021-11-11
### Changed
* [#37](https://github.com/sebastianbergmann/exporter/pull/37): Improve export of closed resources
## [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-26
### Added
* This component is now supported on PHP 8
## [4.0.1] - 2020-06-15
### Changed
* Tests etc. are now ignored for archive exports
## [4.0.0] - 2020-02-07
### Removed
* This component is no longer supported on PHP 7.0, PHP 7.1, and PHP 7.2
## [3.1.5] - 2022-09-14
### Fixed
* [#47](https://github.com/sebastianbergmann/exporter/pull/47): Fix `float` export precision
## [3.1.4] - 2021-11-11
### Changed
* [#38](https://github.com/sebastianbergmann/exporter/pull/38): Improve export of closed resources
## [3.1.3] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.0` to `>=7.0`
## [3.1.2] - 2019-09-14
### Fixed
* [#29](https://github.com/sebastianbergmann/exporter/pull/29): Second parameter for `str_repeat()` must be an integer
### Removed
* Remove HHVM-specific code that is no longer needed
[4.0.5]: https://github.com/sebastianbergmann/exporter/compare/4.0.4...4.0.5
[4.0.4]: https://github.com/sebastianbergmann/exporter/compare/4.0.3...4.0.4
[4.0.3]: https://github.com/sebastianbergmann/exporter/compare/4.0.2...4.0.3
[4.0.2]: https://github.com/sebastianbergmann/exporter/compare/4.0.1...4.0.2
[4.0.1]: https://github.com/sebastianbergmann/exporter/compare/4.0.0...4.0.1
[4.0.0]: https://github.com/sebastianbergmann/exporter/compare/3.1.2...4.0.0
[3.1.5]: https://github.com/sebastianbergmann/exporter/compare/3.1.4...3.1.5
[3.1.4]: https://github.com/sebastianbergmann/exporter/compare/3.1.3...3.1.4
[3.1.3]: https://github.com/sebastianbergmann/exporter/compare/3.1.2...3.1.3
[3.1.2]: https://github.com/sebastianbergmann/exporter/compare/3.1.1...3.1.2

Some files were not shown because too many files have changed in this diff Show More