This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,20 @@
# 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.
## [1.0.3] - 2024-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## [1.0.2] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^5.6 || ^7.0` to `>=5.6`
## 1.0.0 - 2016-02-13
* Initial release
[1.0.3]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/1.0.1...1.0.2
@@ -0,0 +1,33 @@
code-unit-reverse-lookup
Copyright (c) 2016-2017, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Sebastian Bergmann nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,14 @@
# 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
@@ -0,0 +1,28 @@
{
"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"
}
],
"require": {
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "^8.5"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}
@@ -0,0 +1,111 @@
<?php
/*
* This file is part of 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;
/**
* @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];
} else {
return $filename . ':' . $lineNumber;
}
}
private function updateLookupTable()
{
$this->processClassesAndTraits();
$this->processFunctions();
}
private function processClassesAndTraits()
{
foreach (array_merge(get_declared_classes(), get_declared_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()
{
foreach (get_defined_functions()['user'] as $function) {
if (isset($this->processedFunctions[$function])) {
continue;
}
$this->processFunctionOrMethod(new \ReflectionFunction($function));
$this->processedFunctions[$function] = true;
}
}
/**
* @param \ReflectionFunctionAbstract $functionOrMethod
*/
private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod)
{
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;
}
}
}
@@ -0,0 +1,80 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [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`
[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
@@ -0,0 +1,33 @@
Comparator
Copyright (c) 2002-2018, 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.
@@ -0,0 +1,37 @@
[![Build Status](https://travis-ci.org/sebastianbergmann/comparator.svg?branch=master)](https://travis-ci.org/sebastianbergmann/comparator)
# 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";
}
```
@@ -0,0 +1,54 @@
{
"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.1",
"sebastian/diff": "^3.0",
"sebastian/exporter": "^3.1"
},
"require-dev": {
"phpunit/phpunit": "^8.5"
},
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/_fixture"
]
},
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
}
}
@@ -0,0 +1,130 @@
<?php
/*
* 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;
/**
* Compares arrays for equality.
*/
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 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 = [])
{
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));
}
}
@@ -0,0 +1,61 @@
<?php
/*
* 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)
{
$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);
}
@@ -0,0 +1,128 @@
<?php
/*
* 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\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();
}
}
@@ -0,0 +1,86 @@
<?php
/*
* 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 DOMDocument;
use DOMNode;
/**
* 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 = [])
{
$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;
@$document->loadXML($node->C14N());
$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;
}
}
@@ -0,0 +1,86 @@
<?php
/*
* 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;
/**
* 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 = [])
{
/** @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';
}
}
@@ -0,0 +1,58 @@
<?php
/*
* 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;
/**
* Compares doubles for equality.
*
* @deprecated since v3.0.5 and v4.0.8
*/
class DoubleComparator extends NumericComparator
{
/**
* Smallest value available in PHP.
*
* @var float
*/
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)
{
if ($delta == 0) {
$delta = self::EPSILON;
}
parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase);
}
}
@@ -0,0 +1,52 @@
<?php
/*
* 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;
/**
* 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;
}
}
@@ -0,0 +1,137 @@
<?php
/*
* 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;
/**
* 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;
}
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;
}
}
}
/**
* 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)
{
\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)
{
foreach ($this->customComparators as $key => $_comparator) {
if ($comparator === $_comparator) {
unset($this->customComparators[$key]);
}
}
}
/**
* Unregisters all non-default comparators.
*/
public function reset()
{
$this->customComparators = [];
}
private function registerDefaultComparators()
{
$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)
{
$this->defaultComparators[] = $comparator;
$comparator->setFactory($this);
}
}
@@ -0,0 +1,47 @@
<?php
/*
* 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;
/**
* 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 \PHPUnit_Framework_MockObject_MockObject || $expected instanceof \PHPUnit\Framework\MockObject\MockObject) &&
($actual instanceof \PHPUnit_Framework_MockObject_MockObject || $actual instanceof \PHPUnit\Framework\MockObject\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;
}
}
@@ -0,0 +1,66 @@
<?php
/*
* 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;
/**
* 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)
{
if (\is_infinite($actual) && \is_infinite($expected)) {
return; // @codeCoverageIgnore
}
if ((\is_infinite($actual) xor \is_infinite($expected)) ||
(\is_nan($actual) || \is_nan($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)
)
);
}
}
}
@@ -0,0 +1,106 @@
<?php
/*
* 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;
/**
* 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 = [])
{
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);
}
}
@@ -0,0 +1,52 @@
<?php
/*
* 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;
/**
* 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)
{
if ($actual != $expected) {
throw new ComparisonFailure(
$expected,
$actual,
$this->exporter->export($expected),
$this->exporter->export($actual)
);
}
}
}
@@ -0,0 +1,91 @@
<?php
/*
* 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;
/**
* 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)
{
$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)
)
);
}
}
}
@@ -0,0 +1,69 @@
<?php
/*
* 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;
/**
* 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)
{
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.'
);
}
}
}
}
@@ -0,0 +1,59 @@
<?php
/*
* 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;
/**
* 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)
{
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)
)
);
}
}
}
@@ -0,0 +1,80 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [3.0.6] - 2023-03-02
### Changed
* Do not use implicitly nullable parameters
## [3.0.5] - 2023-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## [3.0.4] - 2023-05-07
### Changed
* [#118](https://github.com/sebastianbergmann/diff/pull/118): Improve performance of `MemoryEfficientLongestCommonSubsequenceCalculator`
* [#119](https://github.com/sebastianbergmann/diff/pull/119): Improve performance of `TimeEfficientLongestCommonSubsequenceCalculator`
## [3.0.3] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.1` to `>=7.1`
## [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
* Fixed [#70](https://github.com/sebastianbergmann/diff/issues/70): Diffing of arrays no longer works
## [2.0.1] - 2017-08-03
### Fixed
* Fixed [#66](https://github.com/sebastianbergmann/diff/pull/66): Restored backwards compatibility for PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3
## [2.0.0] - 2017-07-11 [YANKED]
### Added
* Implemented [#64](https://github.com/sebastianbergmann/diff/pull/64): Show line numbers for chunks of a diff
### Removed
* This component is no longer supported on PHP 5.6
[3.0.6]: https://github.com/sebastianbergmann/diff/compare/3.0.5...3.0.6
[3.0.5]: https://github.com/sebastianbergmann/diff/compare/3.0.4...3.0.5
[3.0.4]: https://github.com/sebastianbergmann/diff/compare/3.0.3...3.0.4
[3.0.3]: https://github.com/sebastianbergmann/diff/compare/3.0.2...3.0.3
[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
@@ -0,0 +1,33 @@
sebastian/diff
Copyright (c) 2002-2019, 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.
@@ -0,0 +1,195 @@
# sebastian/diff
Diff implementation for PHP, factored out of PHPUnit into a stand-alone component.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/diff
If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency:
composer require --dev sebastian/diff
### Usage
#### Generating diff
The `Differ` class can be used to generate a textual representation of the difference between two strings:
```php
<?php
use SebastianBergmann\Diff\Differ;
$differ = new Differ;
print $differ->diff('foo', 'bar');
```
The code above yields the output below:
```diff
--- Original
+++ New
@@ @@
-foo
+bar
```
There are three output builders available in this package:
#### UnifiedDiffOutputBuilder
This is default builder, which generates the output close to udiff and is used by PHPUnit.
```php
<?php
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
$builder = new UnifiedDiffOutputBuilder(
"--- Original\n+++ New\n", // custom header
false // do not add line numbers to the diff
);
$differ = new Differ($builder);
print $differ->diff('foo', 'bar');
```
#### StrictUnifiedDiffOutputBuilder
Generates (strict) Unified diff's (unidiffs) with hunks,
similar to `diff -u` and compatible with `patch` and `git apply`.
```php
<?php
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder;
$builder = new StrictUnifiedDiffOutputBuilder([
'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1`
'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed)
'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3
'fromFile' => 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'));
)
)
)
)
)
)
@@ -0,0 +1,39 @@
{
"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"
}
],
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.0",
"symfony/process": "^2 || ^3.3 || ^4"
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/"
]
},
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
}
}
@@ -0,0 +1,90 @@
<?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;
}
}
@@ -0,0 +1,67 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
final class Diff
{
/**
* @var string
*/
private $from;
/**
* @var string
*/
private $to;
/**
* @var Chunk[]
*/
private $chunks;
/**
* @param string $from
* @param string $to
* @param Chunk[] $chunks
*/
public function __construct(string $from, string $to, array $chunks = [])
{
$this->from = $from;
$this->to = $to;
$this->chunks = $chunks;
}
public function getFrom(): string
{
return $this->from;
}
public function getTo(): string
{
return $this->to;
}
/**
* @return Chunk[]
*/
public function getChunks(): array
{
return $this->chunks;
}
/**
* @param Chunk[] $chunks
*/
public function setChunks(array $chunks): void
{
$this->chunks = $chunks;
}
}
@@ -0,0 +1,330 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
/**
* Diff implementation.
*/
final class Differ
{
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
* @param null|LongestCommonSubsequenceCalculator $lcs
*
* @return string
*/
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
*
* @return array
*/
public function diffToArray($from, $to, ?LongestCommonSubsequenceCalculator $lcs = null): array
{
if (\is_string($from)) {
$from = $this->splitStringByLines($from);
} elseif (!\is_array($from)) {
throw new InvalidArgumentException('"from" must be an array or string.');
}
if (\is_string($to)) {
$to = $this->splitStringByLines($to);
} elseif (!\is_array($to)) {
throw new InvalidArgumentException('"to" must be an array or string.');
}
[$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.
*
* @param mixed $input
*
* @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.
*
* @param string $input
*
* @return array
*/
private function splitStringByLines(string $input): array
{
return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
}
/**
* @param array $from
* @param array $to
*
* @return LongestCommonSubsequenceCalculator
*/
private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator
{
// We do not want to use the time-efficient implementation if its memory
// footprint will probably exceed this value. Note that the footprint
// calculation is only an estimation for the matrix and the LCS method
// will typically allocate a bit more memory than this.
$memoryLimit = 100 * 1024 * 1024;
if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) {
return new MemoryEfficientLongestCommonSubsequenceCalculator;
}
return new TimeEfficientLongestCommonSubsequenceCalculator;
}
/**
* Calculates the estimated memory footprint for the DP-based method.
*
* @param array $from
* @param array $to
*
* @return 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.
*
* @param array $diff
*
* @return bool
*/
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];
}
}
@@ -0,0 +1,40 @@
<?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 ConfigurationException extends InvalidArgumentException
{
/**
* @param string $option
* @param string $expected
* @param mixed $value
* @param int $code
* @param null|\Exception $previous
*/
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
);
}
}
@@ -0,0 +1,15 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
interface Exception
{
}
@@ -0,0 +1,15 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
@@ -0,0 +1,44 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
final class Line
{
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;
}
}
@@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
interface LongestCommonSubsequenceCalculator
{
/**
* Calculates the longest common subsequence of two arrays.
*
* @param array $from
* @param array $to
*
* @return array
*/
public function calculate(array $from, array $to): array;
}
@@ -0,0 +1,86 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator
{
/**
* {@inheritdoc}
*/
public function calculate(array $from, array $to): array
{
$cFrom = \count($from);
$cTo = \count($to);
if ($cFrom === 0) {
return [];
}
if ($cFrom === 1) {
if (\in_array($from[0], $to, true)) {
return [$from[0]];
}
return [];
}
$i = (int) ($cFrom / 2);
$fromStart = \array_slice($from, 0, $i);
$fromEnd = \array_slice($from, $i);
$llB = $this->length($fromStart, $to);
$llE = $this->length(\array_reverse($fromEnd), \array_reverse($to));
$jMax = 0;
$max = 0;
for ($j = 0; $j <= $cTo; $j++) {
$m = $llB[$j] + $llE[$cTo - $j];
if ($m >= $max) {
$max = $m;
$jMax = $j;
}
}
$toStart = \array_slice($to, 0, $jMax);
$toEnd = \array_slice($to, $jMax);
return \array_merge(
$this->calculate($fromStart, $toStart),
$this->calculate($fromEnd, $toEnd)
);
}
private function length(array $from, array $to): array
{
$current = \array_fill(0, \count($to) + 1, 0);
$cFrom = \count($from);
$cTo = \count($to);
for ($i = 0; $i < $cFrom; $i++) {
$prev = $current;
for ($j = 0; $j < $cTo; $j++) {
if ($from[$i] === $to[$j]) {
$current[$j + 1] = $prev[$j] + 1;
} else {
// don't use max() to avoid function call overhead
if ($current[$j] > $prev[$j + 1]) {
$current[$j + 1] = $current[$j];
} else {
$current[$j + 1] = $prev[$j + 1];
}
}
}
}
return $current;
}
}
@@ -0,0 +1,56 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff\Output;
abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface
{
/**
* Takes input of the diff array and returns the common parts.
* Iterates through diff line by line.
*
* @param array $diff
* @param int $lineThreshold
*
* @return array
*/
protected function getCommonChunks(array $diff, int $lineThreshold = 5): array
{
$diffSize = \count($diff);
$capturing = false;
$chunkStart = 0;
$chunkSize = 0;
$commonChunks = [];
for ($i = 0; $i < $diffSize; ++$i) {
if ($diff[$i][1] === 0 /* OLD */) {
if ($capturing === false) {
$capturing = true;
$chunkStart = $i;
$chunkSize = 0;
} else {
++$chunkSize;
}
} elseif ($capturing !== false) {
if ($chunkSize >= $lineThreshold) {
$commonChunks[$chunkStart] = $chunkStart + $chunkSize;
}
$capturing = false;
}
}
if ($capturing !== false && $chunkSize >= $lineThreshold) {
$commonChunks[$chunkStart] = $chunkStart + $chunkSize;
}
return $commonChunks;
}
}
@@ -0,0 +1,68 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff\Output;
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;
}
}
@@ -0,0 +1,20 @@
<?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;
}
@@ -0,0 +1,318 @@
<?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 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']);
}
foreach (['fromFile', 'toFile'] as $option) {
if (!\is_string($options[$option])) {
throw new ConfigurationException($option, 'a string', $options[$option]);
}
}
foreach (['fromFileDate', 'toFileDate'] as $option) {
if (null !== $options[$option] && !\is_string($options[$option])) {
throw new ConfigurationException($option, 'a string or <null>', $options[$option]);
}
}
$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;
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
//}
}
}
}
@@ -0,0 +1,264 @@
<?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 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 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;
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]);
}
}
}
}
@@ -0,0 +1,106 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
/**
* Unified diff parser.
*/
final class Parser
{
/**
* @param string $string
*
* @return Diff[]
*/
public function parse(string $string): array
{
$lines = \preg_split('(\r\n|\r|\n)', $string);
if (!empty($lines) && $lines[\count($lines) - 1] === '') {
\array_pop($lines);
}
$lineCount = \count($lines);
$diffs = [];
$diff = null;
$collected = [];
for ($i = 0; $i < $lineCount; ++$i) {
if (\preg_match('(^---\\s+(?P<file>\\S+))', $lines[$i], $fromMatch) &&
\preg_match('(^\\+\\+\\+\\s+(?P<file>\\S+))', $lines[$i + 1], $toMatch)) {
if ($diff !== null) {
$this->parseFileDiff($diff, $collected);
$diffs[] = $diff;
$collected = [];
}
$diff = new Diff($fromMatch['file'], $toMatch['file']);
++$i;
} else {
if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) {
continue;
}
$collected[] = $lines[$i];
}
}
if ($diff !== null && \count($collected)) {
$this->parseFileDiff($diff, $collected);
$diffs[] = $diff;
}
return $diffs;
}
private function parseFileDiff(Diff $diff, array $lines): void
{
$chunks = [];
$chunk = null;
foreach ($lines as $line) {
if (\preg_match('/^@@\s+-(?P<start>\d+)(?:,\s*(?P<startrange>\d+))?\s+\+(?P<end>\d+)(?:,\s*(?P<endrange>\d+))?\s+@@/', $line, $match)) {
$chunk = new Chunk(
(int) $match['start'],
isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1,
(int) $match['end'],
isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1
);
$chunks[] = $chunk;
$diffLines = [];
continue;
}
if (\preg_match('/^(?P<type>[+ -])?(?P<line>.*)/', $line, $match)) {
$type = Line::UNCHANGED;
if ($match['type'] === '+') {
$type = Line::ADDED;
} elseif ($match['type'] === '-') {
$type = Line::REMOVED;
}
$diffLines[] = new Line($type, $match['line']);
if (null !== $chunk) {
$chunk->setLines($diffLines);
}
}
}
$diff->setChunks($chunks);
}
}
@@ -0,0 +1,78 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/diff.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Diff;
final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator
{
/**
* {@inheritdoc}
*/
public function calculate(array $from, array $to): array
{
$common = [];
$fromLength = \count($from);
$toLength = \count($to);
$width = $fromLength + 1;
$matrix = new \SplFixedArray($width * ($toLength + 1));
for ($i = 0; $i <= $fromLength; ++$i) {
$matrix[$i] = 0;
}
for ($j = 0; $j <= $toLength; ++$j) {
$matrix[$j * $width] = 0;
}
for ($i = 1; $i <= $fromLength; ++$i) {
for ($j = 1; $j <= $toLength; ++$j) {
$o = ($j * $width) + $i;
// don't use max() to avoid function call overhead
$firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0;
if ($matrix[$o - 1] > $matrix[$o - $width]) {
if ($firstOrLast > $matrix[$o - 1]) {
$matrix[$o] = $firstOrLast;
} else {
$matrix[$o] = $matrix[$o - 1];
}
} else {
if ($firstOrLast > $matrix[$o - $width]) {
$matrix[$o] = $firstOrLast;
} else {
$matrix[$o] = $matrix[$o - $width];
}
}
}
}
$i = $fromLength;
$j = $toLength;
while ($i > 0 && $j > 0) {
if ($from[$i - 1] === $to[$j - 1]) {
$common[] = $from[$i - 1];
--$i;
--$j;
} else {
$o = ($j * $width) + $i;
if ($matrix[$o - $width] > $matrix[$o - 1]) {
--$j;
} else {
--$i;
}
}
}
return \array_reverse($common);
}
}
@@ -0,0 +1,132 @@
# Changes in sebastianbergmann/environment
All notable changes in `sebastianbergmann/environment` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [4.2.5] - 2024-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## [4.2.4] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.1` to `>=7.1`
## [4.2.3] - 2019-11-20
### Changed
* Implemented [#50](https://github.com/sebastianbergmann/environment/pull/50): Windows improvements to console capabilities
### Fixed
* 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
* 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
* Implemented [#36](https://github.com/sebastianbergmann/environment/pull/36): `Runtime::getCurrentSettings()`
## [4.1.0] - 2019-02-01
### Added
* Implemented `Runtime::getNameWithVersionAndCodeCoverageDriver()` method
* Implemented [#34](https://github.com/sebastianbergmann/environment/pull/34): Support for PCOV extension
## [4.0.2] - 2019-01-28
### Fixed
* 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
* Fixed [#31](https://github.com/sebastianbergmann/environment/issues/31): Regressions in `Console` class
## [4.0.0] - 2018-10-23 [YANKED]
### Fixed
* 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
* Implemented [#21](https://github.com/sebastianbergmann/environment/issues/21): Equivalent of `PHP_OS_FAMILY` (for PHP < 7.2)
## [3.0.4] - 2017-06-20
### Fixed
* Fixed [#20](https://github.com/sebastianbergmann/environment/pull/20): PHP 7 mode of HHVM not forced
## [3.0.3] - 2017-05-18
### Fixed
* 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
* 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
[4.2.5]: https://github.com/sebastianbergmann/phpunit/compare/4.2.4...4.2.5
[4.2.4]: https://github.com/sebastianbergmann/phpunit/compare/4.2.3...4.2.4
[4.2.3]: https://github.com/sebastianbergmann/phpunit/compare/4.2.2...4.2.3
[4.2.2]: https://github.com/sebastianbergmann/phpunit/compare/4.2.1...4.2.2
[4.2.1]: https://github.com/sebastianbergmann/phpunit/compare/4.2.0...4.2.1
[4.2.0]: https://github.com/sebastianbergmann/phpunit/compare/4.1.0...4.2.0
[4.1.0]: https://github.com/sebastianbergmann/phpunit/compare/4.0.2...4.1.0
[4.0.2]: https://github.com/sebastianbergmann/phpunit/compare/4.0.1...4.0.2
[4.0.1]: https://github.com/sebastianbergmann/phpunit/compare/66691f8e2dc4641909166b275a9a4f45c0e89092...4.0.1
[4.0.0]: https://github.com/sebastianbergmann/phpunit/compare/3.1.0...66691f8e2dc4641909166b275a9a4f45c0e89092
[3.1.0]: https://github.com/sebastianbergmann/phpunit/compare/3.0...3.1.0
[3.0.4]: https://github.com/sebastianbergmann/phpunit/compare/3.0.3...3.0.4
[3.0.3]: https://github.com/sebastianbergmann/phpunit/compare/3.0.2...3.0.3
[3.0.2]: https://github.com/sebastianbergmann/phpunit/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/sebastianbergmann/phpunit/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/sebastianbergmann/phpunit/compare/2.0...3.0.0
@@ -0,0 +1,33 @@
sebastian/environment
Copyright (c) 2014-2019, 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.
@@ -0,0 +1,17 @@
# sebastian/environment
This component provides functionality that helps writing PHP code that has runtime-specific (PHP / HHVM) execution paths.
[![Latest Stable Version](https://img.shields.io/packagist/v/sebastian/environment.svg?style=flat-square)](https://packagist.org/packages/sebastian/environment)
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.1-8892BF.svg?style=flat-square)](https://php.net/)
[![Build Status](https://travis-ci.org/sebastianbergmann/environment.svg?branch=master)](https://travis-ci.org/sebastianbergmann/environment)
## 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
@@ -0,0 +1,37 @@
{
"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": {
"optimize-autoloader": true,
"sort-packages": true
},
"prefer-stable": true,
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5"
},
"suggest": {
"ext-posix": "*"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "4.2-dev"
}
}
}
@@ -0,0 +1,164 @@
<?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;
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;
}
$stat = @\fstat(\STDOUT);
// Check if formatted mode is S_IFCHR
return $stat ? 0020000 === ($stat['mode'] & 0170000) : 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 = $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 = $matches[2];
}
}
}
return $columns - 1;
}
}
@@ -0,0 +1,48 @@
<?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;
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';
}
}
}
@@ -0,0 +1,265 @@
<?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;
/**
* 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 (!\extension_loaded('Zend OPcache')) {
return false;
}
if (\ini_get('opcache.save_comments') !== '0') {
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;
}
/**
* 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->hasXdebug()) {
return \sprintf(
'%s with Xdebug %s',
$this->getNameWithVersion(),
\phpversion('xdebug')
);
}
if ($this->hasPCOV()) {
return \sprintf(
'%s with PCOV %s',
$this->getNameWithVersion(),
\phpversion('pcov')
);
}
}
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 'https://hhvm.com/';
// @codeCoverageIgnoreEnd
}
return 'https://www.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 (isset($config[$value]) && $set != $config[$value]) {
$diff[] = \sprintf('%s=%s', $value, $set);
}
}
}
return $diff;
}
}
@@ -0,0 +1,47 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [3.1.6] - 2024-03-02
### Changed
* Do not use implicitly nullable parameters
### Removed
* This component is no longer supported on PHP 7.0 and PHP 7.1
## [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
[3.1.6]: https://github.com/sebastianbergmann/exporter/compare/3.1.5...3.1.6
[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
@@ -0,0 +1,33 @@
Exporter
Copyright (c) 2002-2021, 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.
@@ -0,0 +1,169 @@
Exporter
========
This component provides the functionality to export PHP variables for visualization.
## Usage
Exporting:
```php
<?php
use SebastianBergmann\Exporter\Exporter;
$exporter = new Exporter;
/*
Exception Object &0000000078de0f0d000000002003a261 (
'message' => ''
'string' => ''
'code' => 0
'file' => '/home/sebastianbergmann/test.php'
'line' => 34
'previous' => null
)
*/
print $exporter->export(new Exception);
```
## Data Types
Exporting simple types:
```php
<?php
use SebastianBergmann\Exporter\Exporter;
$exporter = new Exporter;
// 46
print $exporter->export(46);
// 4.0
print $exporter->export(4.0);
// 'hello, world!'
print $exporter->export('hello, world!');
// false
print $exporter->export(false);
// NAN
print $exporter->export(acos(8));
// -INF
print $exporter->export(log(0));
// null
print $exporter->export(null);
// resource(13) of type (stream)
print $exporter->export(fopen('php://stderr', 'w'));
// Binary String: 0x000102030405
print $exporter->export(chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5));
```
Exporting complex types:
```php
<?php
use SebastianBergmann\Exporter\Exporter;
$exporter = new Exporter;
/*
Array &0 (
0 => Array &1 (
0 => 1
1 => 2
2 => 3
)
1 => Array &2 (
0 => ''
1 => 0
2 => false
)
)
*/
print $exporter->export(array(array(1,2,3), array("",0,FALSE)));
/*
Array &0 (
'self' => Array &1 (
'self' => Array &1
)
)
*/
$array = array();
$array['self'] = &$array;
print $exporter->export($array);
/*
stdClass Object &0000000003a66dcc0000000025e723e2 (
'self' => stdClass Object &0000000003a66dcc0000000025e723e2
)
*/
$obj = new stdClass();
$obj->self = $obj;
print $exporter->export($obj);
```
Compact exports:
```php
<?php
use SebastianBergmann\Exporter\Exporter;
$exporter = new Exporter;
// Array ()
print $exporter->shortenedExport(array());
// Array (...)
print $exporter->shortenedExport(array(1,2,3,4,5));
// stdClass Object ()
print $exporter->shortenedExport(new stdClass);
// Exception Object (...)
print $exporter->shortenedExport(new Exception);
// this\nis\na\nsuper\nlong\nstring\nt...\nspace
print $exporter->shortenedExport(
<<<LONG_STRING
this
is
a
super
long
string
that
wraps
a
lot
and
eats
up
a
lot
of
space
LONG_STRING
);
```
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/exporter
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/exporter
@@ -0,0 +1,53 @@
{
"name": "sebastian/exporter",
"description": "Provides the functionality to export PHP variables for visualization",
"keywords": ["exporter","export"],
"homepage": "http://www.github.com/sebastianbergmann/exporter",
"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": "Adam Harvey",
"email": "aharvey@php.net"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"prefer-stable": true,
"require": {
"php": ">=7.2",
"sebastian/recursion-context": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^8.5",
"ext-mbstring": "*"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "3.1.x-dev"
}
}
}
@@ -0,0 +1,380 @@
<?php declare(strict_types=1);
/*
* This file is part of exporter package.
*
* (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\Exporter;
use SebastianBergmann\RecursionContext\Context;
/**
* A nifty utility for visualizing PHP variables.
*
* <code>
* <?php
* use SebastianBergmann\Exporter\Exporter;
*
* $exporter = new Exporter;
* print $exporter->export(new Exception);
* </code>
*/
class Exporter
{
/**
* Exports a value as a string
*
* The output of this method is similar to the output of print_r(), but
* improved in various aspects:
*
* - NULL is rendered as "null" (instead of "")
* - TRUE is rendered as "true" (instead of "1")
* - FALSE is rendered as "false" (instead of "")
* - Strings are always quoted with single quotes
* - Carriage returns and newlines are normalized to \n
* - Recursion and repeated rendering is treated properly
*
* @param int $indentation The indentation level of the 2nd+ line
*
* @return string
*/
public function export($value, $indentation = 0)
{
return $this->recursiveExport($value, $indentation);
}
/**
* @param array<mixed> $data
* @param Context $context
*
* @return string
*/
public function shortenedRecursiveExport(&$data, ?Context $context = null)
{
$result = [];
$exporter = new self();
if (!$context) {
$context = new Context;
}
$array = $data;
$context->add($data);
foreach ($array as $key => $value) {
if (\is_array($value)) {
if ($context->contains($data[$key]) !== false) {
$result[] = '*RECURSION*';
} else {
$result[] = \sprintf(
'array(%s)',
$this->shortenedRecursiveExport($data[$key], $context)
);
}
} else {
$result[] = $exporter->shortenedExport($value);
}
}
return \implode(', ', $result);
}
/**
* Exports a value into a single-line string
*
* The output of this method is similar to the output of
* SebastianBergmann\Exporter\Exporter::export().
*
* Newlines are replaced by the visible string '\n'.
* Contents of arrays and objects (if any) are replaced by '...'.
*
* @return string
*
* @see SebastianBergmann\Exporter\Exporter::export
*/
public function shortenedExport($value)
{
if (\is_string($value)) {
$string = \str_replace("\n", '', $this->export($value));
if (\function_exists('mb_strlen')) {
if (\mb_strlen($string) > 40) {
$string = \mb_substr($string, 0, 30) . '...' . \mb_substr($string, -7);
}
} else {
if (\strlen($string) > 40) {
$string = \substr($string, 0, 30) . '...' . \substr($string, -7);
}
}
return $string;
}
if (\is_object($value)) {
return \sprintf(
'%s Object (%s)',
\get_class($value),
\count($this->toArray($value)) > 0 ? '...' : ''
);
}
if (\is_array($value)) {
return \sprintf(
'Array (%s)',
\count($value) > 0 ? '...' : ''
);
}
return $this->export($value);
}
/**
* Converts an object to an array containing all of its private, protected
* and public properties.
*
* @return array
*/
public function toArray($value)
{
if (!\is_object($value)) {
return (array) $value;
}
$array = [];
foreach ((array) $value as $key => $val) {
// Exception traces commonly reference hundreds to thousands of
// objects currently loaded in memory. Including them in the result
// has a severe negative performance impact.
if ("\0Error\0trace" === $key || "\0Exception\0trace" === $key) {
continue;
}
// properties are transformed to keys in the following way:
// private $property => "\0Classname\0property"
// protected $property => "\0*\0property"
// public $property => "property"
if (\preg_match('/^\0.+\0(.+)$/', (string) $key, $matches)) {
$key = $matches[1];
}
// See https://github.com/php/php-src/commit/5721132
if ($key === "\0gcdata") {
continue;
}
$array[$key] = $val;
}
// Some internal classes like SplObjectStorage don't work with the
// above (fast) mechanism nor with reflection in Zend.
// Format the output similarly to print_r() in this case
if ($value instanceof \SplObjectStorage) {
foreach ($value as $key => $val) {
$array[\spl_object_hash($val)] = [
'obj' => $val,
'inf' => $value->getInfo(),
];
}
}
return $array;
}
/**
* Recursive implementation of export
*
* @param mixed $value The value to export
* @param int $indentation The indentation level of the 2nd+ line
* @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects
*
* @return string
*
* @see SebastianBergmann\Exporter\Exporter::export
*/
protected function recursiveExport(&$value, $indentation, $processed = null)
{
if ($value === null) {
return 'null';
}
if ($value === true) {
return 'true';
}
if ($value === false) {
return 'false';
}
if (\is_float($value)) {
$precisionBackup = \ini_get('precision');
\ini_set('precision', '-1');
try {
$valueStr = (string) $value;
if ((string) (int) $value === $valueStr) {
return $valueStr . '.0';
}
return $valueStr;
} finally {
\ini_set('precision', $precisionBackup);
}
}
if ($this->isClosedResource($value)) {
return 'resource (closed)';
}
if (\is_resource($value)) {
return \sprintf(
'resource(%d) of type (%s)',
$value,
\get_resource_type($value)
);
}
if (\is_string($value)) {
// Match for most non printable chars somewhat taking multibyte chars into account
if (\preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) {
return 'Binary String: 0x' . \bin2hex($value);
}
return "'" .
\str_replace(
'<lf>',
"\n",
\str_replace(
["\r\n", "\n\r", "\r", "\n"],
['\r\n<lf>', '\n\r<lf>', '\r<lf>', '\n<lf>'],
$value
)
) .
"'";
}
$whitespace = \str_repeat(' ', (int)(4 * $indentation));
if (!$processed) {
$processed = new Context;
}
if (\is_array($value)) {
if (($key = $processed->contains($value)) !== false) {
return 'Array &' . $key;
}
$array = $value;
$key = $processed->add($value);
$values = '';
if (\count($array) > 0) {
foreach ($array as $k => $v) {
$values .= \sprintf(
'%s %s => %s' . "\n",
$whitespace,
$this->recursiveExport($k, $indentation),
$this->recursiveExport($value[$k], $indentation + 1, $processed)
);
}
$values = "\n" . $values . $whitespace;
}
return \sprintf('Array &%s (%s)', $key, $values);
}
if (\is_object($value)) {
$class = \get_class($value);
if ($hash = $processed->contains($value)) {
return \sprintf('%s Object &%s', $class, $hash);
}
$hash = $processed->add($value);
$values = '';
$array = $this->toArray($value);
if (\count($array) > 0) {
foreach ($array as $k => $v) {
$values .= \sprintf(
'%s %s => %s' . "\n",
$whitespace,
$this->recursiveExport($k, $indentation),
$this->recursiveExport($v, $indentation + 1, $processed)
);
}
$values = "\n" . $values . $whitespace;
}
return \sprintf('%s Object &%s (%s)', $class, $hash, $values);
}
return \var_export($value, true);
}
/**
* Determines whether a variable represents a resource, either open or closed.
*
* @param mixed $actual The variable to test.
*
* @return bool
*/
private function isResource($value)
{
return $value !== null
&& \is_scalar($value) === false
&& \is_array($value) === false
&& \is_object($value) === false;
}
/**
* Determines whether a variable represents a closed resource.
*
* @param mixed $actual The variable to test.
*
* @return bool
*/
private function isClosedResource($value)
{
/*
* PHP 7.2 introduced "resource (closed)".
*/
if (\gettype($value) === 'resource (closed)') {
return true;
}
/*
* If gettype did not work, attempt to determine whether this is
* a closed resource in another way.
*/
$isResource = \is_resource($value);
$isNotNonResource = $this->isResource($value);
if ($isResource === false && $isNotNonResource === true) {
return true;
}
if ($isNotNonResource === true) {
try {
$resourceType = @\get_resource_type($value);
if ($resourceType === 'Unknown') {
return true;
}
} catch (TypeError $e) {
// Ignore. Not a resource.
} catch (Exception $e) {
// Ignore. Not a resource.
}
}
return false;
}
}
@@ -0,0 +1,49 @@
# Changes in sebastian/global-state
All notable changes in `sebastian/global-state` are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [3.0.5] - 2024-03-02
### Changed
* Do not use implicitly nullable parameters
## [3.0.4] - 2024-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## [3.0.3] - 2023-08-02
### Changed
* Changed usage of `ReflectionProperty::setValue()` to be compatible with PHP 8.3
## [3.0.2] - 2022-02-10
### Fixed
* The `$includeTraits` parameter of `SebastianBergmann\GlobalState\Snapshot::__construct()` is not respected
## [3.0.1] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.2` to `>=7.2`
## [3.0.0] - 2019-02-01
### Changed
* `Snapshot::canBeSerialized()` now recursively checks arrays and object graphs for variables that cannot be serialized
### Removed
* This component is no longer supported on PHP 7.0 and PHP 7.1
[3.0.5]: https://github.com/sebastianbergmann/phpunit/compare/3.0.4...3.0.5
[3.0.4]: https://github.com/sebastianbergmann/phpunit/compare/3.0.3...3.0.4
[3.0.3]: https://github.com/sebastianbergmann/phpunit/compare/3.0.2...3.0.3
[3.0.2]: https://github.com/sebastianbergmann/phpunit/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/sebastianbergmann/phpunit/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/sebastianbergmann/phpunit/compare/2.0.0...3.0.0
@@ -0,0 +1,33 @@
sebastian/global-state
Copyright (c) 2001-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.
@@ -0,0 +1,16 @@
# global-state
Snapshotting of global state, factored out of PHPUnit into a stand-alone component.
[![Build Status](https://travis-ci.org/sebastianbergmann/global-state.svg?branch=master)](https://travis-ci.org/sebastianbergmann/global-state)
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/global-state
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/global-state
@@ -0,0 +1,48 @@
{
"name": "sebastian/global-state",
"description": "Snapshotting of global state",
"keywords": ["global state"],
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"prefer-stable": true,
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"require": {
"php": ">=7.2",
"sebastian/object-reflector": "^1.1.1",
"sebastian/recursion-context": "^3.0"
},
"require-dev": {
"ext-dom": "*",
"phpunit/phpunit": "^8.0"
},
"suggest": {
"ext-uopz": "*"
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/_fixture/"
],
"files": [
"tests/_fixture/SnapshotFunctions.php"
]
},
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
}
}
@@ -0,0 +1,118 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (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\GlobalState;
/**
* A blacklist for global state elements that should not be snapshotted.
*/
final class Blacklist
{
/**
* @var array
*/
private $globalVariables = [];
/**
* @var string[]
*/
private $classes = [];
/**
* @var string[]
*/
private $classNamePrefixes = [];
/**
* @var string[]
*/
private $parentClasses = [];
/**
* @var string[]
*/
private $interfaces = [];
/**
* @var array
*/
private $staticAttributes = [];
public function addGlobalVariable(string $variableName): void
{
$this->globalVariables[$variableName] = true;
}
public function addClass(string $className): void
{
$this->classes[] = $className;
}
public function addSubclassesOf(string $className): void
{
$this->parentClasses[] = $className;
}
public function addImplementorsOf(string $interfaceName): void
{
$this->interfaces[] = $interfaceName;
}
public function addClassNamePrefix(string $classNamePrefix): void
{
$this->classNamePrefixes[] = $classNamePrefix;
}
public function addStaticAttribute(string $className, string $attributeName): void
{
if (!isset($this->staticAttributes[$className])) {
$this->staticAttributes[$className] = [];
}
$this->staticAttributes[$className][$attributeName] = true;
}
public function isGlobalVariableBlacklisted(string $variableName): bool
{
return isset($this->globalVariables[$variableName]);
}
public function isStaticAttributeBlacklisted(string $className, string $attributeName): bool
{
if (\in_array($className, $this->classes)) {
return true;
}
foreach ($this->classNamePrefixes as $prefix) {
if (\strpos($className, $prefix) === 0) {
return true;
}
}
$class = new \ReflectionClass($className);
foreach ($this->parentClasses as $type) {
if ($class->isSubclassOf($type)) {
return true;
}
}
foreach ($this->interfaces as $type) {
if ($class->implementsInterface($type)) {
return true;
}
}
if (isset($this->staticAttributes[$className][$attributeName])) {
return true;
}
return false;
}
}
@@ -0,0 +1,91 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (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\GlobalState;
/**
* Exports parts of a Snapshot as PHP code.
*/
final class CodeExporter
{
public function constants(Snapshot $snapshot): string
{
$result = '';
foreach ($snapshot->constants() as $name => $value) {
$result .= \sprintf(
'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n",
$name,
$name,
$this->exportVariable($value)
);
}
return $result;
}
public function globalVariables(Snapshot $snapshot): string
{
$result = '$GLOBALS = [];' . \PHP_EOL;
foreach ($snapshot->globalVariables() as $name => $value) {
$result .= \sprintf(
'$GLOBALS[%s] = %s;' . \PHP_EOL,
$this->exportVariable($name),
$this->exportVariable($value)
);
}
return $result;
}
public function iniSettings(Snapshot $snapshot): string
{
$result = '';
foreach ($snapshot->iniSettings() as $key => $value) {
$result .= \sprintf(
'@ini_set(%s, %s);' . "\n",
$this->exportVariable($key),
$this->exportVariable($value)
);
}
return $result;
}
private function exportVariable($variable): string
{
if (\is_scalar($variable) || null === $variable ||
(\is_array($variable) && $this->arrayOnlyContainsScalars($variable))) {
return \var_export($variable, true);
}
return 'unserialize(' . \var_export(\serialize($variable), true) . ')';
}
private function arrayOnlyContainsScalars(array $array): bool
{
$result = true;
foreach ($array as $element) {
if (\is_array($element)) {
$result = $this->arrayOnlyContainsScalars($element);
} elseif (!\is_scalar($element) && null !== $element) {
$result = false;
}
if ($result === false) {
break;
}
}
return $result;
}
}
@@ -0,0 +1,132 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (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\GlobalState;
/**
* Restorer of snapshots of global state.
*/
class Restorer
{
/**
* Deletes function definitions that are not defined in a snapshot.
*
* @throws RuntimeException when the uopz_delete() function is not available
*
* @see https://github.com/krakjoe/uopz
*/
public function restoreFunctions(Snapshot $snapshot): void
{
if (!\function_exists('uopz_delete')) {
throw new RuntimeException('The uopz_delete() function is required for this operation');
}
$functions = \get_defined_functions();
foreach (\array_diff($functions['user'], $snapshot->functions()) as $function) {
uopz_delete($function);
}
}
/**
* Restores all global and super-global variables from a snapshot.
*/
public function restoreGlobalVariables(Snapshot $snapshot): void
{
$superGlobalArrays = $snapshot->superGlobalArrays();
foreach ($superGlobalArrays as $superGlobalArray) {
$this->restoreSuperGlobalArray($snapshot, $superGlobalArray);
}
$globalVariables = $snapshot->globalVariables();
foreach (\array_keys($GLOBALS) as $key) {
if ($key !== 'GLOBALS' &&
!\in_array($key, $superGlobalArrays) &&
!$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) {
if (\array_key_exists($key, $globalVariables)) {
$GLOBALS[$key] = $globalVariables[$key];
} else {
unset($GLOBALS[$key]);
}
}
}
}
/**
* Restores all static attributes in user-defined classes from this snapshot.
*/
public function restoreStaticAttributes(Snapshot $snapshot): void
{
$current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false);
$newClasses = \array_diff($current->classes(), $snapshot->classes());
unset($current);
foreach ($snapshot->staticAttributes() as $className => $staticAttributes) {
foreach ($staticAttributes as $name => $value) {
$reflector = new \ReflectionProperty($className, $name);
$reflector->setAccessible(true);
$reflector->setValue(null, $value);
}
}
foreach ($newClasses as $className) {
$class = new \ReflectionClass($className);
$defaults = $class->getDefaultProperties();
foreach ($class->getProperties() as $attribute) {
if (!$attribute->isStatic()) {
continue;
}
$name = $attribute->getName();
if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) {
continue;
}
if (!isset($defaults[$name])) {
continue;
}
$attribute->setAccessible(true);
$attribute->setValue(null, $defaults[$name]);
}
}
}
/**
* Restores a super-global variable array from this snapshot.
*/
private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray): void
{
$superGlobalVariables = $snapshot->superGlobalVariables();
if (isset($GLOBALS[$superGlobalArray]) &&
\is_array($GLOBALS[$superGlobalArray]) &&
isset($superGlobalVariables[$superGlobalArray])) {
$keys = \array_keys(
\array_merge(
$GLOBALS[$superGlobalArray],
$superGlobalVariables[$superGlobalArray]
)
);
foreach ($keys as $key) {
if (isset($superGlobalVariables[$superGlobalArray][$key])) {
$GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key];
} else {
unset($GLOBALS[$superGlobalArray][$key]);
}
}
}
}
}
@@ -0,0 +1,421 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (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\GlobalState;
use SebastianBergmann\ObjectReflector\ObjectReflector;
use SebastianBergmann\RecursionContext\Context;
/**
* A snapshot of global state.
*/
class Snapshot
{
/**
* @var Blacklist
*/
private $blacklist;
/**
* @var array
*/
private $globalVariables = [];
/**
* @var array
*/
private $superGlobalArrays = [];
/**
* @var array
*/
private $superGlobalVariables = [];
/**
* @var array
*/
private $staticAttributes = [];
/**
* @var array
*/
private $iniSettings = [];
/**
* @var array
*/
private $includedFiles = [];
/**
* @var array
*/
private $constants = [];
/**
* @var array
*/
private $functions = [];
/**
* @var array
*/
private $interfaces = [];
/**
* @var array
*/
private $classes = [];
/**
* @var array
*/
private $traits = [];
/**
* Creates a snapshot of the current global state.
*/
public function __construct(?Blacklist $blacklist = null, bool $includeGlobalVariables = true, bool $includeStaticAttributes = true, bool $includeConstants = true, bool $includeFunctions = true, bool $includeClasses = true, bool $includeInterfaces = true, bool $includeTraits = true, bool $includeIniSettings = true, bool $includeIncludedFiles = true)
{
if ($blacklist === null) {
$blacklist = new Blacklist;
}
$this->blacklist = $blacklist;
if ($includeConstants) {
$this->snapshotConstants();
}
if ($includeFunctions) {
$this->snapshotFunctions();
}
if ($includeClasses || $includeStaticAttributes) {
$this->snapshotClasses();
}
if ($includeInterfaces) {
$this->snapshotInterfaces();
}
if ($includeGlobalVariables) {
$this->setupSuperGlobalArrays();
$this->snapshotGlobals();
}
if ($includeStaticAttributes) {
$this->snapshotStaticAttributes();
}
if ($includeIniSettings) {
$this->iniSettings = \ini_get_all(null, false);
}
if ($includeIncludedFiles) {
$this->includedFiles = \get_included_files();
}
if ($includeTraits) {
$this->traits = \get_declared_traits();
}
}
public function blacklist(): Blacklist
{
return $this->blacklist;
}
public function globalVariables(): array
{
return $this->globalVariables;
}
public function superGlobalVariables(): array
{
return $this->superGlobalVariables;
}
public function superGlobalArrays(): array
{
return $this->superGlobalArrays;
}
public function staticAttributes(): array
{
return $this->staticAttributes;
}
public function iniSettings(): array
{
return $this->iniSettings;
}
public function includedFiles(): array
{
return $this->includedFiles;
}
public function constants(): array
{
return $this->constants;
}
public function functions(): array
{
return $this->functions;
}
public function interfaces(): array
{
return $this->interfaces;
}
public function classes(): array
{
return $this->classes;
}
public function traits(): array
{
return $this->traits;
}
/**
* Creates a snapshot user-defined constants.
*/
private function snapshotConstants(): void
{
$constants = \get_defined_constants(true);
if (isset($constants['user'])) {
$this->constants = $constants['user'];
}
}
/**
* Creates a snapshot user-defined functions.
*/
private function snapshotFunctions(): void
{
$functions = \get_defined_functions();
$this->functions = $functions['user'];
}
/**
* Creates a snapshot user-defined classes.
*/
private function snapshotClasses(): void
{
foreach (\array_reverse(\get_declared_classes()) as $className) {
$class = new \ReflectionClass($className);
if (!$class->isUserDefined()) {
break;
}
$this->classes[] = $className;
}
$this->classes = \array_reverse($this->classes);
}
/**
* Creates a snapshot user-defined interfaces.
*/
private function snapshotInterfaces(): void
{
foreach (\array_reverse(\get_declared_interfaces()) as $interfaceName) {
$class = new \ReflectionClass($interfaceName);
if (!$class->isUserDefined()) {
break;
}
$this->interfaces[] = $interfaceName;
}
$this->interfaces = \array_reverse($this->interfaces);
}
/**
* Creates a snapshot of all global and super-global variables.
*/
private function snapshotGlobals(): void
{
$superGlobalArrays = $this->superGlobalArrays();
foreach ($superGlobalArrays as $superGlobalArray) {
$this->snapshotSuperGlobalArray($superGlobalArray);
}
foreach (\array_keys($GLOBALS) as $key) {
if ($key !== 'GLOBALS' &&
!\in_array($key, $superGlobalArrays) &&
$this->canBeSerialized($GLOBALS[$key]) &&
!$this->blacklist->isGlobalVariableBlacklisted($key)) {
/* @noinspection UnserializeExploitsInspection */
$this->globalVariables[$key] = \unserialize(\serialize($GLOBALS[$key]));
}
}
}
/**
* Creates a snapshot a super-global variable array.
*/
private function snapshotSuperGlobalArray(string $superGlobalArray): void
{
$this->superGlobalVariables[$superGlobalArray] = [];
if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) {
foreach ($GLOBALS[$superGlobalArray] as $key => $value) {
/* @noinspection UnserializeExploitsInspection */
$this->superGlobalVariables[$superGlobalArray][$key] = \unserialize(\serialize($value));
}
}
}
/**
* Creates a snapshot of all static attributes in user-defined classes.
*/
private function snapshotStaticAttributes(): void
{
foreach ($this->classes as $className) {
$class = new \ReflectionClass($className);
$snapshot = [];
foreach ($class->getProperties() as $attribute) {
if ($attribute->isStatic()) {
$name = $attribute->getName();
if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) {
continue;
}
$attribute->setAccessible(true);
$value = $attribute->getValue();
if ($this->canBeSerialized($value)) {
/* @noinspection UnserializeExploitsInspection */
$snapshot[$name] = \unserialize(\serialize($value));
}
}
}
if (!empty($snapshot)) {
$this->staticAttributes[$className] = $snapshot;
}
}
}
/**
* Returns a list of all super-global variable arrays.
*/
private function setupSuperGlobalArrays(): void
{
$this->superGlobalArrays = [
'_ENV',
'_POST',
'_GET',
'_COOKIE',
'_SERVER',
'_FILES',
'_REQUEST',
];
}
private function canBeSerialized($variable): bool
{
if (\is_scalar($variable) || $variable === null) {
return true;
}
if (\is_resource($variable)) {
return false;
}
foreach ($this->enumerateObjectsAndResources($variable) as $value) {
if (\is_resource($value)) {
return false;
}
if (\is_object($value)) {
$class = new \ReflectionClass($value);
if ($class->isAnonymous()) {
return false;
}
try {
@\serialize($value);
} catch (\Throwable $t) {
return false;
}
}
}
return true;
}
private function enumerateObjectsAndResources($variable): array
{
if (isset(\func_get_args()[1])) {
$processed = \func_get_args()[1];
} else {
$processed = new Context;
}
$result = [];
if ($processed->contains($variable)) {
return $result;
}
$array = $variable;
$processed->add($variable);
if (\is_array($variable)) {
foreach ($array as $element) {
if (!\is_array($element) && !\is_object($element) && !\is_resource($element)) {
continue;
}
if (!\is_resource($element)) {
/** @noinspection SlowArrayOperationsInLoopInspection */
$result = \array_merge(
$result,
$this->enumerateObjectsAndResources($element, $processed)
);
} else {
$result[] = $element;
}
}
} else {
$result[] = $variable;
foreach ((new ObjectReflector)->getAttributes($variable) as $value) {
if (!\is_array($value) && !\is_object($value) && !\is_resource($value)) {
continue;
}
if (!\is_resource($value)) {
/** @noinspection SlowArrayOperationsInLoopInspection */
$result = \array_merge(
$result,
$this->enumerateObjectsAndResources($value, $processed)
);
} else {
$result[] = $value;
}
}
}
return $result;
}
}
@@ -0,0 +1,14 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (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\GlobalState;
interface Exception
{
}
@@ -0,0 +1,14 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/global-state.
*
* (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\GlobalState;
final class RuntimeException extends \RuntimeException implements Exception
{
}
@@ -0,0 +1,65 @@
# Change Log
All notable changes to `sebastianbergmann/object-enumerator` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [3.0.5] - 2024-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## [3.0.4] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.0` to `>=7.0`
## [3.0.3] - 2017-08-03
### Changed
* Bumped required version of `sebastian/object-reflector`
## [3.0.2] - 2017-03-12
### Changed
* `sebastian/object-reflector` is now a dependency
## [3.0.1] - 2017-03-12
### Fixed
* Objects aggregated in inherited attributes are not enumerated
## [3.0.0] - 2017-03-03
### Removed
* This component is no longer supported on PHP 5.6
## [2.0.1] - 2017-02-18
### Fixed
* Fixed [#2](https://github.com/sebastianbergmann/phpunit/pull/2): Exceptions in `ReflectionProperty::getValue()` are not handled
## [2.0.0] - 2016-11-19
### Changed
* This component is now compatible with `sebastian/recursion-context: ~1.0.4`
## 1.0.0 - 2016-02-04
### Added
* Initial release
[3.0.5]: https://github.com/sebastianbergmann/object-enumerator/compare/3.0.4...3.0.5
[3.0.4]: https://github.com/sebastianbergmann/object-enumerator/compare/3.0.3...3.0.4
[3.0.3]: https://github.com/sebastianbergmann/object-enumerator/compare/3.0.2...3.0.3
[3.0.2]: https://github.com/sebastianbergmann/object-enumerator/compare/3.0.1...3.0.2
[3.0.1]: https://github.com/sebastianbergmann/object-enumerator/compare/3.0.0...3.0.1
[3.0.0]: https://github.com/sebastianbergmann/object-enumerator/compare/2.0...3.0.0
[2.0.1]: https://github.com/sebastianbergmann/object-enumerator/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/sebastianbergmann/object-enumerator/compare/1.0...2.0.0
@@ -0,0 +1,33 @@
Object Enumerator
Copyright (c) 2016-2017, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Sebastian Bergmann nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,14 @@
# Object Enumerator
Traverses array structures and object graphs to enumerate all referenced objects.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/object-enumerator
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/object-enumerator
@@ -0,0 +1,35 @@
{
"name": "sebastian/object-enumerator",
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"require": {
"php": ">=7.0",
"sebastian/object-reflector": "^1.1.1",
"sebastian/recursion-context": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/_fixture/"
]
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
}
}
@@ -0,0 +1,85 @@
<?php
/*
* This file is part of Object Enumerator.
*
* (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\ObjectEnumerator;
use SebastianBergmann\ObjectReflector\ObjectReflector;
use SebastianBergmann\RecursionContext\Context;
/**
* Traverses array structures and object graphs
* to enumerate all referenced objects.
*/
class Enumerator
{
/**
* Returns an array of all objects referenced either
* directly or indirectly by a variable.
*
* @param array|object $variable
*
* @return object[]
*/
public function enumerate($variable)
{
if (!is_array($variable) && !is_object($variable)) {
throw new InvalidArgumentException;
}
if (isset(func_get_args()[1])) {
if (!func_get_args()[1] instanceof Context) {
throw new InvalidArgumentException;
}
$processed = func_get_args()[1];
} else {
$processed = new Context;
}
$objects = [];
if ($processed->contains($variable)) {
return $objects;
}
$array = $variable;
$processed->add($variable);
if (is_array($variable)) {
foreach ($array as $element) {
if (!is_array($element) && !is_object($element)) {
continue;
}
$objects = array_merge(
$objects,
$this->enumerate($element, $processed)
);
}
} else {
$objects[] = $variable;
$reflector = new ObjectReflector;
foreach ($reflector->getAttributes($variable) as $value) {
if (!is_array($value) && !is_object($value)) {
continue;
}
$objects = array_merge(
$objects,
$this->enumerate($value, $processed)
);
}
}
return $objects;
}
}
@@ -0,0 +1,15 @@
<?php
/*
* This file is part of Object Enumerator.
*
* (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\ObjectEnumerator;
interface Exception
{
}
@@ -0,0 +1,15 @@
<?php
/*
* This file is part of Object Enumerator.
*
* (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\ObjectEnumerator;
class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
@@ -0,0 +1,32 @@
# Change Log
All notable changes to `sebastianbergmann/object-reflector` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## 1.1.3 - 2024-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## 1.1.2 - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.0` to `>=7.1`
## 1.1.1 - 2017-03-29
* Fixed [#1](https://github.com/sebastianbergmann/object-reflector/issues/1): Attributes that with non-string names are not handled correctly
## 1.1.0 - 2017-03-16
### Changed
* Changed implementation of `ObjectReflector::getattributes()` to use `(array)` cast instead of `ReflectionObject`
## 1.0.0 - 2017-03-12
* Initial release
[1.1.3]: https://github.com/sebastianbergmann/object-reflector/compare/1.1.2...1.1.3
[1.1.2]: https://github.com/sebastianbergmann/object-reflector/compare/1.1.1...1.1.2
[1.1.1]: https://github.com/sebastianbergmann/object-reflector/compare/1.1.0...1.1.1
[1.1.0]: https://github.com/sebastianbergmann/object-reflector/compare/1.0.0...1.1.0
@@ -0,0 +1,33 @@
Object Reflector
Copyright (c) 2017, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Sebastian Bergmann nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,14 @@
# Object Reflector
Allows reflection of object attributes, including inherited and non-public ones.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/object-reflector
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/object-reflector
@@ -0,0 +1,33 @@
{
"name": "sebastian/object-reflector",
"description": "Allows reflection of object attributes, including inherited and non-public ones",
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"autoload": {
"classmap": [
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/_fixture/"
]
},
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
}
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of object-reflector.
*
* (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.
*/
declare(strict_types=1);
namespace SebastianBergmann\ObjectReflector;
interface Exception
{
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of object-reflector.
*
* (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.
*/
declare(strict_types=1);
namespace SebastianBergmann\ObjectReflector;
class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
@@ -0,0 +1,51 @@
<?php
/*
* This file is part of object-reflector.
*
* (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.
*/
declare(strict_types=1);
namespace SebastianBergmann\ObjectReflector;
class ObjectReflector
{
/**
* @param object $object
*
* @return array
*
* @throws InvalidArgumentException
*/
public function getAttributes($object): array
{
if (!is_object($object)) {
throw new InvalidArgumentException;
}
$attributes = [];
$className = get_class($object);
foreach ((array) $object as $name => $value) {
$name = explode("\0", (string) $name);
if (count($name) === 1) {
$name = $name[0];
} else {
if ($name[1] !== $className) {
$name = $name[1] . '::' . $name[2];
} else {
$name = $name[2];
}
}
$attributes[$name] = $value;
}
return $attributes;
}
}
@@ -0,0 +1,33 @@
Recursion Context
Copyright (c) 2002-2017, Sebastian Bergmann <sebastian@phpunit.de>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Sebastian Bergmann nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,14 @@
# Recursion Context
...
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/recursion-context
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/recursion-context
@@ -0,0 +1,36 @@
{
"name": "sebastian/recursion-context",
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"autoload": {
"classmap": [
"src/"
]
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
}
}
@@ -0,0 +1,167 @@
<?php
/*
* This file is part of the Recursion Context package.
*
* (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\RecursionContext;
/**
* A context containing previously processed arrays and objects
* when recursively processing a value.
*/
final class Context
{
/**
* @var array[]
*/
private $arrays;
/**
* @var \SplObjectStorage
*/
private $objects;
/**
* Initialises the context
*/
public function __construct()
{
$this->arrays = array();
$this->objects = new \SplObjectStorage;
}
/**
* Adds a value to the context.
*
* @param array|object $value The value to add.
*
* @return int|string The ID of the stored value, either as a string or integer.
*
* @throws InvalidArgumentException Thrown if $value is not an array or object
*/
public function add(&$value)
{
if (is_array($value)) {
return $this->addArray($value);
} elseif (is_object($value)) {
return $this->addObject($value);
}
throw new InvalidArgumentException(
'Only arrays and objects are supported'
);
}
/**
* Checks if the given value exists within the context.
*
* @param array|object $value The value to check.
*
* @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored.
*
* @throws InvalidArgumentException Thrown if $value is not an array or object
*/
public function contains(&$value)
{
if (is_array($value)) {
return $this->containsArray($value);
} elseif (is_object($value)) {
return $this->containsObject($value);
}
throw new InvalidArgumentException(
'Only arrays and objects are supported'
);
}
/**
* @param array $array
*
* @return bool|int
*/
private function addArray(array &$array)
{
$key = $this->containsArray($array);
if ($key !== false) {
return $key;
}
$key = count($this->arrays);
$this->arrays[] = &$array;
if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) {
$array[] = $key;
$array[] = $this->objects;
} else { /* cover the improbable case too */
do {
$key = random_int(PHP_INT_MIN, PHP_INT_MAX);
} while (isset($array[$key]));
$array[$key] = $key;
do {
$key = random_int(PHP_INT_MIN, PHP_INT_MAX);
} while (isset($array[$key]));
$array[$key] = $this->objects;
}
return $key;
}
/**
* @param object $object
*
* @return string
*/
private function addObject($object)
{
if (!$this->objects->contains($object)) {
$this->objects->attach($object);
}
return spl_object_hash($object);
}
/**
* @param array $array
*
* @return int|false
*/
private function containsArray(array &$array)
{
$end = array_slice($array, -2);
return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false;
}
/**
* @param object $value
*
* @return string|false
*/
private function containsObject($value)
{
if ($this->objects->contains($value)) {
return spl_object_hash($value);
}
return false;
}
public function __destruct()
{
foreach ($this->arrays as &$array) {
if (is_array($array)) {
array_pop($array);
array_pop($array);
}
}
}
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of the Recursion Context package.
*
* (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\RecursionContext;
/**
*/
interface Exception
{
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of the Recursion Context package.
*
* (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\RecursionContext;
/**
*/
final class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}
@@ -0,0 +1,38 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [2.0.3] - 2024-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## [2.0.2] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.1` to `>=7.1`
## [2.0.1] - 2018-10-04
### Fixed
* Functions and methods with nullable parameters of type `resource` are now also considered
## [2.0.0] - 2018-09-27
### Changed
* [FunctionSignatureMap.php](https://raw.githubusercontent.com/phan/phan/master/src/Phan/Language/Internal/FunctionSignatureMap.php) from `phan/phan` is now used instead of [arginfo.php](https://raw.githubusercontent.com/rlerdorf/phan/master/includes/arginfo.php) from `rlerdorf/phan`
### Removed
* This component is no longer supported on PHP 5.6 and PHP 7.0
## 1.0.0 - 2015-07-28
* Initial release
[2.0.3]: https://github.com/sebastianbergmann/comparator/compare/2.0.2...2.0.3
[2.0.2]: https://github.com/sebastianbergmann/comparator/compare/2.0.1...2.0.2
[2.0.1]: https://github.com/sebastianbergmann/comparator/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/sebastianbergmann/comparator/compare/1.0.0...2.0.0
@@ -0,0 +1,33 @@
Resource Operations
Copyright (c) 2015-2018, 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.
@@ -0,0 +1,14 @@
# Resource Operations
Provides a list of PHP built-in functions that operate on resources.
## Installation
You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/):
composer require sebastian/resource-operations
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/resource-operations
@@ -0,0 +1,33 @@
{
"name": "sebastian/resource-operations",
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"require": {
"php": ">=7.1"
},
"autoload": {
"classmap": [
"src/"
]
},
"config": {
"platform": {
"php": "7.1.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
}
}
@@ -0,0 +1,499 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AmdModulesDependencies" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="AngularAmbiguousComponentTag" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularCliAddDependency" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AngularIncorrectTemplateDefinition" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularInsecureBindingToEvent" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AngularInvalidAnimationTriggerAssignment" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularInvalidEntryComponent" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularInvalidExpressionResultType" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AngularInvalidImportedOrDeclaredSymbol" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularInvalidSelector" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularInvalidTemplateReferenceVariable" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularMissingEventHandler" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularMissingOrInvalidDeclarationInModule" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularMultipleStructuralDirectives" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularNonEmptyNgContent" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularRecursiveModuleImportExport" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularUndefinedBinding" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularUndefinedModuleExport" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AngularUndefinedTag" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AutoloadingIssuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="BadExceptionsProcessingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="BadExpressionStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="BladeControlDirectives" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CallerJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckDtdRefs" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckNodeTest" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckTagEmptyBody" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckValidXmlInScriptTagBody" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CheckXmlFileWithXercesValidator" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ClassOverridesFieldOfSuperClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CoffeeScriptArgumentsOutsideFunction" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptFunctionSignatures" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptInfiniteLoop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptLiteralNotFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptModulesDependencies" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptSillyAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptSwitchStatementWithNoDefaultBranch" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptUnusedLocalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CommaExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ComposeUnknownKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ComposeUnknownValues" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ConstantConditionalExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ConstantIfStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ContinueOrBreakFromFinallyBlockJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssFloatPxLength" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidAtRule" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidCharsetRule" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidElement" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidHtmlTagReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidMediaFeature" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPropertyValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPseudoSelector" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssMissingComma" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssNegativeValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssNoGenericFontName" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssOverwrittenProperties" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssRedundantUnit" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssReplaceWithShorthandSafely" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssReplaceWithShorthandUnsafely" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="CssUnitlessNumber" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssUnknownProperty" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myCustomPropertiesEnabled" value="false" />
<option name="myIgnoreVendorSpecificProperties" value="false" />
<option name="myCustomPropertiesList">
<value>
<list size="0" />
</value>
</option>
</inspection_tool>
<inspection_tool class="CssUnknownTarget" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedClass" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedCustomProperty" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CucumberExamplesColon" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CucumberMissedExamples" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CucumberTableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CucumberUndefinedStep" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DisallowWritingIntoStaticPropertiesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="DockerFileAddOrCopySemantic" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DockerFileArgumentCount" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="DockerFileAssignments" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="DuplicateCaseLabelJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DuplicateKeyInSection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DuplicateSectionInFile" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ES6BindWithArrowFunction" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ES6CheckImport" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6ClassMemberInitializationOrder" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ES6ConvertModuleExportToExport" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertRequireIntoImport" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertToForOf" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertVarToLetConst" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6MissingAwait" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ModulesDependencies" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6PossiblyAsyncFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6ShorthandObjectProperty" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6UnusedImports" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyStatementBodyJS" enabled="false" level="WARNING" enabled_by_default="false">
<option name="m_reportEmptyBlocks" value="false" />
</inspection_tool>
<inspection_tool class="ExceptionCaughtLocallyJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FallThroughInSwitchStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FileHeaderInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="FlowJSConfig" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FlowJSFlagCommentPlacement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ForgottenDebugOutputInspection" enabled="true" level="ERROR" enabled_by_default="true">
<option name="configuration">
<list>
<option value="\Codeception\Util\Debug::debug" />
<option value="\Codeception\Util\Debug::pause" />
<option value="\Doctrine::dump" />
<option value="\Doctrine\Common\Util\Debug::dump" />
<option value="\Doctrine\Common\Util\Debug::export" />
<option value="\Illuminate\Support\Debug\Dumper::dump" />
<option value="\Symfony\Component\Debug\Debug::enable" />
<option value="\Symfony\Component\Debug\DebugClassLoader::enable" />
<option value="\Symfony\Component\Debug\ErrorHandler::register" />
<option value="\Symfony\Component\Debug\ExceptionHandler::register" />
<option value="\TYPO3\CMS\Core\Utility\DebugUtility::debug" />
<option value="\Zend\Debug\Debug::dump" />
<option value="\Zend\Di\Display\Console::export" />
<option value="\Zend_Debug::dump" />
<option value="dd" />
<option value="debug_print_backtrace" />
<option value="debug_zval_dump" />
<option value="dpm" />
<option value="dpq" />
<option value="dsm" />
<option value="dump" />
<option value="dvm" />
<option value="error_log" />
<option value="kpr" />
<option value="phpinfo" />
<option value="print_r" />
<option value="var_dump" />
<option value="var_export" />
<option value="xdebug_break" />
<option value="xdebug_call_class" />
<option value="xdebug_call_file" />
<option value="xdebug_call_function" />
<option value="xdebug_call_line" />
<option value="xdebug_code_coverage_started" />
<option value="xdebug_debug_zval" />
<option value="xdebug_debug_zval_stdout" />
<option value="xdebug_dump_superglobals" />
<option value="xdebug_enable" />
<option value="xdebug_get_code_coverage" />
<option value="xdebug_get_collected_errors" />
<option value="xdebug_get_declared_vars" />
<option value="xdebug_get_function_stack" />
<option value="xdebug_get_headers" />
<option value="xdebug_get_monitored_functions" />
<option value="xdebug_get_profiler_filename" />
<option value="xdebug_get_stack_depth" />
<option value="xdebug_get_tracefile_name" />
<option value="xdebug_is_enabled" />
<option value="xdebug_memory_usage" />
<option value="xdebug_peak_memory_usage" />
<option value="xdebug_print_function_stack" />
<option value="xdebug_start_code_coverage" />
<option value="xdebug_start_error_collection" />
<option value="xdebug_start_function_monitor" />
<option value="xdebug_start_trace" />
<option value="xdebug_stop_code_coverage" />
<option value="xdebug_stop_error_collection" />
<option value="xdebug_stop_function_monitor" />
<option value="xdebug_stop_trace" />
<option value="xdebug_time_index" />
<option value="xdebug_var_dump" />
</list>
</option>
<option name="migratedIntoUserSpace" value="true" />
</inspection_tool>
<inspection_tool class="GherkinBrokenTableInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="GherkinMisplacedBackground" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="GherkinScenarioToScenarioOutline" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HamlNestedTagContent" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HardwiredNamespacePrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlDeprecatedAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlDeprecatedTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlExtraClosingTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlMissingClosingTag" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="HtmlRequiredAltAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlRequiredLangAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlRequiredTitleElement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAnchorTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="0" />
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownBooleanAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownTag" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="6">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ImplicitTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
<option name="BITS" value="1720" />
<option name="FLAG_EXPLICIT_CONVERSION" value="true" />
<option name="IGNORE_NODESET_TO_BOOLEAN_VIA_STRING" value="true" />
</inspection_tool>
<inspection_tool class="IncompatibleMaskJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IndexZeroUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="InfiniteLoopJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="InfiniteRecursionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSAccessibilityCheck" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSAnnotator" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JSArrowFunctionBracesCanBeRemoved" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSAssignmentUsedAsCondition" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSBitwiseOperatorUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSCheckFunctionSignatures" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSClosureCompilerSyntax" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSCommentMatchesSignature" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSComparisonWithNaN" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSConsecutiveCommasInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSConstructorReturnsPrimitive" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSDeprecatedSymbols" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSDuplicatedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSEqualityComparisonWithCoercion" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSFileReferences" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSFunctionExpressionToArrowFunction" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSIgnoredPromiseFromCall" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSIncompatibleTypesComparison" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSJQueryEfficiency" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSJoinVariableDeclarationAndAssignment" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSLastCommaInObjectLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSMethodCanBeStatic" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSMismatchedCollectionQueryUpdate" enabled="false" level="WARNING" enabled_by_default="false">
<option name="queries" value="trace,write,forEach,length" />
<option name="updates" value="pop,push,shift,splice,unshift,add,insert,remove" />
</inspection_tool>
<inspection_tool class="JSMissingSwitchBranches" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSMissingSwitchDefault" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSNonASCIINames" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSObjectNullOrUndefined" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSPotentiallyInvalidConstructorUsage" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myConsiderUppercaseFunctionsToBeConstructors" value="true" />
</inspection_tool>
<inspection_tool class="JSPotentiallyInvalidTargetOfIndexedPropertyAccess" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSPotentiallyInvalidUsageOfClassThis" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSPotentiallyInvalidUsageOfThis" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSPrimitiveTypeWrapperUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSRedeclarationOfBlockScope" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JSRedundantSwitchStatement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSReferencingArgumentsOutsideOfFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JSReferencingMutableVariableFromClosure" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSRemoveUnnecessaryParentheses" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSStringConcatenationToES6Template" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSSuspiciousNameCombination" enabled="false" level="WARNING" enabled_by_default="false">
<group names="x,width,left,right" />
<group names="y,height,top,bottom" />
<exclude classes="Math" />
</inspection_tool>
<inspection_tool class="JSSwitchVariableDeclarationIssue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSTestFailedLine" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSTypeOfValues" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUndeclaredVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUndefinedPropertyAssignment" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnfilteredForInLoop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnnecessarySemicolon" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnreachableSwitchBranches" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedExtXType" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedLibraryURL" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnusedAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnusedGlobalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnusedLocalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSValidateJSDoc" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSValidateTypes" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSXNamespaceValidation" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="Json5StandardCompliance" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JsonDuplicatePropertyKeys" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JsonSchemaCompliance" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JsonSchemaDeprecation" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JsonSchemaRefReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JsonStandardCompliance" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="LessResolvedByNameOnly" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="LessUnresolvedMixin" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LessUnresolvedVariable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LoopStatementThatDoesntLoopJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownUnresolvedFileReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MissingSinceTagDocInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="MssqlBuiltinInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MssqlTriggerInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NodeJsCodingAssistanceForCoreModules" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NodeModulesDependencies" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="NpmUsedModulesInstalled" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="OctalIntegerJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PackageJsonMismatchedDependency" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PgSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhingDomInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PhpAssignmentInConditionInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpDivisionByZeroInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpDocMissingReturnTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocSignatureInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpFullyQualifiedNameUsageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpIncludeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpMethodOrClassCallIsNotCaseSensitiveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMissingStrictTypesDeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpMultipleClassesDeclarationsInOneFile" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpShortOpenTagInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpTraditionalSyntaxArrayLiteralInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PhpUnnecessaryFullyQualifiedNameInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUsageOfSilenceOperatorInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PhpVariableVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PointlessArithmeticExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PointlessBooleanExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
<option name="CHECK_ANY" value="false" />
</inspection_tool>
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RequiredAttributes" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myAdditionalRequiredHtmlAttributes" value="" />
</inspection_tool>
<inspection_tool class="ReservedWordUsedAsNameJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ReturnFromFinallyBlockJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SassScssResolvedByNameOnly" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="SassScssUnresolvedMixin" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SassScssUnresolvedPlaceholderSelector" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SassScssUnresolvedVariable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SecurityAdvisoriesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="REPORT_MISSING_ROAVE_ADVISORIES" value="false" />
<option name="optionConfiguration">
<list>
<option value="barryvdh/laravel-debugbar" />
<option value="behat/behat" />
<option value="brianium/paratest" />
<option value="codeception/codeception" />
<option value="codeception/mockery-module" />
<option value="codeception/specify" />
<option value="codeception/verify" />
<option value="codedungeon/phpunit-result-printer" />
<option value="composer/composer" />
<option value="doctrine/coding-standard" />
<option value="filp/whoops" />
<option value="friendsofphp/php-cs-fixer" />
<option value="humbug/humbug" />
<option value="infection/infection" />
<option value="jakub-onderka/php-parallel-lint" />
<option value="johnkary/phpunit-speedtrap" />
<option value="mikey179/vfsStream" />
<option value="mockery/mockery" />
<option value="mybuilder/phpunit-accelerator" />
<option value="orchestra/testbench" />
<option value="pdepend/pdepend" />
<option value="phan/phan" />
<option value="phing/phing" />
<option value="phpcompatibility/php-compatibility" />
<option value="phpmd/phpmd" />
<option value="phpro/grumphp" />
<option value="phpspec/phpspec" />
<option value="phpspec/prophecy" />
<option value="phpstan/phpstan" />
<option value="phpunit/dbunit" />
<option value="phpunit/phpcov" />
<option value="phpunit/phpunit" />
<option value="phpunit/phpunit-selenium" />
<option value="povils/phpmnd" />
<option value="roave/security-advisories" />
<option value="satooshi/php-coveralls" />
<option value="sebastian/phpcpd" />
<option value="slevomat/coding-standard" />
<option value="spatie/phpunit-watcher" />
<option value="squizlabs/php_codesniffer" />
<option value="sstalle/php7cc" />
<option value="symfony/debug" />
<option value="symfony/maker-bundle" />
<option value="symfony/phpunit-bridge" />
<option value="symfony/var-dumper" />
<option value="vimeo/psalm" />
<option value="wimg/php-compatibility" />
<option value="yiisoft/yii2-debug" />
<option value="yiisoft/yii2-gii" />
<option value="zendframework/zend-debug" />
<option value="zendframework/zend-test" />
</list>
</option>
</inspection_tool>
<inspection_tool class="ShiftOutOfRangeJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SillyAssignmentJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlErrorHandlingInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIllegalCursorStateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlJoinWithoutOnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnreachableCodeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedSubqueryItemInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlWithoutWhereInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SuspiciousTypeOfGuard" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ThisExpressionReferencesGlobalObjectJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ThrowFromFinallyBlockJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TrivialConditionalJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TrivialIfJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptAbstractClassConstructorCanBeMadeProtected" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptAccessibilityCheck" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptCheckImport" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TypeScriptConfig" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptFieldCanBeMadeReadonly" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptLibrary" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TypeScriptMissingAugmentationImport" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="TypeScriptPreferShortImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptSuspiciousConstructorParameterAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptUMDGlobal" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptUnresolvedFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptValidateJSTypes" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptValidateTypes" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TypescriptExplicitMemberType" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="UnnecessaryBooleanCheckInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryContinueJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLabelJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLabelOnBreakStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLabelOnContinueStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLocalVariableJS" enabled="false" level="WARNING" enabled_by_default="false">
<option name="m_ignoreImmediatelyReturnedVariables" value="false" />
<option name="m_ignoreAnnotatedVariables" value="false" />
</inspection_tool>
<inspection_tool class="UnnecessaryReturnJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnqualifiedReferenceInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnreachableCodeJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnresolvedReference" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="UnterminatedStatementJS" enabled="false" level="WARNING" enabled_by_default="false">
<option name="ignoreSemicolonAtEndOfBlock" value="true" />
</inspection_tool>
<inspection_tool class="VueDataFunction" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="VueDuplicateTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="WebpackConfigHighlighting" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="WithStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDefaultAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDeprecatedElement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDuplicatedId" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlHighlighting" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlInvalidId" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlPathReference" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlUnboundNsPrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlUnusedNamespaceDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlWrongRootElement" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XsltDeclarations" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XsltTemplateInvocation" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XsltUnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XsltVariableShadowing" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="YAMLDuplicatedKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="YAMLRecursiveAlias" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="YAMLSchemaDeprecation" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="YAMLUnresolvedAlias" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="YAMLUnusedAnchor" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/type.iml" filepath="$PROJECT_DIR$/.idea/type.iml" />
</modules>
</component>
</project>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EAUltimateProjectSettings">
<categories>
<STRICTNESS_CATEGORY_SECURITY enabled="yes" />
<STRICTNESS_CATEGORY_PROBABLE_BUGS enabled="yes" />
<STRICTNESS_CATEGORY_PERFORMANCE enabled="yes" />
<STRICTNESS_CATEGORY_ARCHITECTURE enabled="yes" />
<STRICTNESS_CATEGORY_CONTROL_FLOW enabled="yes" />
<STRICTNESS_CATEGORY_LANGUAGE_LEVEL_MIGRATION enabled="yes" />
<STRICTNESS_CATEGORY_CODE_STYLE enabled="yes" />
<STRICTNESS_CATEGORY_UNUSED enabled="yes" />
<STRICTNESS_CATEGORY_PHPUNIT enabled="yes" />
</categories>
<settings>
<ANALYZE_ONLY_MODIFIED_FILES value="no" />
<PREFER_YODA_COMPARISON_STYLE value="no" />
</settings>
</component>
</project>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
<path value="$PROJECT_DIR$/vendor/phpspec/prophecy" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-token-stream" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/type-resolver" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
<component name="PhpUnit">
<phpunit_settings>
<PhpUnitSettings load_method="CUSTOM_LOADER" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
</phpunit_settings>
</component>
</project>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
@@ -0,0 +1,50 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [1.1.5] - 2024-03-01
* No code changes, only updated `.gitattributes` to not export non-essential files.
## [1.1.4] - 2020-11-30
### Changed
* Changed PHP version constraint in `composer.json` from `^7.2` to `>=7.2`
## [1.1.3] - 2019-07-02
### Fixed
* Fixed class name comparison in `ObjectType` to be case insensitive
## [1.1.2] - 2019-06-19
### Fixed
* Fixed handling of `object` type
## [1.1.1] - 2019-06-08
### Fixed
* Fixed autoloading of `callback_function.php` fixture file
## [1.1.0] - 2019-06-07
### Added
* Added support for `callable` type
* Added support for `iterable` type
## [1.0.0] - 2019-06-06
* Initial release based on [code contributed by Michel Hartmann to PHPUnit](https://github.com/sebastianbergmann/phpunit/pull/3673)
[1.1.5]: https://github.com/sebastianbergmann/type/compare/1.1.4...1.1.5
[1.1.4]: https://github.com/sebastianbergmann/type/compare/1.1.3...1.1.4
[1.1.3]: https://github.com/sebastianbergmann/type/compare/1.1.2...1.1.3
[1.1.2]: https://github.com/sebastianbergmann/type/compare/1.1.1...1.1.2
[1.1.1]: https://github.com/sebastianbergmann/type/compare/1.1.0...1.1.1
[1.1.0]: https://github.com/sebastianbergmann/type/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/sebastianbergmann/type/compare/ff74aa41746bd8d10e931843ebf37d42da513ede...1.0.0
@@ -0,0 +1,33 @@
sebastian/type
Copyright (c) 2019, 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.

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