JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3RbrPKAZnnversion/composer.jsonnu[{ "name": "sebastian/version", "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/version/issues" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "prefer-stable": true, "require": { "php": ">=8.1" }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-main": "4.0-dev" } } } PKAZE >PPversion/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann; use function end; use function explode; use function fclose; use function is_dir; use function is_resource; use function proc_close; use function proc_open; use function stream_get_contents; use function substr_count; use function trim; final class Version { private readonly string $version; public function __construct(string $release, string $path) { $this->version = $this->generate($release, $path); } public function asString(): string { return $this->version; } private function generate(string $release, string $path): string { if (substr_count($release, '.') + 1 === 3) { $version = $release; } else { $version = $release . '-dev'; } $git = $this->getGitInformation($path); if (!$git) { return $version; } if (substr_count($release, '.') + 1 === 3) { return $git; } $git = explode('-', $git); return $release . '-' . end($git); } private function getGitInformation(string $path): bool|string { if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) { return false; } $process = proc_open( 'git describe --tags', [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ], $pipes, $path ); if (!is_resource($process)) { return false; } $result = trim(stream_get_contents($pipes[1])); fclose($pipes[1]); fclose($pipes[2]); $returnCode = proc_close($process); if ($returnCode !== 0) { return false; } return $result; } } PKAZWversion/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/version/v/stable.png)](https://packagist.org/packages/sebastian/version) # sebastian/version **sebastian/version** is a library that helps with managing the version number of Git-hosted PHP projects. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/version ``` 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/version ``` ## Usage The constructor of the `SebastianBergmann\Version` class expects two parameters: * `$release` is the version number of the latest release (`X.Y.Z`, for instance) or the name of the release series (`X.Y`) when no release has been made from that branch / for that release series yet. * `$path` is the path to the directory (or a subdirectory thereof) where the sourcecode of the project can be found. Simply passing `__DIR__` here usually suffices. Apart from the constructor, the `SebastianBergmann\Version` class has a single public method: `asString()`. Here is a contrived example that shows the basic usage: ```php asString()); ``` ``` string(18) "1.0.0-17-g00f3408" ``` When a new release is prepared, the string that is passed to the constructor as the first argument needs to be updated. ### How SebastianBergmann\Version::asString() works * If `$path` is not (part of) a Git repository and `$release` is in `X.Y.Z` format then `$release` is returned as-is. * If `$path` is not (part of) a Git repository and `$release` is in `X.Y` format then `$release` is returned suffixed with `-dev`. * If `$path` is (part of) a Git repository and `$release` is in `X.Y.Z` format then the output of `git describe --tags` is returned as-is. * If `$path` is (part of) a Git repository and `$release` is in `X.Y` format then a string is returned that begins with `X.Y` and ends with information from `git describe --tags`. PKAZ>Btversion/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. ## [4.0.1] - 2023-02-07 ### Fixed * [#17](https://github.com/sebastianbergmann/version/pull/17): Release archive contains unnecessary assets ## [4.0.0] - 2023-02-03 ### Changed * `Version::getVersion()` has been renamed to `Version::asString()` ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4, and PHP 8.0 ## [3.0.2] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [3.0.1] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [3.0.0] - 2020-01-21 ### Removed * This component is no longer supported on PHP 7.1 and PHP 7.2 [4.0.1]: https://github.com/sebastianbergmann/version/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/version/compare/3.0.2...4.0.0 [3.0.2]: https://github.com/sebastianbergmann/version/compare/3.0.1...3.0.2 [3.0.1]: https://github.com/sebastianbergmann/version/compare/3.0.0...3.0.1 [3.0.0]: https://github.com/sebastianbergmann/version/compare/2.0.1...3.0.0 PKAZVoversion/LICENSEnu[BSD 3-Clause License Copyright (c) 2013-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZAAexporter/composer.jsonnu[{ "name": "sebastian/exporter", "description": "Provides the functionality to export PHP variables for visualization", "keywords": ["exporter","export"], "homepage": "https://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" } ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "prefer-stable": true, "require": { "php": ">=8.1", "ext-mbstring": "*", "sebastian/recursion-context": "^5.0" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture" ] }, "extra": { "branch-alias": { "dev-main": "5.1-dev" } } } PKAZKJuuexporter/SECURITY.mdnu[# Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. PKAZak;~&~&exporter/src/Exporter.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Exporter; use function bin2hex; use function count; use function get_resource_type; use function gettype; use function implode; use function ini_get; use function ini_set; use function is_array; use function is_float; use function is_object; use function is_resource; use function is_string; use function mb_strlen; use function mb_substr; use function preg_match; use function spl_object_id; use function sprintf; use function str_repeat; use function str_replace; use function var_export; use BackedEnum; use SebastianBergmann\RecursionContext\Context; use SplObjectStorage; use UnitEnum; final 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 */ public function export(mixed $value, int $indentation = 0): string { return $this->recursiveExport($value, $indentation); } public function shortenedRecursiveExport(array &$data, Context $context = null): string { $result = []; $exporter = new self; if (!$context) { $context = new Context; } $array = $data; /* @noinspection UnusedFunctionResultInspection */ $context->add($data); foreach ($array as $key => $value) { if (is_array($value)) { if ($context->contains($data[$key]) !== false) { $result[] = '*RECURSION*'; } else { $result[] = sprintf('[%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 '...'. */ public function shortenedExport(mixed $value): string { if (is_string($value)) { $string = str_replace("\n", '', $this->export($value)); if (mb_strlen($string) > 40) { return mb_substr($string, 0, 30) . '...' . mb_substr($string, -7); } return $string; } if ($value instanceof BackedEnum) { return sprintf( '%s Enum (%s, %s)', $value::class, $value->name, $this->export($value->value), ); } if ($value instanceof UnitEnum) { return sprintf( '%s Enum (%s)', $value::class, $value->name, ); } if (is_object($value)) { return sprintf( '%s Object (%s)', $value::class, count($this->toArray($value)) > 0 ? '...' : '', ); } if (is_array($value)) { return sprintf( '[%s]', count($value) > 0 ? '...' : '', ); } return $this->export($value); } /** * Converts an object to an array containing all of its private, protected * and public properties. */ public function toArray(mixed $value): array { 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 $propertyName => "\0ClassName\0propertyName" // protected $propertyName => "\0*\0propertyName" // public $propertyName => "propertyName" 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 do not 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 $_value) { $array['Object #' . spl_object_id($_value)] = [ 'obj' => $_value, 'inf' => $value->getInfo(), ]; } $value->rewind(); } return $array; } private function recursiveExport(mixed &$value, int $indentation, ?Context $processed = null): string { 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 (gettype($value) === 'resource (closed)') { return 'resource (closed)'; } if (is_resource($value)) { return sprintf( 'resource(%d) of type (%s)', $value, get_resource_type($value), ); } if ($value instanceof BackedEnum) { return sprintf( '%s Enum #%d (%s, %s)', $value::class, spl_object_id($value), $value->name, $this->export($value->value, $indentation), ); } if ($value instanceof UnitEnum) { return sprintf( '%s Enum #%d (%s)', $value::class, spl_object_id($value), $value->name, ); } 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( '', "\n", str_replace( ["\r\n", "\n\r", "\r", "\n"], ['\r\n', '\n\r', '\r', '\n'], $value, ), ) . "'"; } $whitespace = str_repeat(' ', 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 .= $whitespace . ' ' . $this->recursiveExport($k, $indentation) . ' => ' . $this->recursiveExport($value[$k], $indentation + 1, $processed) . ",\n"; } $values = "\n" . $values . $whitespace; } return 'Array &' . (string) $key . ' [' . $values . ']'; } if (is_object($value)) { $class = $value::class; if ($processed->contains($value)) { return $class . ' Object #' . spl_object_id($value); } $processed->add($value); $values = ''; $array = $this->toArray($value); if (count($array) > 0) { foreach ($array as $k => $v) { $values .= $whitespace . ' ' . $this->recursiveExport($k, $indentation) . ' => ' . $this->recursiveExport($v, $indentation + 1, $processed) . ",\n"; } $values = "\n" . $values . $whitespace; } return $class . ' Object #' . spl_object_id($value) . ' (' . $values . ')'; } return var_export($value, true); } } PKAZ_P exporter/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/exporter/v/stable.png)](https://packagist.org/packages/sebastian/exporter) [![CI Status](https://github.com/sebastianbergmann/exporter/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/exporter/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/exporter/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/exporter) [![codecov](https://codecov.io/gh/sebastianbergmann/exporter/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/exporter) # sebastian/exporter This component provides the functionality to export PHP variables for visualization. ## 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 ``` ## Usage Exporting: ```php '' 'string' => '' 'code' => 0 'file' => '/home/sebastianbergmann/test.php' 'line' => 34 'previous' => null ) */ print $exporter->export(new Exception); ``` ## Data Types Exporting simple types: ```php 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 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 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( <<=7.3` ## [4.0.2] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [4.0.1] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports ## [4.0.0] - 2020-02-07 ### Removed * This component is no longer supported on PHP 7.0, PHP 7.1, and PHP 7.2 ## [3.1.5] - 2022-09-14 ### Fixed * [#47](https://github.com/sebastianbergmann/exporter/pull/47): Fix `float` export precision ## [3.1.4] - 2021-11-11 ### Changed * [#38](https://github.com/sebastianbergmann/exporter/pull/38): Improve export of closed resources ## [3.1.3] - 2020-11-30 ### Changed * Changed PHP version constraint in `composer.json` from `^7.0` to `>=7.0` ## [3.1.2] - 2019-09-14 ### Fixed * [#29](https://github.com/sebastianbergmann/exporter/pull/29): Second parameter for `str_repeat()` must be an integer ### Removed * Remove HHVM-specific code that is no longer needed [5.1.1]: https://github.com/sebastianbergmann/exporter/compare/5.1.0...5.1.1 [5.1.0]: https://github.com/sebastianbergmann/exporter/compare/5.0.1...5.1.0 [5.0.1]: https://github.com/sebastianbergmann/exporter/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/sebastianbergmann/exporter/compare/4.0.5...5.0.0 [4.0.5]: https://github.com/sebastianbergmann/exporter/compare/4.0.4...4.0.5 [4.0.4]: https://github.com/sebastianbergmann/exporter/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/exporter/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/exporter/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/exporter/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/exporter/compare/3.1.2...4.0.0 [3.1.5]: https://github.com/sebastianbergmann/exporter/compare/3.1.4...3.1.5 [3.1.4]: https://github.com/sebastianbergmann/exporter/compare/3.1.3...3.1.4 [3.1.3]: https://github.com/sebastianbergmann/exporter/compare/3.1.2...3.1.3 [3.1.2]: https://github.com/sebastianbergmann/exporter/compare/3.1.1...3.1.2 PKAZTexporter/LICENSEnu[BSD 3-Clause License Copyright (c) 2002-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZ??environment/composer.jsonnu[{ "name": "sebastian/environment", "description": "Provides functionality to handle HHVM/PHP environments", "keywords": ["environment","hhvm","xdebug"], "homepage": "https://github.com/sebastianbergmann/environment", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "prefer-stable": true, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "suggest": { "ext-posix": "*" }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-main": "6.0-dev" } } } PKAZKJuuenvironment/SECURITY.mdnu[# Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. PKAZb6environment/src/Console.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Environment; use const DIRECTORY_SEPARATOR; use const STDIN; use const STDOUT; use function defined; use function fclose; use function fstat; use function function_exists; use function getenv; use function is_resource; use function is_string; use function posix_isatty; use function preg_match; use function proc_close; use function proc_open; use function sapi_windows_vt100_support; use function shell_exec; use function stream_get_contents; use function stream_isatty; use function trim; final class Console { /** * @var int */ public const STDIN = 0; /** * @var int */ public const STDOUT = 1; /** * @var int */ public const STDERR = 2; /** * Returns true if STDOUT supports colorization. * * This code has been copied and adapted from * Symfony\Component\Console\Output\StreamOutput. */ public function hasColorSupport(): bool { if ('Hyper' === getenv('TERM_PROGRAM')) { return true; } if ($this->isWindows()) { // @codeCoverageIgnoreStart return (defined('STDOUT') && function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); // @codeCoverageIgnoreEnd } if (!defined('STDOUT')) { // @codeCoverageIgnoreStart return false; // @codeCoverageIgnoreEnd } return $this->isInteractive(STDOUT); } /** * Returns the number of columns of the terminal. * * @codeCoverageIgnore */ public function getNumberOfColumns(): int { if (!$this->isInteractive(defined('STDIN') ? STDIN : self::STDIN)) { return 80; } if ($this->isWindows()) { return $this->getNumberOfColumnsWindows(); } return $this->getNumberOfColumnsInteractive(); } /** * Returns if the file descriptor is an interactive terminal or not. * * Normally, we want to use a resource as a parameter, yet sadly it's not always awailable, * eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined. * * @param int|resource $fileDescriptor */ public function isInteractive($fileDescriptor = self::STDOUT): bool { if (is_resource($fileDescriptor)) { if (function_exists('stream_isatty') && @stream_isatty($fileDescriptor)) { return true; } if (function_exists('fstat')) { $stat = @fstat(STDOUT); return $stat && 0020000 === ($stat['mode'] & 0170000); } return false; } return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); } private function isWindows(): bool { return DIRECTORY_SEPARATOR === '\\'; } /** * @codeCoverageIgnore */ private function getNumberOfColumnsInteractive(): int { if (function_exists('shell_exec') && preg_match('#\d+ (\d+)#', shell_exec('stty size') ?: '', $match) === 1) { if ((int) $match[1] > 0) { return (int) $match[1]; } } if (function_exists('shell_exec') && preg_match('#columns = (\d+);#', shell_exec('stty') ?: '', $match) === 1) { if ((int) $match[1] > 0) { return (int) $match[1]; } } return 80; } /** * @codeCoverageIgnore */ private function getNumberOfColumnsWindows(): int { $ansicon = getenv('ANSICON'); $columns = 80; if (is_string($ansicon) && preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim($ansicon), $matches)) { $columns = (int) $matches[1]; } elseif (function_exists('proc_open')) { $process = proc_open( 'mode CON', [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ], $pipes, null, null, ['suppress_errors' => true] ); if (is_resource($process)) { $info = stream_get_contents($pipes[1]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { $columns = (int) $matches[2]; } } } return $columns - 1; } } PKAZ>$$environment/src/Runtime.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Environment; use const PHP_BINARY; use const PHP_BINDIR; use const PHP_MAJOR_VERSION; use const PHP_SAPI; use const PHP_VERSION; use function array_map; use function array_merge; use function escapeshellarg; use function explode; use function extension_loaded; use function ini_get; use function is_readable; use function parse_ini_file; use function php_ini_loaded_file; use function php_ini_scanned_files; use function phpversion; use function sprintf; use function strrpos; final class Runtime { private static string $binary; private static bool $initialized = false; /** * Returns true when Xdebug or PCOV is available or * the runtime used is PHPDBG. */ public function canCollectCodeCoverage(): bool { return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); } /** * Returns true when Zend OPcache is loaded, enabled, * and is configured to discard comments. */ public function discardsComments(): bool { if (!$this->isOpcacheActive()) { return false; } if (ini_get('opcache.save_comments') !== '0') { return false; } return true; } /** * Returns true when Zend OPcache is loaded, enabled, * and is configured to perform just-in-time compilation. */ public function performsJustInTimeCompilation(): bool { if (PHP_MAJOR_VERSION < 8) { return false; } if (!$this->isOpcacheActive()) { return false; } if (ini_get('opcache.jit_buffer_size') === '0') { return false; } $jit = ini_get('opcache.jit'); if (($jit === 'disable') || ($jit === 'off')) { return false; } if (strrpos($jit, '0') === 3) { return false; } return true; } /** * Returns the path to the binary of the current runtime. */ public function getBinary(): string { if (self::$initialized) { return self::$binary; } if (PHP_BINARY !== '') { self::$binary = escapeshellarg(PHP_BINARY); self::$initialized = true; return self::$binary; } // @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); self::$initialized = true; return self::$binary; } } // @codeCoverageIgnoreStart self::$binary = 'php'; self::$initialized = true; // @codeCoverageIgnoreEnd return self::$binary; } public function getNameWithVersion(): string { return $this->getName() . ' ' . $this->getVersion(); } public function getNameWithVersionAndCodeCoverageDriver(): string { if ($this->hasPCOV()) { return sprintf( '%s with PCOV %s', $this->getNameWithVersion(), phpversion('pcov') ); } if ($this->hasXdebug()) { return sprintf( '%s with Xdebug %s', $this->getNameWithVersion(), phpversion('xdebug') ); } return $this->getNameWithVersion(); } public function getName(): string { if ($this->isPHPDBG()) { // @codeCoverageIgnoreStart return 'PHPDBG'; // @codeCoverageIgnoreEnd } return 'PHP'; } public function getVendorUrl(): string { return 'https://www.php.net/'; } public function getVersion(): string { return PHP_VERSION; } /** * Returns true when the runtime used is PHP and Xdebug is loaded. */ public function hasXdebug(): bool { return $this->isPHP() && extension_loaded('xdebug'); } /** * Returns true when the runtime used is PHP without the PHPDBG SAPI. */ public function isPHP(): bool { return !$this->isPHPDBG(); } /** * Returns true when the runtime used is PHP with the PHPDBG SAPI. */ public function isPHPDBG(): bool { return PHP_SAPI === 'phpdbg'; } /** * Returns true when the runtime used is PHP with the PHPDBG SAPI * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). */ public function hasPHPDBGCodeCoverage(): bool { return $this->isPHPDBG(); } /** * Returns true when the runtime used is PHP with PCOV loaded and enabled. */ public function hasPCOV(): bool { return $this->isPHP() && extension_loaded('pcov') && ini_get('pcov.enabled'); } /** * Parses the loaded php.ini file (if any) as well as all * additional php.ini files from the additional ini dir for * a list of all configuration settings loaded from files * at startup. Then checks for each php.ini setting passed * via the `$values` parameter whether this setting has * been changed at runtime. Returns an array of strings * where each string has the format `key=value` denoting * the name of a changed php.ini setting with its new value. * * @return string[] */ public function getCurrentSettings(array $values): array { $diff = []; $files = []; if ($file = php_ini_loaded_file()) { $files[] = $file; } if ($scanned = php_ini_scanned_files()) { $files = array_merge( $files, array_map( 'trim', explode(",\n", $scanned) ) ); } foreach ($files as $ini) { $config = parse_ini_file($ini, true); foreach ($values as $value) { $set = ini_get($value); if (empty($set)) { continue; } if ((!isset($config[$value]) || ($set !== $config[$value]))) { $diff[$value] = sprintf('%s=%s', $value, $set); } } } return $diff; } private function isOpcacheActive(): bool { if (!extension_loaded('Zend OPcache')) { return false; } if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ini_get('opcache.enable_cli') === '1') { return true; } if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && ini_get('opcache.enable') === '1') { return true; } return false; } } PKAZffenvironment/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/environment/v/stable.png)](https://packagist.org/packages/sebastian/environment) [![CI Status](https://github.com/sebastianbergmann/environment/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/environment/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/environment/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/environment) [![codecov](https://codecov.io/gh/sebastianbergmann/environment/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/environment) # sebastian/environment This component provides functionality that helps writing PHP code that has runtime-specific (PHP / HHVM) execution paths. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/environment ``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: ``` composer require --dev sebastian/environment ``` PKAZX88environment/ChangeLog.mdnu[# Changes in sebastianbergmann/environment All notable changes in `sebastianbergmann/environment` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [6.0.1] - 2023-04-11 ### Fixed * [#68](https://github.com/sebastianbergmann/environment/pull/68): The Just-in-Time compiler is disabled when `opcache.jit_buffer_size` is set to `0` * [#70](https://github.com/sebastianbergmann/environment/pull/70): The first `0` of `opcache.jit` only disables CPU-specific optimizations, not the Just-in-Time compiler itself ## [6.0.0] - 2023-02-03 ### Removed * Removed `SebastianBergmann\Environment\OperatingSystem::getFamily()` because this component is no longer supported on PHP versions that do not have `PHP_OS_FAMILY` * Removed `SebastianBergmann\Environment\Runtime::isHHVM()` * This component is no longer supported on PHP 7.3, PHP 7.4, and PHP 8.0 ## [5.1.5] - 2022-MM-DD ### Fixed * [#59](https://github.com/sebastianbergmann/environment/issues/59): Wrong usage of `stream_isatty()`, `fstat()` used without checking whether the function is available ## [5.1.4] - 2022-04-03 ### Fixed * [#63](https://github.com/sebastianbergmann/environment/pull/63): `Runtime::getCurrentSettings()` does not correctly process INI settings ## [5.1.3] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [5.1.2] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [5.1.1] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports ## [5.1.0] - 2020-04-14 ### Added * `Runtime::performsJustInTimeCompilation()` returns `true` if PHP 8's JIT is active, `false` otherwise ## [5.0.2] - 2020-03-31 ### Fixed * [#55](https://github.com/sebastianbergmann/environment/issues/55): `stty` command is executed even if no tty is available ## [5.0.1] - 2020-02-19 ### Changed * `Runtime::getNameWithVersionAndCodeCoverageDriver()` now prioritizes PCOV over Xdebug when both extensions are loaded (just like php-code-coverage does) ## [5.0.0] - 2020-02-07 ### Removed * This component is no longer supported on PHP 7.1 and PHP 7.2 ## [4.2.3] - 2019-11-20 ### Changed * [#50](https://github.com/sebastianbergmann/environment/pull/50): Windows improvements to console capabilities ### Fixed * [#49](https://github.com/sebastianbergmann/environment/issues/49): Detection how OpCache handles docblocks does not work correctly when PHPDBG is used ## [4.2.2] - 2019-05-05 ### Fixed * [#44](https://github.com/sebastianbergmann/environment/pull/44): `TypeError` in `Console::getNumberOfColumnsInteractive()` ## [4.2.1] - 2019-04-25 ### Fixed * Fixed an issue in `Runtime::getCurrentSettings()` ## [4.2.0] - 2019-04-25 ### Added * [#36](https://github.com/sebastianbergmann/environment/pull/36): `Runtime::getCurrentSettings()` ## [4.1.0] - 2019-02-01 ### Added * Implemented `Runtime::getNameWithVersionAndCodeCoverageDriver()` method * [#34](https://github.com/sebastianbergmann/environment/pull/34): Support for PCOV extension ## [4.0.2] - 2019-01-28 ### Fixed * [#33](https://github.com/sebastianbergmann/environment/issues/33): `Runtime::discardsComments()` returns true too eagerly ### Removed * Removed support for Zend Optimizer+ in `Runtime::discardsComments()` ## [4.0.1] - 2018-11-25 ### Fixed * [#31](https://github.com/sebastianbergmann/environment/issues/31): Regressions in `Console` class ## [4.0.0] - 2018-10-23 [YANKED] ### Fixed * [#25](https://github.com/sebastianbergmann/environment/pull/25): `Console::hasColorSupport()` does not work on Windows ### Removed * This component is no longer supported on PHP 7.0 ## [3.1.0] - 2017-07-01 ### Added * [#21](https://github.com/sebastianbergmann/environment/issues/21): Equivalent of `PHP_OS_FAMILY` (for PHP < 7.2) ## [3.0.4] - 2017-06-20 ### Fixed * [#20](https://github.com/sebastianbergmann/environment/pull/20): PHP 7 mode of HHVM not forced ## [3.0.3] - 2017-05-18 ### Fixed * [#18](https://github.com/sebastianbergmann/environment/issues/18): `Uncaught TypeError: preg_match() expects parameter 2 to be string, null given` ## [3.0.2] - 2017-04-21 ### Fixed * [#17](https://github.com/sebastianbergmann/environment/issues/17): `Uncaught TypeError: trim() expects parameter 1 to be string, boolean given` ## [3.0.1] - 2017-04-21 ### Fixed * Fixed inverted logic in `Runtime::discardsComments()` ## [3.0.0] - 2017-04-21 ### Added * Implemented `Runtime::discardsComments()` for querying whether the PHP runtime discards annotations ### Removed * This component is no longer supported on PHP 5.6 [6.0.1]: https://github.com/sebastianbergmann/environment/compare/6.0.0...6.0.1 [6.0.0]: https://github.com/sebastianbergmann/environment/compare/5.1.5...6.0.0 [5.1.5]: https://github.com/sebastianbergmann/environment/compare/5.1.4...5.1.5 [5.1.4]: https://github.com/sebastianbergmann/environment/compare/5.1.3...5.1.4 [5.1.3]: https://github.com/sebastianbergmann/environment/compare/5.1.2...5.1.3 [5.1.2]: https://github.com/sebastianbergmann/environment/compare/5.1.1...5.1.2 [5.1.1]: https://github.com/sebastianbergmann/environment/compare/5.1.0...5.1.1 [5.1.0]: https://github.com/sebastianbergmann/environment/compare/5.0.2...5.1.0 [5.0.2]: https://github.com/sebastianbergmann/environment/compare/5.0.1...5.0.2 [5.0.1]: https://github.com/sebastianbergmann/environment/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/sebastianbergmann/environment/compare/4.2.3...5.0.0 [4.2.3]: https://github.com/sebastianbergmann/environment/compare/4.2.2...4.2.3 [4.2.2]: https://github.com/sebastianbergmann/environment/compare/4.2.1...4.2.2 [4.2.1]: https://github.com/sebastianbergmann/environment/compare/4.2.0...4.2.1 [4.2.0]: https://github.com/sebastianbergmann/environment/compare/4.1.0...4.2.0 [4.1.0]: https://github.com/sebastianbergmann/environment/compare/4.0.2...4.1.0 [4.0.2]: https://github.com/sebastianbergmann/environment/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/environment/compare/66691f8e2dc4641909166b275a9a4f45c0e89092...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/environment/compare/3.1.0...66691f8e2dc4641909166b275a9a4f45c0e89092 [3.1.0]: https://github.com/sebastianbergmann/environment/compare/3.0...3.1.0 [3.0.4]: https://github.com/sebastianbergmann/environment/compare/3.0.3...3.0.4 [3.0.3]: https://github.com/sebastianbergmann/environment/compare/3.0.2...3.0.3 [3.0.2]: https://github.com/sebastianbergmann/environment/compare/3.0.1...3.0.2 [3.0.1]: https://github.com/sebastianbergmann/environment/compare/3.0.0...3.0.1 [3.0.0]: https://github.com/sebastianbergmann/environment/compare/2.0...3.0.0 PKAZWjenvironment/LICENSEnu[BSD 3-Clause License Copyright (c) 2014-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZ #recursion-context/composer.jsonnu[{ "name": "sebastian/recursion-context", "description": "Provides functionality to recursively process PHP variables", "homepage": "https://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" } ], "prefer-stable": true, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-main": "5.0-dev" } } } PKAZE >PPrecursion-context/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZ!recursion-context/src/Context.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\RecursionContext; use const PHP_INT_MAX; use const PHP_INT_MIN; use function array_key_exists; use function array_pop; use function array_slice; use function count; use function is_array; use function random_int; use function spl_object_hash; use SplObjectStorage; final class Context { private array $arrays = []; private SplObjectStorage $objects; public function __construct() { $this->objects = new SplObjectStorage; } /** * @codeCoverageIgnore */ public function __destruct() { foreach ($this->arrays as &$array) { if (is_array($array)) { array_pop($array); array_pop($array); } } } /** * @psalm-template T * * @psalm-param T $value * * @param-out T $value */ public function add(object|array &$value): int|string|false { if (is_array($value)) { return $this->addArray($value); } return $this->addObject($value); } /** * @psalm-template T * * @psalm-param T $value * * @param-out T $value */ public function contains(object|array &$value): int|string|false { if (is_array($value)) { return $this->containsArray($value); } return $this->containsObject($value); } private function addArray(array &$array): int { $key = $this->containsArray($array); if ($key !== false) { return $key; } $key = count($this->arrays); $this->arrays[] = &$array; if (!array_key_exists(PHP_INT_MAX, $array) && !array_key_exists(PHP_INT_MAX - 1, $array)) { $array[] = $key; $array[] = $this->objects; } else { /* Cover the improbable case, too. * * Note that array_slice() (used in containsArray()) will return the * last two values added, *not necessarily* the highest integer keys * in the array. Therefore, the order of these writes to $array is * important, but the actual keys used is not. */ do { /** @noinspection PhpUnhandledExceptionInspection */ $key = random_int(PHP_INT_MIN, PHP_INT_MAX); } while (array_key_exists($key, $array)); $array[$key] = $key; do { /** @noinspection PhpUnhandledExceptionInspection */ $key = random_int(PHP_INT_MIN, PHP_INT_MAX); } while (array_key_exists($key, $array)); $array[$key] = $this->objects; } return $key; } private function addObject(object $object): string { if (!$this->objects->contains($object)) { $this->objects->attach($object); } return spl_object_hash($object); } private function containsArray(array $array): int|false { $end = array_slice($array, -2); return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false; } private function containsObject(object $value): string|false { if ($this->objects->contains($value)) { return spl_object_hash($value); } return false; } } PKAZ >--recursion-context/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/recursion-context/v/stable.png)](https://packagist.org/packages/sebastian/recursion-context) [![CI Status](https://github.com/sebastianbergmann/recursion-context/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/recursion-context/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/recursion-context/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/recursion-context) [![codecov](https://codecov.io/gh/sebastianbergmann/recursion-context/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/recursion-context) # sebastian/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 ``` PKAZ <[recursion-context/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. ## [5.0.0] - 2023-02-03 ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0 ## [4.0.5] - 2023-02-03 ### Fixed * [#26](https://github.com/sebastianbergmann/recursion-context/pull/26): Don't clobber `null` values if `array_key_exists(PHP_INT_MAX, $array)` ## [4.0.4] - 2020-10-26 ### Fixed * `SebastianBergmann\RecursionContext\Exception` now correctly extends `\Throwable` ## [4.0.3] - 2020-09-28 ### Changed * [#21](https://github.com/sebastianbergmann/recursion-context/pull/21): Add type annotations for in/out parameters * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [4.0.2] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [4.0.1] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports [5.0.0]: https://github.com/sebastianbergmann/recursion-context/compare/4.0.5...5.0.0 [4.0.5]: https://github.com/sebastianbergmann/recursion-context/compare/4.0.4...4.0.5 [4.0.4]: https://github.com/sebastianbergmann/recursion-context/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/recursion-context/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/recursion-context/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/recursion-context/compare/4.0.0...4.0.1 PKAZTrecursion-context/LICENSEnu[BSD 3-Clause License Copyright (c) 2002-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZ#=e$$comparator/composer.jsonnu[{ "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" } ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy" }, "prefer-stable": true, "require": { "php": ">=8.1", "sebastian/diff": "^5.0", "sebastian/exporter": "^5.0", "ext-dom": "*", "ext-mbstring": "*" }, "require-dev": { "phpunit/phpunit": "^10.3" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture" ] }, "extra": { "branch-alias": { "dev-main": "5.0-dev" } } } PKAZKJuucomparator/SECURITY.mdnu[# Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. PKAZ` #comparator/src/ScalarComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function is_bool; use function is_object; use function is_scalar; use function is_string; use function mb_strtolower; use function method_exists; use function sprintf; use SebastianBergmann\Exporter\Exporter; /** * Compares scalar or NULL values for equality. */ class ScalarComparator extends Comparator { public function accepts(mixed $expected, mixed $actual): bool { 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)); } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false): void { $expectedToCompare = $expected; $actualToCompare = $actual; $exporter = new Exporter; // 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 = mb_strtolower($expectedToCompare, 'UTF-8'); $actualToCompare = mb_strtolower($actualToCompare, 'UTF-8'); } } if ($expectedToCompare !== $actualToCompare && is_string($expected) && is_string($actual)) { throw new ComparisonFailure( $expected, $actual, $exporter->export($expected), $exporter->export($actual), 'Failed asserting that two strings are equal.' ); } if ($expectedToCompare != $actualToCompare) { throw new ComparisonFailure( $expected, $actual, // no diff is required '', '', sprintf( 'Failed asserting that %s matches expected %s.', $exporter->export($actual), $exporter->export($expected) ) ); } } } PKAZ32!comparator/src/TypeComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function gettype; use function sprintf; use SebastianBergmann\Exporter\Exporter; final class TypeComparator extends Comparator { public function accepts(mixed $expected, mixed $actual): bool { return true; } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false): void { if (gettype($expected) != gettype($actual)) { throw new ComparisonFailure( $expected, $actual, // we don't need a diff '', '', sprintf( '%s does not match expected type "%s".', (new Exporter)->shortenedExport($actual), gettype($expected) ) ); } } } PKAZG3WB B $comparator/src/DOMNodeComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function assert; use function mb_strtolower; use function sprintf; use DOMDocument; use DOMNode; use ValueError; final class DOMNodeComparator extends ObjectComparator { public function accepts(mixed $expected, mixed $actual): bool { return $expected instanceof DOMNode && $actual instanceof DOMNode; } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false, array &$processed = []): void { assert($expected instanceof DOMNode); assert($actual instanceof DOMNode); $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, sprintf("Failed asserting that two DOM %s are equal.\n", $type) ); } } /** * Returns the normalized, whitespace-cleaned, and indented textual * representation of a DOMNode. */ private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string { if ($canonicalize) { $document = new DOMDocument; try { $c14n = $node->C14N(); assert(!empty($c14n)); @$document->loadXML($c14n); } catch (ValueError) { } $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 ? mb_strtolower($text, 'UTF-8') : $text; } } PKAZ88'comparator/src/MockObjectComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function array_keys; use function assert; use function str_starts_with; use PHPUnit\Framework\MockObject\Stub; /** * Compares PHPUnit\Framework\MockObject\MockObject instances for equality. */ final class MockObjectComparator extends ObjectComparator { public function accepts(mixed $expected, mixed $actual): bool { return $expected instanceof Stub && $actual instanceof Stub; } protected function toArray(object $object): array { assert($object instanceof Stub); $array = parent::toArray($object); foreach (array_keys($array) as $key) { if (!str_starts_with($key, '__phpunit_')) { continue; } unset($array[$key]); } return $array; } } PKAZC-$comparator/src/ComparisonFailure.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use RuntimeException; use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; final class ComparisonFailure extends RuntimeException { private mixed $expected; private mixed $actual; private string $expectedAsString; private string $actualAsString; public function __construct(mixed $expected, mixed $actual, string $expectedAsString, string $actualAsString, string $message = '') { parent::__construct($message); $this->expected = $expected; $this->actual = $actual; $this->expectedAsString = $expectedAsString; $this->actualAsString = $actualAsString; } public function getActual(): mixed { return $this->actual; } public function getExpected(): mixed { return $this->expected; } public function getActualAsString(): string { return $this->actualAsString; } public function getExpectedAsString(): string { return $this->expectedAsString; } public function getDiff(): string { if (!$this->actualAsString && !$this->expectedAsString) { return ''; } $differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); return $differ->diff($this->expectedAsString, $this->actualAsString); } public function toString(): string { return $this->getMessage() . $this->getDiff(); } } PKAZ#-comparator/src/SplObjectStorageComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function assert; use SebastianBergmann\Exporter\Exporter; use SplObjectStorage; final class SplObjectStorageComparator extends Comparator { public function accepts(mixed $expected, mixed $actual): bool { return $expected instanceof SplObjectStorage && $actual instanceof SplObjectStorage; } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false): void { assert($expected instanceof SplObjectStorage); assert($actual instanceof SplObjectStorage); $exporter = new Exporter; foreach ($actual as $object) { if (!$expected->contains($object)) { throw new ComparisonFailure( $expected, $actual, $exporter->export($expected), $exporter->export($actual), 'Failed asserting that two objects are equal.' ); } } foreach ($expected as $object) { if (!$actual->contains($object)) { throw new ComparisonFailure( $expected, $actual, $exporter->export($expected), $exporter->export($actual), 'Failed asserting that two objects are equal.' ); } } } } PKAZ+ [X X comparator/src/Factory.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function array_unshift; final class Factory { private static ?Factory $instance = null; /** * @psalm-var list */ private array $customComparators = []; /** * @psalm-var list */ private array $defaultComparators = []; public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self; // @codeCoverageIgnore } return self::$instance; } public function __construct() { $this->registerDefaultComparators(); } public function getComparatorFor(mixed $expected, mixed $actual): Comparator { foreach ($this->customComparators as $comparator) { if ($comparator->accepts($expected, $actual)) { return $comparator; } } foreach ($this->defaultComparators as $comparator) { if ($comparator->accepts($expected, $actual)) { return $comparator; } } throw new RuntimeException('No suitable Comparator implementation found'); } /** * Registers a new comparator. * * This comparator will be returned by getComparatorFor() if its accept() method * returns TRUE for the compared values. It has higher priority than the * existing comparators, meaning that its accept() method will be invoked * before those of the other comparators. */ public function register(Comparator $comparator): void { array_unshift($this->customComparators, $comparator); $comparator->setFactory($this); } /** * Unregisters a comparator. * * This comparator will no longer be considered by getComparatorFor(). */ public function unregister(Comparator $comparator): void { foreach ($this->customComparators as $key => $_comparator) { if ($comparator === $_comparator) { unset($this->customComparators[$key]); } } } public function reset(): void { $this->customComparators = []; } private function registerDefaultComparators(): void { $this->registerDefaultComparator(new MockObjectComparator); $this->registerDefaultComparator(new DateTimeComparator); $this->registerDefaultComparator(new DOMNodeComparator); $this->registerDefaultComparator(new SplObjectStorageComparator); $this->registerDefaultComparator(new ExceptionComparator); $this->registerDefaultComparator(new ObjectComparator); $this->registerDefaultComparator(new ResourceComparator); $this->registerDefaultComparator(new ArrayComparator); $this->registerDefaultComparator(new NumericComparator); $this->registerDefaultComparator(new ScalarComparator); $this->registerDefaultComparator(new TypeComparator); } private function registerDefaultComparator(Comparator $comparator): void { $this->defaultComparators[] = $comparator; $comparator->setFactory($this); } } PKAZQ6$comparator/src/NumericComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function abs; use function is_float; use function is_infinite; use function is_nan; use function is_numeric; use function is_string; use function sprintf; use SebastianBergmann\Exporter\Exporter; final class NumericComparator extends ScalarComparator { public function accepts(mixed $expected, mixed $actual): bool { // all numerical values, but not if both of them are strings return is_numeric($expected) && is_numeric($actual) && !(is_string($expected) && is_string($actual)); } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false): void { if ($this->isInfinite($actual) && $this->isInfinite($expected)) { return; } if (($this->isInfinite($actual) xor $this->isInfinite($expected)) || ($this->isNan($actual) || $this->isNan($expected)) || abs($actual - $expected) > $delta) { $exporter = new Exporter; throw new ComparisonFailure( $expected, $actual, '', '', sprintf( 'Failed asserting that %s matches expected %s.', $exporter->export($actual), $exporter->export($expected) ) ); } } private function isInfinite(mixed $value): bool { return is_float($value) && is_infinite($value); } private function isNan(mixed $value): bool { return is_float($value) && is_nan($value); } } PKAZqb  %comparator/src/DateTimeComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function abs; use function assert; use function floor; use function sprintf; use DateInterval; use DateTimeInterface; use DateTimeZone; final class DateTimeComparator extends ObjectComparator { public function accepts(mixed $expected, mixed $actual): bool { return ($expected instanceof DateTimeInterface) && ($actual instanceof DateTimeInterface); } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false, array &$processed = []): void { assert($expected instanceof DateTimeInterface); assert($actual instanceof DateTimeInterface); $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), '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'; } } PKAZ%comparator/src/ResourceComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function assert; use function is_resource; use SebastianBergmann\Exporter\Exporter; final class ResourceComparator extends Comparator { public function accepts(mixed $expected, mixed $actual): bool { return is_resource($expected) && is_resource($actual); } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false): void { assert(is_resource($expected)); assert(is_resource($actual)); $exporter = new Exporter; if ($actual != $expected) { throw new ComparisonFailure( $expected, $actual, $exporter->export($expected), $exporter->export($actual) ); } } } PKAZӃ.comparator/src/exceptions/RuntimeException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; final class RuntimeException extends \RuntimeException implements Exception { } PKAZt mm'comparator/src/exceptions/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use Throwable; interface Exception extends Throwable { } PKAZ2h&comparator/src/ExceptionComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function assert; use Exception; /** * Compares Exception instances for equality. */ final class ExceptionComparator extends ObjectComparator { public function accepts(mixed $expected, mixed $actual): bool { return $expected instanceof Exception && $actual instanceof Exception; } protected function toArray(object $object): array { assert($object instanceof Exception); $array = parent::toArray($object); unset( $array['file'], $array['line'], $array['trace'], $array['string'], $array['xdebug_message'] ); return $array; } } PKAZǫ33"comparator/src/ArrayComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function array_key_exists; use function assert; use function is_array; use function sort; use function sprintf; use function str_replace; use function trim; use SebastianBergmann\Exporter\Exporter; /** * Arrays are equal if they contain the same key-value pairs. * The order of the keys does not matter. * The types of key-value pairs do not matter. */ class ArrayComparator extends Comparator { public function accepts(mixed $expected, mixed $actual): bool { return is_array($expected) && is_array($actual); } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false, array &$processed = []): void { assert(is_array($expected)); assert(is_array($actual)); if ($canonicalize) { sort($expected); sort($actual); } $remaining = $actual; $actualAsString = "Array (\n"; $expectedAsString = "Array (\n"; $equal = true; $exporter = new Exporter; foreach ($expected as $key => $value) { unset($remaining[$key]); if (!array_key_exists($key, $actual)) { $expectedAsString .= sprintf( " %s => %s\n", $exporter->export($key), $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", $exporter->export($key), $exporter->shortenedExport($value) ); $actualAsString .= sprintf( " %s => %s\n", $exporter->export($key), $exporter->shortenedExport($actual[$key]) ); } catch (ComparisonFailure $e) { $expectedAsString .= sprintf( " %s => %s\n", $exporter->export($key), $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $exporter->shortenedExport($e->getExpected()) ); $actualAsString .= sprintf( " %s => %s\n", $exporter->export($key), $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $exporter->shortenedExport($e->getActual()) ); $equal = false; } } foreach ($remaining as $key => $value) { $actualAsString .= sprintf( " %s => %s\n", $exporter->export($key), $exporter->shortenedExport($value) ); $equal = false; } $expectedAsString .= ')'; $actualAsString .= ')'; if (!$equal) { throw new ComparisonFailure( $expected, $actual, $expectedAsString, $actualAsString, 'Failed asserting that two arrays are equal.' ); } } private function indent(string $lines): string { return trim(str_replace("\n", "\n ", $lines)); } } PKAZR7øA A #comparator/src/ObjectComparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; use function assert; use function in_array; use function is_object; use function sprintf; use function substr_replace; use SebastianBergmann\Exporter\Exporter; class ObjectComparator extends ArrayComparator { public function accepts(mixed $expected, mixed $actual): bool { return is_object($expected) && is_object($actual); } /** * @throws ComparisonFailure */ public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false, array &$processed = []): void { assert(is_object($expected)); assert(is_object($actual)); if ($actual::class !== $expected::class) { $exporter = new Exporter; throw new ComparisonFailure( $expected, $actual, $exporter->export($expected), $exporter->export($actual), sprintf( '%s is not instance of expected class "%s".', $exporter->export($actual), $expected::class ) ); } // 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(), $expected::class . ' Object', 0, 5), substr_replace($e->getActualAsString(), $actual::class . ' Object', 0, 5), 'Failed asserting that two objects are equal.' ); } } } protected function toArray(object $object): array { return (new Exporter)->toArray($object); } } PKAZ0tFFcomparator/src/Comparator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Comparator; abstract class Comparator { private Factory $factory; public function setFactory(Factory $factory): void { $this->factory = $factory; } abstract public function accepts(mixed $expected, mixed $actual): bool; /** * @throws ComparisonFailure */ abstract public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false): void; protected function factory(): Factory { return $this->factory; } } PKAZ;;comparator/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/comparator/v/stable.png)](https://packagist.org/packages/sebastian/comparator) [![CI Status](https://github.com/sebastianbergmann/comparator/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/comparator/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/comparator/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/comparator) [![codecov](https://codecov.io/gh/sebastianbergmann/comparator/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/comparator) # sebastian/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 getComparatorFor($date1, $date2); try { $comparator->assertEquals($date1, $date2); print "Dates match"; } catch (ComparisonFailure $failure) { print "Dates don't match"; } ``` PKAZgJJcomparator/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [5.0.1] - 2023-08-14 ### Fixed * `MockObjectComparator` only works on instances of `PHPUnit\Framework\MockObject\MockObject`, but not on instances of `PHPUnit\Framework\MockObject\Stub` * `MockObjectComparator` only ignores the `$__phpunit_invocationMocker` property, but not other properties with names prefixed with `__phpunit_` ## [5.0.0] - 2023-02-03 ### Changed * Methods now have parameter and return type declarations * `Comparator::$factory` is now private, use `Comparator::factory()` instead * `ComparisonFailure`, `DOMNodeComparator`, `DateTimeComparator`, `ExceptionComparator`, `MockObjectComparator`, `NumericComparator`, `ResourceComparator`, `SplObjectStorageComparator`, and `TypeComparator` are now `final` * `ScalarComparator` and `DOMNodeComparator` now use `mb_strtolower($string, 'UTF-8')` instead of `strtolower($string)` ### Removed * Removed `$identical` parameter from `ComparisonFailure::__construct()` * Removed `Comparator::$exporter` * Removed support for PHP 7.3, PHP 7.4, and PHP 8.0 ## [4.0.8] - 2022-09-14 ### Fixed * [#102](https://github.com/sebastianbergmann/comparator/pull/102): Fix `float` comparison precision ## [4.0.7] - 2022-09-14 ### Fixed * [#99](https://github.com/sebastianbergmann/comparator/pull/99): Fix weak comparison between `'0'` and `false` ## [4.0.6] - 2020-10-26 ### Fixed * `SebastianBergmann\Comparator\Exception` now correctly extends `\Throwable` ## [4.0.5] - 2020-09-30 ### Fixed * [#89](https://github.com/sebastianbergmann/comparator/pull/89): Handle PHP 8 `ValueError` ## [4.0.4] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [4.0.3] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [4.0.2] - 2020-06-15 ### Fixed * [#85](https://github.com/sebastianbergmann/comparator/issues/85): Version 4.0.1 breaks backward compatibility ## [4.0.1] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports ## [4.0.0] - 2020-02-07 ### Removed * Removed support for PHP 7.1 and PHP 7.2 ## [3.0.5] - 2022-09-14 ### Fixed * [#102](https://github.com/sebastianbergmann/comparator/pull/102): Fix `float` comparison precision ## [3.0.4] - 2022-09-14 ### Fixed * [#99](https://github.com/sebastianbergmann/comparator/pull/99): Fix weak comparison between `'0'` and `false` ## [3.0.3] - 2020-11-30 ### Changed * Changed PHP version constraint in `composer.json` from `^7.1` to `>=7.1` ## [3.0.2] - 2018-07-12 ### Changed * By default, `MockObjectComparator` is now tried before all other (default) comparators ## [3.0.1] - 2018-06-14 ### Fixed * [#53](https://github.com/sebastianbergmann/comparator/pull/53): `DOMNodeComparator` ignores `$ignoreCase` parameter * [#58](https://github.com/sebastianbergmann/comparator/pull/58): `ScalarComparator` does not handle extremely ugly string comparison edge cases ## [3.0.0] - 2018-04-18 ### Fixed * [#48](https://github.com/sebastianbergmann/comparator/issues/48): `DateTimeComparator` does not support fractional second deltas ### Removed * Removed support for PHP 7.0 ## [2.1.3] - 2018-02-01 ### Changed * This component is now compatible with version 3 of `sebastian/diff` ## [2.1.2] - 2018-01-12 ### Fixed * Fix comparison of `DateTimeImmutable` objects ## [2.1.1] - 2017-12-22 ### Fixed * [phpunit/#2923](https://github.com/sebastianbergmann/phpunit/issues/2923): Unexpected failed date matching ## [2.1.0] - 2017-11-03 ### Added * Added `SebastianBergmann\Comparator\Factory::reset()` to unregister all non-default comparators * Added support for `phpunit/phpunit-mock-objects` version `^5.0` [5.0.1]: https://github.com/sebastianbergmann/comparator/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/sebastianbergmann/comparator/compare/4.0.8...5.0.0 [4.0.8]: https://github.com/sebastianbergmann/comparator/compare/4.0.7...4.0.8 [4.0.7]: https://github.com/sebastianbergmann/comparator/compare/4.0.6...4.0.7 [4.0.6]: https://github.com/sebastianbergmann/comparator/compare/4.0.5...4.0.6 [4.0.5]: https://github.com/sebastianbergmann/comparator/compare/4.0.4...4.0.5 [4.0.4]: https://github.com/sebastianbergmann/comparator/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/comparator/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/comparator/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/comparator/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/comparator/compare/3.0.5...4.0.0 [3.0.5]: https://github.com/sebastianbergmann/comparator/compare/3.0.4...3.0.5 [3.0.4]: https://github.com/sebastianbergmann/comparator/compare/3.0.3...3.0.4 [3.0.3]: https://github.com/sebastianbergmann/comparator/compare/3.0.2...3.0.3 [3.0.2]: https://github.com/sebastianbergmann/comparator/compare/3.0.1...3.0.2 [3.0.1]: https://github.com/sebastianbergmann/comparator/compare/3.0.0...3.0.1 [3.0.0]: https://github.com/sebastianbergmann/comparator/compare/2.1.3...3.0.0 [2.1.3]: https://github.com/sebastianbergmann/comparator/compare/2.1.2...2.1.3 [2.1.2]: https://github.com/sebastianbergmann/comparator/compare/2.1.1...2.1.2 [2.1.1]: https://github.com/sebastianbergmann/comparator/compare/2.1.0...2.1.1 [2.1.0]: https://github.com/sebastianbergmann/comparator/compare/2.0.2...2.1.0 PKAZTcomparator/LICENSEnu[BSD 3-Clause License Copyright (c) 2002-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZpobject-reflector/composer.jsonnu[{ "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" } ], "prefer-stable": true, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture/" ] }, "extra": { "branch-alias": { "dev-main": "3.0-dev" } } } PKAZE >PPobject-reflector/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZt(object-reflector/src/ObjectReflector.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\ObjectReflector; use function count; use function explode; final class ObjectReflector { /** * @psalm-return array */ public function getProperties(object $object): array { $properties = []; $className = $object::class; foreach ((array) $object as $name => $value) { $name = explode("\0", (string) $name); if (count($name) === 1) { $name = $name[0]; } elseif ($name[1] !== $className) { $name = $name[1] . '::' . $name[2]; } else { $name = $name[2]; } $properties[$name] = $value; } return $properties; } } PKAZɆobject-reflector/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/object-reflector/v/stable.png)](https://packagist.org/packages/sebastian/object-reflector) [![CI Status](https://github.com/sebastianbergmann/object-reflector/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/object-reflector/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/object-reflector/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/object-reflector) [![codecov](https://codecov.io/gh/sebastianbergmann/object-reflector/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/object-reflector) # sebastian/object-reflector Allows reflection of object properties, including inherited and private as well as protected 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 ``` PKAZaobject-reflector/ChangeLog.mdnu[# Change Log All notable changes to `sebastianbergmann/object-reflector` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [3.0.0] - 2023-02-03 ### Changed * `ObjectReflector::getAttributes()` has been renamed to `ObjectReflector::getProperties()` ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0 ## [2.0.4] - 2020-10-26 ### Fixed * `SebastianBergmann\ObjectReflector\Exception` now correctly extends `\Throwable` ## [2.0.3] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [2.0.2] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [2.0.1] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports ## [2.0.0] - 2020-02-07 ### Removed * This component is no longer supported on PHP 7.0, PHP 7.1, and PHP 7.2 ## [1.1.1] - 2017-03-29 * Fixed [#1](https://github.com/sebastianbergmann/object-reflector/issues/1): Attributes 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 [3.0.0]: https://github.com/sebastianbergmann/object-reflector/compare/2.0.4...3.0.0 [2.0.4]: https://github.com/sebastianbergmann/object-reflector/compare/2.0.3...2.0.4 [2.0.3]: https://github.com/sebastianbergmann/object-reflector/compare/2.0.2...2.0.3 [2.0.2]: https://github.com/sebastianbergmann/object-reflector/compare/2.0.1...2.0.2 [2.0.1]: https://github.com/sebastianbergmann/object-reflector/compare/2.0.0...2.0.1 [2.0.0]: https://github.com/sebastianbergmann/object-reflector/compare/1.1.1...2.0.0 [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 PKAZR6object-reflector/LICENSEnu[BSD 3-Clause License Copyright (c) 2017-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZˆcode-unit/composer.jsonnu[{ "name": "sebastian/code-unit", "description": "Collection of value objects that represent the PHP code units", "type": "library", "homepage": "https://github.com/sebastianbergmann/code-unit", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues" }, "prefer-stable": true, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture" ], "files": [ "tests/_fixture/file_with_multiple_code_units.php", "tests/_fixture/function.php" ] }, "extra": { "branch-alias": { "dev-main": "2.0-dev" } } } PKAZE >PPcode-unit/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZ%|UU,code-unit/src/CodeUnitCollectionIterator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use Iterator; /** * @template-implements Iterator */ final class CodeUnitCollectionIterator implements Iterator { /** * @psalm-var list */ private array $codeUnits; private int $position = 0; public function __construct(CodeUnitCollection $collection) { $this->codeUnits = $collection->asArray(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return isset($this->codeUnits[$this->position]); } public function key(): int { return $this->position; } public function current(): CodeUnit { return $this->codeUnits[$this->position]; } public function next(): void { $this->position++; } } PKAZ 1Z  !code-unit/src/TraitMethodUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class TraitMethodUnit extends CodeUnit { /** * @psalm-assert-if-true TraitMethodUnit $this */ public function isTraitMethod(): bool { return true; } } PKAZN'$code-unit/src/CodeUnitCollection.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use function array_merge; use function count; use Countable; use IteratorAggregate; /** * @template-implements IteratorAggregate * * @psalm-immutable */ final class CodeUnitCollection implements Countable, IteratorAggregate { /** * @psalm-var list */ private readonly array $codeUnits; public static function fromList(CodeUnit ...$codeUnits): self { return new self($codeUnits); } /** * @psalm-param list $codeUnits */ private function __construct(array $codeUnits) { $this->codeUnits = $codeUnits; } /** * @psalm-return list */ public function asArray(): array { return $this->codeUnits; } public function getIterator(): CodeUnitCollectionIterator { return new CodeUnitCollectionIterator($this); } public function count(): int { return count($this->codeUnits); } public function isEmpty(): bool { return empty($this->codeUnits); } public function mergeWith(self $other): self { return new self( array_merge( $this->asArray(), $other->asArray() ) ); } } PKAZJ  !code-unit/src/ClassMethodUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class ClassMethodUnit extends CodeUnit { /** * @psalm-assert-if-true ClassMethodUnit $this */ public function isClassMethod(): bool { return true; } } PKAZm=code-unit/src/ClassUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class ClassUnit extends CodeUnit { /** * @psalm-assert-if-true ClassUnit $this */ public function isClass(): bool { return true; } } PKAZl`%code-unit/src/InterfaceMethodUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class InterfaceMethodUnit extends CodeUnit { /** * @psalm-assert-if-true InterfaceMethod $this */ public function isInterfaceMethod(): bool { return true; } } PKAZ=%tcode-unit/src/InterfaceUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class InterfaceUnit extends CodeUnit { /** * @psalm-assert-if-true InterfaceUnit $this */ public function isInterface(): bool { return true; } } PKAZcode-unit/src/FileUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class FileUnit extends CodeUnit { /** * @psalm-assert-if-true FileUnit $this */ public function isFile(): bool { return true; } } PKAZTJ˿code-unit/src/TraitUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class TraitUnit extends CodeUnit { /** * @psalm-assert-if-true TraitUnit $this */ public function isTrait(): bool { return true; } } PKAZ6*a5code-unit/src/exceptions/InvalidCodeUnitException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use RuntimeException; final class InvalidCodeUnitException extends RuntimeException implements Exception { } PKAZO0code-unit/src/exceptions/ReflectionException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use RuntimeException; final class ReflectionException extends RuntimeException implements Exception { } PKAZwkjj&code-unit/src/exceptions/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use Throwable; interface Exception extends Throwable { } PKAZΖ-code-unit/src/exceptions/NoTraitException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use RuntimeException; final class NoTraitException extends RuntimeException implements Exception { } PKAZ=i3030code-unit/src/CodeUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use function count; use function file; use function file_exists; use function is_readable; use function range; use function sprintf; use ReflectionClass; use ReflectionFunction; use ReflectionMethod; /** * @psalm-immutable */ abstract class CodeUnit { private readonly string $name; private readonly string $sourceFileName; /** * @psalm-var list */ private readonly array $sourceLines; /** * @psalm-param class-string $className * * @throws InvalidCodeUnitException * @throws ReflectionException */ public static function forClass(string $className): ClassUnit { self::ensureUserDefinedClass($className); $reflector = self::reflectorForClass($className); return new ClassUnit( $className, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } /** * @psalm-param class-string $className * * @throws InvalidCodeUnitException * @throws ReflectionException */ public static function forClassMethod(string $className, string $methodName): ClassMethodUnit { self::ensureUserDefinedClass($className); $reflector = self::reflectorForClassMethod($className, $methodName); return new ClassMethodUnit( $className . '::' . $methodName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } /** * @throws InvalidCodeUnitException */ public static function forFileWithAbsolutePath(string $path): FileUnit { self::ensureFileExistsAndIsReadable($path); return new FileUnit( $path, $path, range( 1, count(file($path)) ) ); } /** * @psalm-param class-string $interfaceName * * @throws InvalidCodeUnitException * @throws ReflectionException */ public static function forInterface(string $interfaceName): InterfaceUnit { self::ensureUserDefinedInterface($interfaceName); $reflector = self::reflectorForClass($interfaceName); return new InterfaceUnit( $interfaceName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } /** * @psalm-param class-string $interfaceName * * @throws InvalidCodeUnitException * @throws ReflectionException */ public static function forInterfaceMethod(string $interfaceName, string $methodName): InterfaceMethodUnit { self::ensureUserDefinedInterface($interfaceName); $reflector = self::reflectorForClassMethod($interfaceName, $methodName); return new InterfaceMethodUnit( $interfaceName . '::' . $methodName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } /** * @psalm-param class-string $traitName * * @throws InvalidCodeUnitException * @throws ReflectionException */ public static function forTrait(string $traitName): TraitUnit { self::ensureUserDefinedTrait($traitName); $reflector = self::reflectorForClass($traitName); return new TraitUnit( $traitName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } /** * @psalm-param class-string $traitName * * @throws InvalidCodeUnitException * @throws ReflectionException */ public static function forTraitMethod(string $traitName, string $methodName): TraitMethodUnit { self::ensureUserDefinedTrait($traitName); $reflector = self::reflectorForClassMethod($traitName, $methodName); return new TraitMethodUnit( $traitName . '::' . $methodName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } /** * @psalm-param callable-string $functionName * * @throws InvalidCodeUnitException * @throws ReflectionException */ public static function forFunction(string $functionName): FunctionUnit { $reflector = self::reflectorForFunction($functionName); if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined function', $functionName ) ); } return new FunctionUnit( $functionName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } /** * @psalm-param list $sourceLines */ private function __construct(string $name, string $sourceFileName, array $sourceLines) { $this->name = $name; $this->sourceFileName = $sourceFileName; $this->sourceLines = $sourceLines; } public function name(): string { return $this->name; } public function sourceFileName(): string { return $this->sourceFileName; } /** * @psalm-return list */ public function sourceLines(): array { return $this->sourceLines; } public function isClass(): bool { return false; } public function isClassMethod(): bool { return false; } public function isInterface(): bool { return false; } public function isInterfaceMethod(): bool { return false; } public function isTrait(): bool { return false; } public function isTraitMethod(): bool { return false; } public function isFunction(): bool { return false; } public function isFile(): bool { return false; } /** * @throws InvalidCodeUnitException */ private static function ensureFileExistsAndIsReadable(string $path): void { if (!(file_exists($path) && is_readable($path))) { throw new InvalidCodeUnitException( sprintf( 'File "%s" does not exist or is not readable', $path ) ); } } /** * @psalm-param class-string $className * * @throws InvalidCodeUnitException */ private static function ensureUserDefinedClass(string $className): void { try { $reflector = new ReflectionClass($className); if ($reflector->isInterface()) { throw new InvalidCodeUnitException( sprintf( '"%s" is an interface and not a class', $className ) ); } if ($reflector->isTrait()) { throw new InvalidCodeUnitException( sprintf( '"%s" is a trait and not a class', $className ) ); } if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined class', $className ) ); } // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @psalm-param class-string $interfaceName * * @throws InvalidCodeUnitException */ private static function ensureUserDefinedInterface(string $interfaceName): void { try { $reflector = new ReflectionClass($interfaceName); if (!$reflector->isInterface()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not an interface', $interfaceName ) ); } if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined interface', $interfaceName ) ); } // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @psalm-param class-string $traitName * * @throws InvalidCodeUnitException */ private static function ensureUserDefinedTrait(string $traitName): void { try { $reflector = new ReflectionClass($traitName); if (!$reflector->isTrait()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a trait', $traitName ) ); } // @codeCoverageIgnoreStart if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined trait', $traitName ) ); } } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @psalm-param class-string $className * * @throws ReflectionException */ private static function reflectorForClass(string $className): ReflectionClass { try { return new ReflectionClass($className); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @psalm-param class-string $className * * @throws ReflectionException */ private static function reflectorForClassMethod(string $className, string $methodName): ReflectionMethod { try { return new ReflectionMethod($className, $methodName); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @psalm-param callable-string $functionName * * @throws ReflectionException */ private static function reflectorForFunction(string $functionName): ReflectionFunction { try { return new ReflectionFunction($functionName); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } } PKAZXVXcode-unit/src/Mapper.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; use function array_keys; use function array_merge; use function array_unique; use function array_values; use function class_exists; use function explode; use function function_exists; use function interface_exists; use function ksort; use function method_exists; use function sort; use function sprintf; use function str_contains; use function trait_exists; use ReflectionClass; use ReflectionFunction; use ReflectionMethod; final class Mapper { /** * @psalm-return array> */ public function codeUnitsToSourceLines(CodeUnitCollection $codeUnits): array { $result = []; foreach ($codeUnits as $codeUnit) { $sourceFileName = $codeUnit->sourceFileName(); if (!isset($result[$sourceFileName])) { $result[$sourceFileName] = []; } $result[$sourceFileName] = array_merge($result[$sourceFileName], $codeUnit->sourceLines()); } foreach (array_keys($result) as $sourceFileName) { $result[$sourceFileName] = array_values(array_unique($result[$sourceFileName])); sort($result[$sourceFileName]); } ksort($result); return $result; } /** * @throws InvalidCodeUnitException * @throws ReflectionException */ public function stringToCodeUnits(string $unit): CodeUnitCollection { if (str_contains($unit, '::')) { [$firstPart, $secondPart] = explode('::', $unit); if ($this->isUserDefinedFunction($secondPart)) { return CodeUnitCollection::fromList(CodeUnit::forFunction($secondPart)); } if ($this->isUserDefinedMethod($firstPart, $secondPart)) { return CodeUnitCollection::fromList(CodeUnit::forClassMethod($firstPart, $secondPart)); } if ($this->isUserDefinedInterface($firstPart)) { return CodeUnitCollection::fromList(CodeUnit::forInterfaceMethod($firstPart, $secondPart)); } if ($this->isUserDefinedTrait($firstPart)) { return CodeUnitCollection::fromList(CodeUnit::forTraitMethod($firstPart, $secondPart)); } } else { if ($this->isUserDefinedClass($unit)) { $units = [CodeUnit::forClass($unit)]; foreach ($this->reflectorForClass($unit)->getTraits() as $trait) { if (!$trait->isUserDefined()) { // @codeCoverageIgnoreStart continue; // @codeCoverageIgnoreEnd } $units[] = CodeUnit::forTrait($trait->getName()); } return CodeUnitCollection::fromList(...$units); } if ($this->isUserDefinedInterface($unit)) { return CodeUnitCollection::fromList(CodeUnit::forInterface($unit)); } if ($this->isUserDefinedTrait($unit)) { return CodeUnitCollection::fromList(CodeUnit::forTrait($unit)); } if ($this->isUserDefinedFunction($unit)) { return CodeUnitCollection::fromList(CodeUnit::forFunction($unit)); } } throw new InvalidCodeUnitException( sprintf( '"%s" is not a valid code unit', $unit ) ); } /** * @psalm-param class-string $className * * @throws ReflectionException */ private function reflectorForClass(string $className): ReflectionClass { try { return new ReflectionClass($className); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @throws ReflectionException */ private function isUserDefinedFunction(string $functionName): bool { if (!function_exists($functionName)) { return false; } try { return (new ReflectionFunction($functionName))->isUserDefined(); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @throws ReflectionException */ private function isUserDefinedClass(string $className): bool { if (!class_exists($className)) { return false; } try { return (new ReflectionClass($className))->isUserDefined(); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @throws ReflectionException */ private function isUserDefinedInterface(string $interfaceName): bool { if (!interface_exists($interfaceName)) { return false; } try { return (new ReflectionClass($interfaceName))->isUserDefined(); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @throws ReflectionException */ private function isUserDefinedTrait(string $traitName): bool { if (!trait_exists($traitName)) { return false; } try { return (new ReflectionClass($traitName))->isUserDefined(); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } /** * @throws ReflectionException */ private function isUserDefinedMethod(string $className, string $methodName): bool { if (!class_exists($className)) { // @codeCoverageIgnoreStart return false; // @codeCoverageIgnoreEnd } if (!method_exists($className, $methodName)) { // @codeCoverageIgnoreStart return false; // @codeCoverageIgnoreEnd } try { return (new ReflectionMethod($className, $methodName))->isUserDefined(); // @codeCoverageIgnoreStart } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), $e->getCode(), $e ); } // @codeCoverageIgnoreEnd } } PKAZrYcode-unit/src/FunctionUnit.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnit; /** * @psalm-immutable */ final class FunctionUnit extends CodeUnit { /** * @psalm-assert-if-true FunctionUnit $this */ public function isFunction(): bool { return true; } } PKAZsρcode-unit/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/code-unit/v/stable.png)](https://packagist.org/packages/sebastian/code-unit) [![CI Status](https://github.com/sebastianbergmann/code-unit/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/code-unit/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/code-unit/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/code-unit) [![codecov](https://codecov.io/gh/sebastianbergmann/code-unit/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/code-unit) # sebastian/code-unit Collection of value objects that represent the PHP code units. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/code-unit ``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: ``` composer require --dev sebastian/code-unit ``` PKAZG3 code-unit/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [2.0.0] - 2023-02-03 ### Added * Added `SebastianBergmann\CodeUnit\FileUnit` value object that represents a sourcecode file ### Removed * `SebastianBergmann\CodeUnit\CodeUnitCollection::fromArray()` has been removed * `SebastianBergmann\CodeUnit\Mapper::stringToCodeUnits()` no longer supports `ClassName<*>` * This component is no longer supported on PHP 7.3, PHP 7.4, and PHP 8.0 ## [1.0.8] - 2020-10-26 ### Fixed * `SebastianBergmann\CodeUnit\Exception` now correctly extends `\Throwable` ## [1.0.7] - 2020-10-02 ### Fixed * `SebastianBergmann\CodeUnit\Mapper::stringToCodeUnits()` no longer attempts to create `CodeUnit` objects for code units that are not declared in userland ## [1.0.6] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [1.0.5] - 2020-06-26 ### Fixed * [#3](https://github.com/sebastianbergmann/code-unit/issues/3): Regression in 1.0.4 ## [1.0.4] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [1.0.3] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports ## [1.0.2] - 2020-04-30 ### Fixed * `Mapper::stringToCodeUnits()` raised the wrong exception for `Class::method` when a class named `Class` exists but does not have a method named `method` ## [1.0.1] - 2020-04-27 ### Fixed * [#2](https://github.com/sebastianbergmann/code-unit/issues/2): `Mapper::stringToCodeUnits()` breaks when `ClassName` is used for class that extends built-in class ## [1.0.0] - 2020-03-30 * Initial release [2.0.0]: https://github.com/sebastianbergmann/code-unit/compare/1.0.8...2.0.0 [1.0.8]: https://github.com/sebastianbergmann/code-unit/compare/1.0.7...1.0.8 [1.0.7]: https://github.com/sebastianbergmann/code-unit/compare/1.0.6...1.0.7 [1.0.6]: https://github.com/sebastianbergmann/code-unit/compare/1.0.5...1.0.6 [1.0.5]: https://github.com/sebastianbergmann/code-unit/compare/1.0.4...1.0.5 [1.0.4]: https://github.com/sebastianbergmann/code-unit/compare/1.0.3...1.0.4 [1.0.3]: https://github.com/sebastianbergmann/code-unit/compare/1.0.2...1.0.3 [1.0.2]: https://github.com/sebastianbergmann/code-unit/compare/1.0.1...1.0.2 [1.0.1]: https://github.com/sebastianbergmann/code-unit/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/sebastianbergmann/code-unit/compare/530c3900e5db9bcb8516da545bef0d62536cedaa...1.0.0 PKAZP@code-unit/LICENSEnu[BSD 3-Clause License Copyright (c) 2020-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZy}33complexity/composer.jsonnu[{ "name": "sebastian/complexity", "description": "Library for calculating the complexity of PHP code units", "type": "library", "homepage": "https://github.com/sebastianbergmann/complexity", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy" }, "prefer-stable": true, "require": { "php": ">=8.1", "nikic/php-parser": "^4.10" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-main": "3.1-dev" } } } PKAZKJuucomplexity/SECURITY.mdnu[# Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. PKAZ%#2 7complexity/src/Visitor/ComplexityCalculatingVisitor.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function assert; use function is_array; use PhpParser\Node; use PhpParser\Node\Expr\New_; use PhpParser\Node\Name; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Trait_; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; final class ComplexityCalculatingVisitor extends NodeVisitorAbstract { /** * @psalm-var list */ private array $result = []; private bool $shortCircuitTraversal; public function __construct(bool $shortCircuitTraversal) { $this->shortCircuitTraversal = $shortCircuitTraversal; } public function enterNode(Node $node): ?int { if (!$node instanceof ClassMethod && !$node instanceof Function_) { return null; } if ($node instanceof ClassMethod) { if ($node->getAttribute('parent') instanceof Interface_) { return null; } if ($node->isAbstract()) { return null; } $name = $this->classMethodName($node); } else { $name = $this->functionName($node); } $statements = $node->getStmts(); assert(is_array($statements)); $this->result[] = new Complexity( $name, $this->cyclomaticComplexity($statements), ); if ($this->shortCircuitTraversal) { return NodeTraverser::DONT_TRAVERSE_CHILDREN; } return null; } public function result(): ComplexityCollection { return ComplexityCollection::fromList(...$this->result); } /** * @param Stmt[] $statements * * @psalm-return positive-int */ private function cyclomaticComplexity(array $statements): int { $traverser = new NodeTraverser; $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor; $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); /* @noinspection UnusedFunctionResultInspection */ $traverser->traverse($statements); return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } /** * @psalm-return non-empty-string */ private function classMethodName(ClassMethod $node): string { $parent = $node->getAttribute('parent'); assert($parent instanceof Class_ || $parent instanceof Trait_); if ($parent->getAttribute('parent') instanceof New_) { return 'anonymous class'; } assert(isset($parent->namespacedName)); assert($parent->namespacedName instanceof Name); return $parent->namespacedName->toString() . '::' . $node->name->toString(); } /** * @psalm-return non-empty-string */ private function functionName(Function_ $node): string { assert(isset($node->namespacedName)); assert($node->namespacedName instanceof Name); $functionName = $node->namespacedName->toString(); assert($functionName !== ''); return $functionName; } } PKAZ;ɽ,Acomplexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use PhpParser\Node; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; use PhpParser\Node\Expr\BinaryOp\BooleanOr; use PhpParser\Node\Expr\BinaryOp\LogicalAnd; use PhpParser\Node\Expr\BinaryOp\LogicalOr; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Stmt\Case_; use PhpParser\Node\Stmt\Catch_; use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\While_; use PhpParser\NodeVisitorAbstract; final class CyclomaticComplexityCalculatingVisitor extends NodeVisitorAbstract { /** * @psalm-var positive-int */ private int $cyclomaticComplexity = 1; public function enterNode(Node $node): void { switch ($node::class) { case BooleanAnd::class: case BooleanOr::class: case Case_::class: case Catch_::class: case ElseIf_::class: case For_::class: case Foreach_::class: case If_::class: case LogicalAnd::class: case LogicalOr::class: case Ternary::class: case While_::class: $this->cyclomaticComplexity++; } } /** * @psalm-return positive-int */ public function cyclomaticComplexity(): int { return $this->cyclomaticComplexity; } } PKAZ ;;(complexity/src/Complexity/Complexity.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function str_contains; /** * @psalm-immutable */ final class Complexity { /** * @psalm-var non-empty-string */ private readonly string $name; /** * @psalm-var positive-int */ private int $cyclomaticComplexity; /** * @psalm-param non-empty-string $name * @psalm-param positive-int $cyclomaticComplexity */ public function __construct(string $name, int $cyclomaticComplexity) { $this->name = $name; $this->cyclomaticComplexity = $cyclomaticComplexity; } /** * @psalm-return non-empty-string */ public function name(): string { return $this->name; } /** * @psalm-return positive-int */ public function cyclomaticComplexity(): int { return $this->cyclomaticComplexity; } public function isFunction(): bool { return !$this->isMethod(); } public function isMethod(): bool { return str_contains($this->name, '::'); } } PKAZZ 2complexity/src/Complexity/ComplexityCollection.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function array_filter; use function array_merge; use function array_values; use function count; use Countable; use IteratorAggregate; /** * @psalm-immutable */ final class ComplexityCollection implements Countable, IteratorAggregate { /** * @psalm-var list */ private readonly array $items; public static function fromList(Complexity ...$items): self { return new self($items); } /** * @psalm-param list $items */ private function __construct(array $items) { $this->items = $items; } /** * @psalm-return list */ public function asArray(): array { return $this->items; } public function getIterator(): ComplexityCollectionIterator { return new ComplexityCollectionIterator($this); } /** * @psalm-return non-negative-int */ public function count(): int { return count($this->items); } public function isEmpty(): bool { return empty($this->items); } /** * @psalm-return non-negative-int */ public function cyclomaticComplexity(): int { $cyclomaticComplexity = 0; foreach ($this as $item) { $cyclomaticComplexity += $item->cyclomaticComplexity(); } return $cyclomaticComplexity; } public function isFunction(): self { return new self( array_values( array_filter( $this->items, static fn (Complexity $complexity): bool => $complexity->isFunction(), ), ), ); } public function isMethod(): self { return new self( array_values( array_filter( $this->items, static fn (Complexity $complexity): bool => $complexity->isMethod(), ), ), ); } public function mergeWith(self $other): self { return new self( array_merge( $this->asArray(), $other->asArray(), ), ); } } PKAZh:complexity/src/Complexity/ComplexityCollectionIterator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use Iterator; final class ComplexityCollectionIterator implements Iterator { /** * @psalm-var list */ private readonly array $items; private int $position = 0; public function __construct(ComplexityCollection $items) { $this->items = $items->asArray(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return isset($this->items[$this->position]); } public function key(): int { return $this->position; } public function current(): Complexity { return $this->items[$this->position]; } public function next(): void { $this->position++; } } PKAZlE-complexity/src/Exception/RuntimeException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; final class RuntimeException extends \RuntimeException implements Exception { } PKAZ-mm&complexity/src/Exception/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use Throwable; interface Exception extends Throwable { } PKAZDZ complexity/src/Calculator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Complexity; use function assert; use function file_get_contents; use PhpParser\Error; use PhpParser\Lexer; use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor\NameResolver; use PhpParser\NodeVisitor\ParentConnectingVisitor; use PhpParser\Parser; use PhpParser\ParserFactory; final class Calculator { /** * @throws RuntimeException */ public function calculateForSourceFile(string $sourceFile): ComplexityCollection { return $this->calculateForSourceString(file_get_contents($sourceFile)); } /** * @throws RuntimeException */ public function calculateForSourceString(string $source): ComplexityCollection { try { $nodes = $this->parser()->parse($source); assert($nodes !== null); return $this->calculateForAbstractSyntaxTree($nodes); // @codeCoverageIgnoreStart } catch (Error $error) { throw new RuntimeException( $error->getMessage(), $error->getCode(), $error, ); } // @codeCoverageIgnoreEnd } /** * @param Node[] $nodes * * @throws RuntimeException */ public function calculateForAbstractSyntaxTree(array $nodes): ComplexityCollection { $traverser = new NodeTraverser; $complexityCalculatingVisitor = new ComplexityCalculatingVisitor(true); $traverser->addVisitor(new NameResolver); $traverser->addVisitor(new ParentConnectingVisitor); $traverser->addVisitor($complexityCalculatingVisitor); try { /* @noinspection UnusedFunctionResultInspection */ $traverser->traverse($nodes); // @codeCoverageIgnoreStart } catch (Error $error) { throw new RuntimeException( $error->getMessage(), $error->getCode(), $error, ); } // @codeCoverageIgnoreEnd return $complexityCalculatingVisitor->result(); } private function parser(): Parser { return (new ParserFactory)->create(ParserFactory::PREFER_PHP7, new Lexer); } } PKAZ[complexity/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/complexity/v/stable.png)](https://packagist.org/packages/sebastian/complexity) [![CI Status](https://github.com/sebastianbergmann/complexity/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/complexity/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/complexity/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/complexity) [![codecov](https://codecov.io/gh/sebastianbergmann/complexity/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/complexity) # sebastian/complexity Library for calculating the complexity of PHP code units. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/complexity ``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: ``` composer require --dev sebastian/complexity ``` PKAZP?complexity/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. ## [3.1.0] - 2023-09-28 ### Added * `Complexity::isFunction()` and `Complexity::isMethod()` * `ComplexityCollection::isFunction()` and `ComplexityCollection::isMethod()` * `ComplexityCollection::mergeWith()` ### Fixed * Anonymous classes are not processed correctly ## [3.0.1] - 2023-08-31 ### Fixed * [#7](https://github.com/sebastianbergmann/complexity/pull/7): `ComplexityCalculatingVisitor` tries to process interface methods ## [3.0.0] - 2023-02-03 ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0 ## [2.0.2] - 2020-10-26 ### Fixed * `SebastianBergmann\Complexity\Exception` now correctly extends `\Throwable` ## [2.0.1] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [2.0.0] - 2020-07-25 ### Removed * The `ParentConnectingVisitor` has been removed (it should have been marked as `@internal`) ## [1.0.0] - 2020-07-22 * Initial release [3.1.0]: https://github.com/sebastianbergmann/complexity/compare/3.0.1...3.1.0 [3.0.1]: https://github.com/sebastianbergmann/complexity/compare/3.0.0...3.0.1 [3.0.0]: https://github.com/sebastianbergmann/complexity/compare/2.0.2...3.0.0 [2.0.2]: https://github.com/sebastianbergmann/complexity/compare/2.0.1...2.0.2 [2.0.1]: https://github.com/sebastianbergmann/complexity/compare/2.0.0...2.0.1 [2.0.0]: https://github.com/sebastianbergmann/complexity/compare/1.0.0...2.0.0 [1.0.0]: https://github.com/sebastianbergmann/complexity/compare/70ee0ad32d9e2be3f85beffa3e2eb474193f2487...1.0.0 PKAZP@complexity/LICENSEnu[BSD 3-Clause License Copyright (c) 2020-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZS깞cli-parser/composer.jsonnu[{ "name": "sebastian/cli-parser", "description": "Library for parsing CLI options", "type": "library", "homepage": "https://github.com/sebastianbergmann/cli-parser", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues" }, "prefer-stable": true, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-main": "2.0-dev" } } } PKAZE >PPcli-parser/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZ@1ll'cli-parser/src/exceptions/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CliParser; use Throwable; interface Exception extends Throwable { } PKAZ5Acli-parser/src/exceptions/OptionDoesNotAllowArgumentException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CliParser; use function sprintf; use RuntimeException; final class OptionDoesNotAllowArgumentException extends RuntimeException implements Exception { public function __construct(string $option) { parent::__construct( sprintf( 'Option "%s" does not allow an argument', $option ) ); } } PKAZw~Dcli-parser/src/exceptions/RequiredOptionArgumentMissingException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CliParser; use function sprintf; use RuntimeException; final class RequiredOptionArgumentMissingException extends RuntimeException implements Exception { public function __construct(string $option) { parent::__construct( sprintf( 'Required argument for option "%s" is missing', $option ) ); } } PKAZ=6cli-parser/src/exceptions/AmbiguousOptionException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CliParser; use function sprintf; use RuntimeException; final class AmbiguousOptionException extends RuntimeException implements Exception { public function __construct(string $option) { parent::__construct( sprintf( 'Option "%s" is ambiguous', $option ) ); } } PKAZzz4cli-parser/src/exceptions/UnknownOptionException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CliParser; use function sprintf; use RuntimeException; final class UnknownOptionException extends RuntimeException implements Exception { public function __construct(string $option) { parent::__construct( sprintf( 'Unknown option "%s"', $option ) ); } } PKAZcli-parser/src/Parser.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CliParser; use function array_map; use function array_merge; use function array_shift; use function array_slice; use function assert; use function count; use function current; use function explode; use function is_array; use function is_int; use function is_string; use function key; use function next; use function preg_replace; use function reset; use function sort; use function str_ends_with; use function str_starts_with; use function strlen; use function strstr; use function substr; final class Parser { /** * @psalm-param list $argv * @psalm-param list $longOptions * * @psalm-return array{0: array, 1: array} * * @throws AmbiguousOptionException * @throws OptionDoesNotAllowArgumentException * @throws RequiredOptionArgumentMissingException * @throws UnknownOptionException */ public function parse(array $argv, string $shortOptions, array $longOptions = null): array { if (empty($argv)) { return [[], []]; } $options = []; $nonOptions = []; if ($longOptions) { sort($longOptions); } if (isset($argv[0][0]) && $argv[0][0] !== '-') { array_shift($argv); } reset($argv); $argv = array_map('trim', $argv); while (false !== $arg = current($argv)) { $i = key($argv); assert(is_int($i)); next($argv); if ($arg === '') { continue; } if ($arg === '--') { $nonOptions = array_merge($nonOptions, array_slice($argv, $i + 1)); break; } if ($arg[0] !== '-' || (strlen($arg) > 1 && $arg[1] === '-' && !$longOptions)) { $nonOptions[] = $arg; continue; } if (strlen($arg) > 1 && $arg[1] === '-' && is_array($longOptions)) { $this->parseLongOption( substr($arg, 2), $longOptions, $options, $argv ); continue; } $this->parseShortOption( substr($arg, 1), $shortOptions, $options, $argv ); } return [$options, $nonOptions]; } /** * @throws RequiredOptionArgumentMissingException */ private function parseShortOption(string $argument, string $shortOptions, array &$options, array &$argv): void { $argumentLength = strlen($argument); for ($i = 0; $i < $argumentLength; $i++) { $option = $argument[$i]; $optionArgument = null; if ($argument[$i] === ':' || ($spec = strstr($shortOptions, $option)) === false) { throw new UnknownOptionException('-' . $option); } if (strlen($spec) > 1 && $spec[1] === ':') { if ($i + 1 < $argumentLength) { $options[] = [$option, substr($argument, $i + 1)]; break; } if (!(strlen($spec) > 2 && $spec[2] === ':')) { $optionArgument = current($argv); if (!$optionArgument) { throw new RequiredOptionArgumentMissingException('-' . $option); } assert(is_string($optionArgument)); next($argv); } } $options[] = [$option, $optionArgument]; } } /** * @psalm-param list $longOptions * * @throws AmbiguousOptionException * @throws OptionDoesNotAllowArgumentException * @throws RequiredOptionArgumentMissingException * @throws UnknownOptionException */ private function parseLongOption(string $argument, array $longOptions, array &$options, array &$argv): void { $count = count($longOptions); $list = explode('=', $argument); $option = $list[0]; $optionArgument = null; if (count($list) > 1) { $optionArgument = $list[1]; } $optionLength = strlen($option); foreach ($longOptions as $i => $longOption) { $opt_start = substr($longOption, 0, $optionLength); if ($opt_start !== $option) { continue; } $opt_rest = substr($longOption, $optionLength); if ($opt_rest !== '' && $i + 1 < $count && $option[0] !== '=' && str_starts_with($longOptions[$i + 1], $option)) { throw new AmbiguousOptionException('--' . $option); } if (str_ends_with($longOption, '=')) { if (!str_ends_with($longOption, '==') && !strlen((string) $optionArgument)) { if (false === $optionArgument = current($argv)) { throw new RequiredOptionArgumentMissingException('--' . $option); } next($argv); } } elseif ($optionArgument) { throw new OptionDoesNotAllowArgumentException('--' . $option); } $fullOption = '--' . preg_replace('/={1,2}$/', '', $longOption); $options[] = [$fullOption, $optionArgument]; return; } throw new UnknownOptionException('--' . $option); } } PKAZb++cli-parser/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/cli-parser/v/stable.png)](https://packagist.org/packages/sebastian/cli-parser) [![CI Status](https://github.com/sebastianbergmann/cli-parser/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/cli-parser/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/cli-parser/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/cli-parser) [![codecov](https://codecov.io/gh/sebastianbergmann/cli-parser/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/cli-parser) # sebastian/cli-parser Library for parsing `$_SERVER['argv']`, extracted from `phpunit/phpunit`. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/cli-parser ``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: ``` composer require --dev sebastian/cli-parser ``` PKAZBcli-parser/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. ## [2.0.0] - 2023-02-03 ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4, and PHP 8.0 ## [1.0.1] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [1.0.0] - 2020-08-12 * Initial release [2.0.0]: https://github.com/sebastianbergmann/cli-parser/compare/1.0.1...2.0.0 [1.0.1]: https://github.com/sebastianbergmann/cli-parser/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/sebastianbergmann/cli-parser/compare/bb7bb3297957927962b0a3335befe7b66f7462e9...1.0.0 PKAZP@cli-parser/LICENSEnu[BSD 3-Clause License Copyright (c) 2020-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZMjtype/composer.jsonnu[{ "name": "sebastian/type", "description": "Collection of value objects that represent the types of the PHP type system", "type": "library", "homepage": "https://github.com/sebastianbergmann/type", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/type/issues" }, "prefer-stable": true, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture" ], "files": [ "tests/_fixture/callback_function.php", "tests/_fixture/functions_that_declare_return_types.php" ] }, "extra": { "branch-alias": { "dev-main": "4.0-dev" } } } PKAZE >PPtype/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZ@i..type/src/ReflectionMapper.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function assert; use ReflectionFunction; use ReflectionIntersectionType; use ReflectionMethod; use ReflectionNamedType; use ReflectionType; use ReflectionUnionType; final class ReflectionMapper { /** * @psalm-return list */ public function fromParameterTypes(ReflectionFunction|ReflectionMethod $functionOrMethod): array { $parameters = []; foreach ($functionOrMethod->getParameters() as $parameter) { $name = $parameter->getName(); assert($name !== ''); if (!$parameter->hasType()) { $parameters[] = new Parameter($name, new UnknownType); continue; } $type = $parameter->getType(); if ($type instanceof ReflectionNamedType) { $parameters[] = new Parameter( $name, $this->mapNamedType($type, $functionOrMethod) ); continue; } if ($type instanceof ReflectionUnionType) { $parameters[] = new Parameter( $name, $this->mapUnionType($type, $functionOrMethod) ); continue; } if ($type instanceof ReflectionIntersectionType) { $parameters[] = new Parameter( $name, $this->mapIntersectionType($type, $functionOrMethod) ); } } return $parameters; } public function fromReturnType(ReflectionFunction|ReflectionMethod $functionOrMethod): Type { if (!$this->hasReturnType($functionOrMethod)) { return new UnknownType; } $returnType = $this->returnType($functionOrMethod); assert($returnType instanceof ReflectionNamedType || $returnType instanceof ReflectionUnionType || $returnType instanceof ReflectionIntersectionType); if ($returnType instanceof ReflectionNamedType) { return $this->mapNamedType($returnType, $functionOrMethod); } if ($returnType instanceof ReflectionUnionType) { return $this->mapUnionType($returnType, $functionOrMethod); } if ($returnType instanceof ReflectionIntersectionType) { return $this->mapIntersectionType($returnType, $functionOrMethod); } } private function mapNamedType(ReflectionNamedType $type, ReflectionFunction|ReflectionMethod $functionOrMethod): Type { if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'self') { return ObjectType::fromName( $functionOrMethod->getDeclaringClass()->getName(), $type->allowsNull() ); } if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'static') { return new StaticType( TypeName::fromReflection($functionOrMethod->getDeclaringClass()), $type->allowsNull() ); } if ($type->getName() === 'mixed') { return new MixedType; } if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'parent') { return ObjectType::fromName( $functionOrMethod->getDeclaringClass()->getParentClass()->getName(), $type->allowsNull() ); } return Type::fromName( $type->getName(), $type->allowsNull() ); } private function mapUnionType(ReflectionUnionType $type, ReflectionFunction|ReflectionMethod $functionOrMethod): Type { $types = []; foreach ($type->getTypes() as $_type) { assert($_type instanceof ReflectionNamedType || $_type instanceof ReflectionIntersectionType); if ($_type instanceof ReflectionNamedType) { $types[] = $this->mapNamedType($_type, $functionOrMethod); continue; } $types[] = $this->mapIntersectionType($_type, $functionOrMethod); } return new UnionType(...$types); } private function mapIntersectionType(ReflectionIntersectionType $type, ReflectionFunction|ReflectionMethod $functionOrMethod): Type { $types = []; foreach ($type->getTypes() as $_type) { assert($_type instanceof ReflectionNamedType); $types[] = $this->mapNamedType($_type, $functionOrMethod); } return new IntersectionType(...$types); } private function hasReturnType(ReflectionFunction|ReflectionMethod $functionOrMethod): bool { if ($functionOrMethod->hasReturnType()) { return true; } return $functionOrMethod->hasTentativeReturnType(); } private function returnType(ReflectionFunction|ReflectionMethod $functionOrMethod): ?ReflectionType { if ($functionOrMethod->hasReturnType()) { return $functionOrMethod->getReturnType(); } return $functionOrMethod->getTentativeReturnType(); } } PKAZtype/src/type/VoidType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class VoidType extends Type { public function isAssignable(Type $other): bool { return $other instanceof self; } public function name(): string { return 'void'; } public function allowsNull(): bool { return false; } /** * @psalm-assert-if-true VoidType $this */ public function isVoid(): bool { return true; } } PKAZ@5#type/src/type/GenericObjectType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class GenericObjectType extends Type { private bool $allowsNull; public function __construct(bool $nullable) { $this->allowsNull = $nullable; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if (!$other instanceof ObjectType) { return false; } return true; } public function name(): string { return 'object'; } public function allowsNull(): bool { return $this->allowsNull; } /** * @psalm-assert-if-true GenericObjectType $this */ public function isGenericObject(): bool { return true; } } PKAZP^+type/src/type/Type.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function gettype; use function strtolower; abstract class Type { public static function fromValue(mixed $value, bool $allowsNull): self { if ($allowsNull === false) { if ($value === true) { return new TrueType; } if ($value === false) { return new FalseType; } } $typeName = gettype($value); if ($typeName === 'object') { return new ObjectType(TypeName::fromQualifiedName($value::class), $allowsNull); } $type = self::fromName($typeName, $allowsNull); if ($type instanceof SimpleType) { $type = new SimpleType($typeName, $allowsNull, $value); } return $type; } public static function fromName(string $typeName, bool $allowsNull): self { return match (strtolower($typeName)) { 'callable' => new CallableType($allowsNull), 'true' => new TrueType, 'false' => new FalseType, 'iterable' => new IterableType($allowsNull), 'never' => new NeverType, 'null' => new NullType, 'object' => new GenericObjectType($allowsNull), 'unknown type' => new UnknownType, 'void' => new VoidType, 'array', 'bool', 'boolean', 'double', 'float', 'int', 'integer', 'real', 'resource', 'resource (closed)', 'string' => new SimpleType($typeName, $allowsNull), 'mixed' => new MixedType, default => new ObjectType(TypeName::fromQualifiedName($typeName), $allowsNull), }; } public function asString(): string { return ($this->allowsNull() ? '?' : '') . $this->name(); } /** * @psalm-assert-if-true CallableType $this */ public function isCallable(): bool { return false; } /** * @psalm-assert-if-true TrueType $this */ public function isTrue(): bool { return false; } /** * @psalm-assert-if-true FalseType $this */ public function isFalse(): bool { return false; } /** * @psalm-assert-if-true GenericObjectType $this */ public function isGenericObject(): bool { return false; } /** * @psalm-assert-if-true IntersectionType $this */ public function isIntersection(): bool { return false; } /** * @psalm-assert-if-true IterableType $this */ public function isIterable(): bool { return false; } /** * @psalm-assert-if-true MixedType $this */ public function isMixed(): bool { return false; } /** * @psalm-assert-if-true NeverType $this */ public function isNever(): bool { return false; } /** * @psalm-assert-if-true NullType $this */ public function isNull(): bool { return false; } /** * @psalm-assert-if-true ObjectType $this */ public function isObject(): bool { return false; } /** * @psalm-assert-if-true SimpleType $this */ public function isSimple(): bool { return false; } /** * @psalm-assert-if-true StaticType $this */ public function isStatic(): bool { return false; } /** * @psalm-assert-if-true UnionType $this */ public function isUnion(): bool { return false; } /** * @psalm-assert-if-true UnknownType $this */ public function isUnknown(): bool { return false; } /** * @psalm-assert-if-true VoidType $this */ public function isVoid(): bool { return false; } abstract public function isAssignable(self $other): bool; abstract public function name(): string; abstract public function allowsNull(): bool; } PKAZ?  type/src/type/UnknownType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class UnknownType extends Type { public function isAssignable(Type $other): bool { return true; } public function name(): string { return 'unknown type'; } public function asString(): string { return ''; } public function allowsNull(): bool { return true; } /** * @psalm-assert-if-true UnknownType $this */ public function isUnknown(): bool { return true; } } PKAZo|type/src/type/NullType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class NullType extends Type { public function isAssignable(Type $other): bool { return !($other instanceof VoidType); } public function name(): string { return 'null'; } public function asString(): string { return 'null'; } public function allowsNull(): bool { return true; } /** * @psalm-assert-if-true NullType $this */ public function isNull(): bool { return true; } } PKAZ-{type/src/type/ObjectType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function is_subclass_of; use function strcasecmp; final class ObjectType extends Type { private TypeName $className; private bool $allowsNull; public function __construct(TypeName $className, bool $allowsNull) { $this->className = $className; $this->allowsNull = $allowsNull; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($other instanceof self) { if (0 === strcasecmp($this->className->qualifiedName(), $other->className->qualifiedName())) { return true; } if (is_subclass_of($other->className->qualifiedName(), $this->className->qualifiedName(), true)) { return true; } } return false; } public function name(): string { return $this->className->qualifiedName(); } public function allowsNull(): bool { return $this->allowsNull; } public function className(): TypeName { return $this->className; } /** * @psalm-assert-if-true ObjectType $this */ public function isObject(): bool { return true; } } PKAZi type/src/type/UnionType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function count; use function implode; use function sort; final class UnionType extends Type { /** * @psalm-var non-empty-list */ private array $types; /** * @throws RuntimeException */ public function __construct(Type ...$types) { $this->ensureMinimumOfTwoTypes(...$types); $this->ensureOnlyValidTypes(...$types); $this->types = $types; } public function isAssignable(Type $other): bool { foreach ($this->types as $type) { if ($type->isAssignable($other)) { return true; } } return false; } public function asString(): string { return $this->name(); } public function name(): string { $types = []; foreach ($this->types as $type) { if ($type->isIntersection()) { $types[] = '(' . $type->name() . ')'; continue; } $types[] = $type->name(); } sort($types); return implode('|', $types); } public function allowsNull(): bool { foreach ($this->types as $type) { if ($type instanceof NullType) { return true; } } return false; } /** * @psalm-assert-if-true UnionType $this */ public function isUnion(): bool { return true; } public function containsIntersectionTypes(): bool { foreach ($this->types as $type) { if ($type->isIntersection()) { return true; } } return false; } /** * @psalm-return non-empty-list */ public function types(): array { return $this->types; } /** * @throws RuntimeException */ private function ensureMinimumOfTwoTypes(Type ...$types): void { if (count($types) < 2) { throw new RuntimeException( 'A union type must be composed of at least two types' ); } } /** * @throws RuntimeException */ private function ensureOnlyValidTypes(Type ...$types): void { foreach ($types as $type) { if ($type instanceof UnknownType) { throw new RuntimeException( 'A union type must not be composed of an unknown type' ); } if ($type instanceof VoidType) { throw new RuntimeException( 'A union type must not be composed of a void type' ); } } } } PKAZ66type/src/type/SimpleType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function strtolower; final class SimpleType extends Type { private string $name; private bool $allowsNull; private mixed $value; public function __construct(string $name, bool $nullable, mixed $value = null) { $this->name = $this->normalize($name); $this->allowsNull = $nullable; $this->value = $value; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($this->name === 'bool' && $other->name() === 'true') { return true; } if ($this->name === 'bool' && $other->name() === 'false') { return true; } if ($other instanceof self) { return $this->name === $other->name; } return false; } public function name(): string { return $this->name; } public function allowsNull(): bool { return $this->allowsNull; } public function value(): mixed { return $this->value; } /** * @psalm-assert-if-true SimpleType $this */ public function isSimple(): bool { return true; } private function normalize(string $name): string { $name = strtolower($name); return match ($name) { 'boolean' => 'bool', 'real', 'double' => 'float', 'integer' => 'int', '[]' => 'array', default => $name, }; } } PKAZ/_type/src/type/CallableType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function assert; use function class_exists; use function count; use function explode; use function function_exists; use function is_array; use function is_object; use function is_string; use function str_contains; use Closure; use ReflectionClass; use ReflectionObject; final class CallableType extends Type { private bool $allowsNull; public function __construct(bool $nullable) { $this->allowsNull = $nullable; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($other instanceof self) { return true; } if ($other instanceof ObjectType) { if ($this->isClosure($other)) { return true; } if ($this->hasInvokeMethod($other)) { return true; } } if ($other instanceof SimpleType) { if ($this->isFunction($other)) { return true; } if ($this->isClassCallback($other)) { return true; } if ($this->isObjectCallback($other)) { return true; } } return false; } public function name(): string { return 'callable'; } public function allowsNull(): bool { return $this->allowsNull; } /** * @psalm-assert-if-true CallableType $this */ public function isCallable(): bool { return true; } private function isClosure(ObjectType $type): bool { return $type->className()->qualifiedName() === Closure::class; } private function hasInvokeMethod(ObjectType $type): bool { $className = $type->className()->qualifiedName(); assert(class_exists($className)); return (new ReflectionClass($className))->hasMethod('__invoke'); } private function isFunction(SimpleType $type): bool { if (!is_string($type->value())) { return false; } return function_exists($type->value()); } private function isObjectCallback(SimpleType $type): bool { if (!is_array($type->value())) { return false; } if (count($type->value()) !== 2) { return false; } if (!isset($type->value()[0], $type->value()[1])) { return false; } if (!is_object($type->value()[0]) || !is_string($type->value()[1])) { return false; } [$object, $methodName] = $type->value(); return (new ReflectionObject($object))->hasMethod($methodName); } private function isClassCallback(SimpleType $type): bool { if (!is_string($type->value()) && !is_array($type->value())) { return false; } if (is_string($type->value())) { if (!str_contains($type->value(), '::')) { return false; } [$className, $methodName] = explode('::', $type->value()); } if (is_array($type->value())) { if (count($type->value()) !== 2) { return false; } if (!isset($type->value()[0], $type->value()[1])) { return false; } if (!is_string($type->value()[0]) || !is_string($type->value()[1])) { return false; } [$className, $methodName] = $type->value(); } assert(isset($className) && is_string($className)); assert(isset($methodName) && is_string($methodName)); if (!class_exists($className)) { return false; } $class = new ReflectionClass($className); if (!$class->hasMethod($methodName)) { return false; } $method = $class->getMethod($methodName); return $method->isPublic() && $method->isStatic(); } } PKAZL/type/src/type/MixedType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class MixedType extends Type { public function isAssignable(Type $other): bool { return !$other instanceof VoidType; } public function asString(): string { return 'mixed'; } public function name(): string { return 'mixed'; } public function allowsNull(): bool { return true; } /** * @psalm-assert-if-true MixedType $this */ public function isMixed(): bool { return true; } } PKAZV!pptype/src/type/FalseType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class FalseType extends Type { public function isAssignable(Type $other): bool { if ($other instanceof self) { return true; } return $other instanceof SimpleType && $other->name() === 'bool' && $other->value() === false; } public function name(): string { return 'false'; } public function allowsNull(): bool { return false; } /** * @psalm-assert-if-true FalseType $this */ public function isFalse(): bool { return true; } } PKAZkktype/src/type/TrueType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class TrueType extends Type { public function isAssignable(Type $other): bool { if ($other instanceof self) { return true; } return $other instanceof SimpleType && $other->name() === 'bool' && $other->value() === true; } public function name(): string { return 'true'; } public function allowsNull(): bool { return false; } /** * @psalm-assert-if-true TrueType $this */ public function isTrue(): bool { return true; } } PKAZӆstype/src/type/StaticType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function is_subclass_of; use function strcasecmp; final class StaticType extends Type { private TypeName $className; private bool $allowsNull; public function __construct(TypeName $className, bool $allowsNull) { $this->className = $className; $this->allowsNull = $allowsNull; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if (!$other instanceof ObjectType) { return false; } if (0 === strcasecmp($this->className->qualifiedName(), $other->className()->qualifiedName())) { return true; } if (is_subclass_of($other->className()->qualifiedName(), $this->className->qualifiedName(), true)) { return true; } return false; } public function name(): string { return 'static'; } public function allowsNull(): bool { return $this->allowsNull; } /** * @psalm-assert-if-true StaticType $this */ public function isStatic(): bool { return true; } } PKAZ2Atype/src/type/IterableType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function assert; use function class_exists; use function is_iterable; use ReflectionClass; final class IterableType extends Type { private bool $allowsNull; public function __construct(bool $nullable) { $this->allowsNull = $nullable; } /** * @throws RuntimeException */ public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($other instanceof self) { return true; } if ($other instanceof SimpleType) { return is_iterable($other->value()); } if ($other instanceof ObjectType) { $className = $other->className()->qualifiedName(); assert(class_exists($className)); return (new ReflectionClass($className))->isIterable(); } return false; } public function name(): string { return 'iterable'; } public function allowsNull(): bool { return $this->allowsNull; } /** * @psalm-assert-if-true IterableType $this */ public function isIterable(): bool { return true; } } PKAZ type/src/type/NeverType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class NeverType extends Type { public function isAssignable(Type $other): bool { return $other instanceof self; } public function name(): string { return 'never'; } public function allowsNull(): bool { return false; } /** * @psalm-assert-if-true NeverType $this */ public function isNever(): bool { return true; } } PKAZ< "type/src/type/IntersectionType.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function assert; use function count; use function implode; use function in_array; use function sort; final class IntersectionType extends Type { /** * @psalm-var non-empty-list */ private array $types; /** * @throws RuntimeException */ public function __construct(Type ...$types) { $this->ensureMinimumOfTwoTypes(...$types); $this->ensureOnlyValidTypes(...$types); $this->ensureNoDuplicateTypes(...$types); $this->types = $types; } public function isAssignable(Type $other): bool { return $other->isObject(); } public function asString(): string { return $this->name(); } public function name(): string { $types = []; foreach ($this->types as $type) { $types[] = $type->name(); } sort($types); return implode('&', $types); } public function allowsNull(): bool { return false; } /** * @psalm-assert-if-true IntersectionType $this */ public function isIntersection(): bool { return true; } /** * @psalm-return non-empty-list */ public function types(): array { return $this->types; } /** * @throws RuntimeException */ private function ensureMinimumOfTwoTypes(Type ...$types): void { if (count($types) < 2) { throw new RuntimeException( 'An intersection type must be composed of at least two types' ); } } /** * @throws RuntimeException */ private function ensureOnlyValidTypes(Type ...$types): void { foreach ($types as $type) { if (!$type->isObject()) { throw new RuntimeException( 'An intersection type can only be composed of interfaces and classes' ); } } } /** * @throws RuntimeException */ private function ensureNoDuplicateTypes(Type ...$types): void { $names = []; foreach ($types as $type) { assert($type instanceof ObjectType); $classQualifiedName = $type->className()->qualifiedName(); if (in_array($classQualifiedName, $names, true)) { throw new RuntimeException('An intersection type must not contain duplicate types'); } $names[] = $classQualifiedName; } } } PKAZ56ww'type/src/exception/RuntimeException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class RuntimeException extends \RuntimeException implements Exception { } PKAZg@ɼaa type/src/exception/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use Throwable; interface Exception extends Throwable { } PKAZ type/src/Parameter.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; final class Parameter { /** * @psalm-var non-empty-string */ private string $name; private Type $type; /** * @psalm-param non-empty-string $name */ public function __construct(string $name, Type $type) { $this->name = $name; $this->type = $type; } public function name(): string { return $this->name; } public function type(): Type { return $this->type; } } PKAZX@@type/src/TypeName.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Type; use function array_pop; use function explode; use function implode; use function substr; use ReflectionClass; final class TypeName { private ?string $namespaceName; private string $simpleName; public static function fromQualifiedName(string $fullClassName): self { if ($fullClassName[0] === '\\') { $fullClassName = substr($fullClassName, 1); } $classNameParts = explode('\\', $fullClassName); $simpleName = array_pop($classNameParts); $namespaceName = implode('\\', $classNameParts); return new self($namespaceName, $simpleName); } public static function fromReflection(ReflectionClass $type): self { return new self( $type->getNamespaceName(), $type->getShortName() ); } public function __construct(?string $namespaceName, string $simpleName) { if ($namespaceName === '') { $namespaceName = null; } $this->namespaceName = $namespaceName; $this->simpleName = $simpleName; } public function namespaceName(): ?string { return $this->namespaceName; } public function simpleName(): string { return $this->simpleName; } public function qualifiedName(): string { return $this->namespaceName === null ? $this->simpleName : $this->namespaceName . '\\' . $this->simpleName; } public function isNamespaced(): bool { return $this->namespaceName !== null; } } PKAZM˳type/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/type/v/stable.png)](https://packagist.org/packages/sebastian/type) [![CI Status](https://github.com/sebastianbergmann/type/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/type/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/type/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/type) [![codecov](https://codecov.io/gh/sebastianbergmann/type/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/type) # sebastian/type Collection of value objects that represent the types of the PHP type system. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/type ``` 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/type ``` PKAZ type/infection.jsonnu[{ "source": { "directories": [ "src" ] }, "mutators": { "@default": true }, "minMsi": 100, "minCoveredMsi": 100 } PKAZytype/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [4.0.0] - 2023-02-03 ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0 ## [3.2.1] - 2023-02-03 ### Fixed * [#28](https://github.com/sebastianbergmann/type/pull/28): Potential undefined offset warning/notice ## [3.2.0] - 2022-09-12 ### Added * [#25](https://github.com/sebastianbergmann/type/issues/25): Support Disjunctive Normal Form types * Added `ReflectionMapper::fromParameterTypes()` * Added `IntersectionType::types()` and `UnionType::types()` * Added `UnionType::containsIntersectionTypes()` ## [3.1.0] - 2022-08-29 ### Added * [#21](https://github.com/sebastianbergmann/type/issues/21): Support `true` as stand-alone type ## [3.0.0] - 2022-03-15 ### Added * Support for intersection types introduced in PHP 8.1 * Support for the `never` return type introduced in PHP 8.1 * Added `Type::isCallable()`, `Type::isGenericObject()`, `Type::isIterable()`, `Type::isMixed()`, `Type::isNever()`, `Type::isNull()`, `Type::isObject()`, `Type::isSimple()`, `Type::isStatic()`, `Type::isUnion()`, `Type::isUnknown()`, and `Type::isVoid()` ### Changed * Renamed `ReflectionMapper::fromMethodReturnType(ReflectionMethod $method)` to `ReflectionMapper::fromReturnType(ReflectionFunctionAbstract $functionOrMethod)` ### Removed * Removed `Type::getReturnTypeDeclaration()` (use `Type::asString()` instead and prefix its result with `': '`) * Removed `TypeName::getNamespaceName()` (use `TypeName::namespaceName()` instead) * Removed `TypeName::getSimpleName()` (use `TypeName::simpleName()` instead) * Removed `TypeName::getQualifiedName()` (use `TypeName::qualifiedName()` instead) ## [2.3.4] - 2021-06-15 ### Fixed * Fixed regression introduced in 2.3.3 ## [2.3.3] - 2021-06-15 [YANKED] ### Fixed * [#15](https://github.com/sebastianbergmann/type/issues/15): "false" pseudo type is not handled properly ## [2.3.2] - 2021-06-04 ### Fixed * Fixed handling of tentatively declared return types ## [2.3.1] - 2020-10-26 ### Fixed * `SebastianBergmann\Type\Exception` now correctly extends `\Throwable` ## [2.3.0] - 2020-10-06 ### Added * [#14](https://github.com/sebastianbergmann/type/issues/14): Support for `static` return type that is introduced in PHP 8 ## [2.2.2] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [2.2.1] - 2020-07-05 ### Fixed * Fixed handling of `mixed` type in `ReflectionMapper::fromMethodReturnType()` ## [2.2.0] - 2020-07-05 ### Added * Added `MixedType` object for representing PHP 8's `mixed` type ## [2.1.1] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [2.1.0] - 2020-06-01 ### Added * Added `UnionType` object for representing PHP 8's Union Types * Added `ReflectionMapper::fromMethodReturnType()` for mapping `\ReflectionMethod::getReturnType()` to a `Type` object * Added `Type::name()` for retrieving the name of a type * Added `Type::asString()` for retrieving a textual representation of a type ### Changed * Deprecated `Type::getReturnTypeDeclaration()` (use `Type::asString()` instead and prefix its result with `': '`) * Deprecated `TypeName::getNamespaceName()` (use `TypeName::namespaceName()` instead) * Deprecated `TypeName::getSimpleName()` (use `TypeName::simpleName()` instead) * Deprecated `TypeName::getQualifiedName()` (use `TypeName::qualifiedName()` instead) ## [2.0.0] - 2020-02-07 ### Removed * This component is no longer supported on PHP 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) [4.0.0]: https://github.com/sebastianbergmann/type/compare/3.2.1...4.0.0 [3.2.1]: https://github.com/sebastianbergmann/type/compare/3.2.0...3.2.1 [3.2.0]: https://github.com/sebastianbergmann/type/compare/3.1.0...3.2.0 [3.1.0]: https://github.com/sebastianbergmann/type/compare/3.0.0...3.1.0 [3.0.0]: https://github.com/sebastianbergmann/type/compare/2.3.4...3.0.0 [2.3.4]: https://github.com/sebastianbergmann/type/compare/ca39369c41313ed12c071ed38ecda8fcdb248859...2.3.4 [2.3.3]: https://github.com/sebastianbergmann/type/compare/2.3.2...ca39369c41313ed12c071ed38ecda8fcdb248859 [2.3.2]: https://github.com/sebastianbergmann/type/compare/2.3.1...2.3.2 [2.3.1]: https://github.com/sebastianbergmann/type/compare/2.3.0...2.3.1 [2.3.0]: https://github.com/sebastianbergmann/type/compare/2.2.2...2.3.0 [2.2.2]: https://github.com/sebastianbergmann/type/compare/2.2.1...2.2.2 [2.2.1]: https://github.com/sebastianbergmann/type/compare/2.2.0...2.2.1 [2.2.0]: https://github.com/sebastianbergmann/type/compare/2.1.1...2.2.0 [2.1.1]: https://github.com/sebastianbergmann/type/compare/2.1.0...2.1.1 [2.1.0]: https://github.com/sebastianbergmann/type/compare/2.0.0...2.1.0 [2.0.0]: https://github.com/sebastianbergmann/type/compare/1.1.3...2.0.0 [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 PKAZ  type/LICENSEnu[BSD 3-Clause License Copyright (c) 2019-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZӾdiff/composer.jsonnu[{ "name": "sebastian/diff", "description": "Diff implementation", "keywords": ["diff", "udiff", "unidiff", "unified diff"], "homepage": "https://github.com/sebastianbergmann/diff", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" } ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy" }, "prefer-stable": true, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0", "symfony/process": "^4.2 || ^5" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/" ] }, "extra": { "branch-alias": { "dev-main": "5.0-dev" } } } PKAZKJuudiff/SECURITY.mdnu[# Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. PKAZ;Ldiff/src/Line.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Line { public const ADDED = 1; public const REMOVED = 2; public const UNCHANGED = 3; private int $type; private string $content; public function __construct(int $type = self::UNCHANGED, string $content = '') { $this->type = $type; $this->content = $content; } public function getContent(): string { return $this->content; } public function getType(): int { return $this->type; } } PKAZES$#KKdiff/src/Differ.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use const PHP_INT_SIZE; use const PREG_SPLIT_DELIM_CAPTURE; use const PREG_SPLIT_NO_EMPTY; use function array_shift; use function array_unshift; use function array_values; use function count; use function current; use function end; use function is_string; use function key; use function min; use function preg_split; use function prev; use function reset; use function str_ends_with; use function substr; use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; final class Differ { public const OLD = 0; public const ADDED = 1; public const REMOVED = 2; public const DIFF_LINE_END_WARNING = 3; public const NO_LINE_END_EOF_WARNING = 4; private DiffOutputBuilderInterface $outputBuilder; public function __construct(DiffOutputBuilderInterface $outputBuilder) { $this->outputBuilder = $outputBuilder; } public function diff(array|string $from, array|string $to, LongestCommonSubsequenceCalculator $lcs = null): string { $diff = $this->diffToArray($from, $to, $lcs); return $this->outputBuilder->getDiff($diff); } public function diffToArray(array|string $from, array|string $to, LongestCommonSubsequenceCalculator $lcs = null): array { if (is_string($from)) { $from = $this->splitStringByLines($from); } if (is_string($to)) { $to = $this->splitStringByLines($to); } [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); if ($lcs === null) { $lcs = $this->selectLcsImplementation($from, $to); } $common = $lcs->calculate(array_values($from), array_values($to)); $diff = []; foreach ($start as $token) { $diff[] = [$token, self::OLD]; } reset($from); reset($to); foreach ($common as $token) { while (($fromToken = reset($from)) !== $token) { $diff[] = [array_shift($from), self::REMOVED]; } while (($toToken = reset($to)) !== $token) { $diff[] = [array_shift($to), self::ADDED]; } $diff[] = [$token, self::OLD]; array_shift($from); array_shift($to); } while (($token = array_shift($from)) !== null) { $diff[] = [$token, self::REMOVED]; } while (($token = array_shift($to)) !== null) { $diff[] = [$token, self::ADDED]; } foreach ($end as $token) { $diff[] = [$token, self::OLD]; } if ($this->detectUnmatchedLineEndings($diff)) { array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); } return $diff; } private function splitStringByLines(string $input): array { return preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator { // We do not want to use the time-efficient implementation if its memory // footprint will probably exceed this value. Note that the footprint // calculation is only an estimation for the matrix and the LCS method // will typically allocate a bit more memory than this. $memoryLimit = 100 * 1024 * 1024; if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { return new MemoryEfficientLongestCommonSubsequenceCalculator; } return new TimeEfficientLongestCommonSubsequenceCalculator; } private function calculateEstimatedFootprint(array $from, array $to): float|int { $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; return $itemSize * min(count($from), count($to)) ** 2; } private function detectUnmatchedLineEndings(array $diff): bool { $newLineBreaks = ['' => true]; $oldLineBreaks = ['' => true]; foreach ($diff as $entry) { if (self::OLD === $entry[1]) { $ln = $this->getLinebreak($entry[0]); $oldLineBreaks[$ln] = true; $newLineBreaks[$ln] = true; } elseif (self::ADDED === $entry[1]) { $newLineBreaks[$this->getLinebreak($entry[0])] = true; } elseif (self::REMOVED === $entry[1]) { $oldLineBreaks[$this->getLinebreak($entry[0])] = true; } } // if either input or output is a single line without breaks than no warning should be raised if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { return false; } // two-way compare foreach ($newLineBreaks as $break => $set) { if (!isset($oldLineBreaks[$break])) { return true; } } foreach ($oldLineBreaks as $break => $set) { if (!isset($newLineBreaks[$break])) { return true; } } return false; } private function getLinebreak($line): string { if (!is_string($line)) { return ''; } $lc = substr($line, -1); if ("\r" === $lc) { return "\r"; } if ("\n" !== $lc) { return ''; } if (str_ends_with($line, "\r\n")) { return "\r\n"; } return "\n"; } private static function getArrayDiffParted(array &$from, array &$to): array { $start = []; $end = []; reset($to); foreach ($from as $k => $v) { $toK = key($to); if ($toK === $k && $v === $to[$k]) { $start[$k] = $v; unset($from[$k], $to[$k]); } else { break; } } end($from); end($to); do { $fromK = key($from); $toK = key($to); if (null === $fromK || null === $toK || current($from) !== current($to)) { break; } prev($from); prev($to); $end = [$fromK => $from[$fromK]] + $end; unset($from[$fromK], $to[$toK]); } while (true); return [$from, $to, $start, $end]; } } PKAZ>faa diff/src/Exception/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use Throwable; interface Exception extends Throwable { } PKAZ\2-diff/src/Exception/ConfigurationException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function gettype; use function is_object; use function sprintf; use Exception; final class ConfigurationException extends InvalidArgumentException { public function __construct( string $option, string $expected, $value, int $code = 0, Exception $previous = null ) { parent::__construct( sprintf( 'Option "%s" must be %s, got "%s".', $option, $expected, is_object($value) ? $value::class : (null === $value ? '' : gettype($value) . '#' . $value) ), $code, $previous ); } } PKAZL}f/diff/src/Exception/InvalidArgumentException.phpnu[ * * 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 { } PKAZN diff/src/Chunk.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Chunk { private int $start; private int $startRange; private int $end; private int $endRange; private array $lines; public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) { $this->start = $start; $this->startRange = $startRange; $this->end = $end; $this->endRange = $endRange; $this->lines = $lines; } public function getStart(): int { return $this->start; } public function getStartRange(): int { return $this->startRange; } public function getEnd(): int { return $this->end; } public function getEndRange(): int { return $this->endRange; } /** * @psalm-return list */ public function getLines(): array { return $this->lines; } /** * @psalm-param list $lines */ public function setLines(array $lines): void { foreach ($lines as $line) { if (!$line instanceof Line) { throw new InvalidArgumentException; } } $this->lines = $lines; } } PKAZ! >diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_fill; use function array_merge; use function array_reverse; use function array_slice; use function count; use function in_array; use function max; final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * @inheritDoc */ public function calculate(array $from, array $to): array { $cFrom = count($from); $cTo = count($to); if ($cFrom === 0) { return []; } if ($cFrom === 1) { if (in_array($from[0], $to, true)) { return [$from[0]]; } return []; } $i = (int) ($cFrom / 2); $fromStart = array_slice($from, 0, $i); $fromEnd = array_slice($from, $i); $llB = $this->length($fromStart, $to); $llE = $this->length(array_reverse($fromEnd), array_reverse($to)); $jMax = 0; $max = 0; for ($j = 0; $j <= $cTo; $j++) { $m = $llB[$j] + $llE[$cTo - $j]; if ($m >= $max) { $max = $m; $jMax = $j; } } $toStart = array_slice($to, 0, $jMax); $toEnd = array_slice($to, $jMax); return array_merge( $this->calculate($fromStart, $toStart), $this->calculate($fromEnd, $toEnd) ); } private function length(array $from, array $to): array { $current = array_fill(0, count($to) + 1, 0); $cFrom = count($from); $cTo = count($to); for ($i = 0; $i < $cFrom; $i++) { $prev = $current; for ($j = 0; $j < $cTo; $j++) { if ($from[$i] === $to[$j]) { $current[$j + 1] = $prev[$j] + 1; } else { // don't use max() to avoid function call overhead if ($current[$j] > $prev[$j + 1]) { $current[$j + 1] = $current[$j]; } else { $current[$j + 1] = $prev[$j + 1]; } } } } return $current; } } PKAZ>D/diff/src/LongestCommonSubsequenceCalculator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; interface LongestCommonSubsequenceCalculator { /** * Calculates the longest common subsequence of two arrays. */ public function calculate(array $from, array $to): array; } PKAZKU{@ @ <diff/src/TimeEfficientLongestCommonSubsequenceCalculator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_reverse; use function count; use function max; use SplFixedArray; final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator { /** * @inheritDoc */ public function calculate(array $from, array $to): array { $common = []; $fromLength = count($from); $toLength = count($to); $width = $fromLength + 1; $matrix = new SplFixedArray($width * ($toLength + 1)); for ($i = 0; $i <= $fromLength; $i++) { $matrix[$i] = 0; } for ($j = 0; $j <= $toLength; $j++) { $matrix[$j * $width] = 0; } for ($i = 1; $i <= $fromLength; $i++) { for ($j = 1; $j <= $toLength; $j++) { $o = ($j * $width) + $i; // don't use max() to avoid function call overhead $firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0; if ($matrix[$o - 1] > $matrix[$o - $width]) { if ($firstOrLast > $matrix[$o - 1]) { $matrix[$o] = $firstOrLast; } else { $matrix[$o] = $matrix[$o - 1]; } } else { if ($firstOrLast > $matrix[$o - $width]) { $matrix[$o] = $firstOrLast; } else { $matrix[$o] = $matrix[$o - $width]; } } } } $i = $fromLength; $j = $toLength; while ($i > 0 && $j > 0) { if ($from[$i - 1] === $to[$j - 1]) { $common[] = $from[$i - 1]; $i--; $j--; } else { $o = ($j * $width) + $i; if ($matrix[$o - $width] > $matrix[$o - 1]) { $j--; } else { $i--; } } } return array_reverse($common); } } PKAZw.diff/src/Output/AbstractChunkOutputBuilder.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function count; abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface { /** * Takes input of the diff array and returns the common parts. * Iterates through diff line by line. */ protected function getCommonChunks(array $diff, int $lineThreshold = 5): array { $diffSize = count($diff); $capturing = false; $chunkStart = 0; $chunkSize = 0; $commonChunks = []; for ($i = 0; $i < $diffSize; $i++) { if ($diff[$i][1] === 0 /* OLD */) { if ($capturing === false) { $capturing = true; $chunkStart = $i; $chunkSize = 0; } else { $chunkSize++; } } elseif ($capturing !== false) { if ($chunkSize >= $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } $capturing = false; } } if ($capturing !== false && $chunkSize >= $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } return $commonChunks; } } PKAZM'11)diff/src/Output/DiffOnlyOutputBuilder.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function fclose; use function fopen; use function fwrite; use function str_ends_with; use function stream_get_contents; use function substr; use SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in a loose unified diff format * listing only changes lines. Does not include line numbers. */ final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface { private string $header; public function __construct(string $header = "--- Original\n+++ New\n") { $this->header = $header; } public function getDiff(array $diff): string { $buffer = fopen('php://memory', 'r+b'); if ('' !== $this->header) { fwrite($buffer, $this->header); if (!str_ends_with($this->header, "\n")) { fwrite($buffer, "\n"); } } foreach ($diff as $diffEntry) { if ($diffEntry[1] === Differ::ADDED) { fwrite($buffer, '+' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::REMOVED) { fwrite($buffer, '-' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { fwrite($buffer, ' ' . $diffEntry[0]); continue; // Warnings should not be tested for line break, it will always be there } else { /* Not changed (old) 0 */ continue; // we didn't write the not-changed line, so do not add a line break either } $lc = substr($diffEntry[0], -1); if ($lc !== "\n" && $lc !== "\r") { fwrite($buffer, "\n"); // \No newline at end of file } } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); return $diff; } } PKAZfܢ**2diff/src/Output/StrictUnifiedDiffOutputBuilder.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function array_merge; use function array_splice; use function count; use function fclose; use function fopen; use function fwrite; use function is_bool; use function is_int; use function is_string; use function max; use function min; use function sprintf; use function stream_get_contents; use function substr; use SebastianBergmann\Diff\ConfigurationException; use SebastianBergmann\Diff\Differ; /** * Strict Unified diff output builder. * * Generates (strict) Unified diff's (unidiffs) with hunks. */ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface { private static array $default = [ 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 'fromFile' => null, 'fromFileDate' => null, 'toFile' => null, 'toFileDate' => null, ]; private bool $changed; private bool $collapseRanges; /** * @psalm-var positive-int */ private int $commonLineThreshold; private string $header; /** * @psalm-var positive-int */ private int $contextLines; public function __construct(array $options = []) { $options = array_merge(self::$default, $options); if (!is_bool($options['collapseRanges'])) { throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); } if (!is_int($options['contextLines']) || $options['contextLines'] < 0) { throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); } if (!is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); } $this->assertString($options, 'fromFile'); $this->assertString($options, 'toFile'); $this->assertStringOrNull($options, 'fromFileDate'); $this->assertStringOrNull($options, 'toFileDate'); $this->header = sprintf( "--- %s%s\n+++ %s%s\n", $options['fromFile'], null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], $options['toFile'], null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'] ); $this->collapseRanges = $options['collapseRanges']; $this->commonLineThreshold = $options['commonLineThreshold']; $this->contextLines = $options['contextLines']; } public function getDiff(array $diff): string { if (0 === count($diff)) { return ''; } $this->changed = false; $buffer = fopen('php://memory', 'r+b'); fwrite($buffer, $this->header); $this->writeDiffHunks($buffer, $diff); if (!$this->changed) { fclose($buffer); return ''; } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); // If the last char is not a linebreak: add it. // This might happen when both the `from` and `to` do not have a trailing linebreak $last = substr($diff, -1); return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } private function writeDiffHunks($output, array $diff): void { // detect "No newline at end of file" and insert into `$diff` if needed $upperLimit = count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { // search back for the last `+` and `-` line, // check if it has a trailing linebreak, else add a warning under it $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; $i--) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = substr($diff[$i][0], -1); if ("\n" !== $lc) { array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!count($toFind)) { break; } } } } // write hunks to output buffer $cutOff = max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; $i = 0; /** @var int $i */ foreach ($diff as $i => $entry) { if (0 === $entry[1]) { // same if (false === $hunkCapture) { $fromStart++; $toStart++; continue; } $sameCount++; $toRange++; $fromRange++; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines; // note: $contextEndOffset = $this->contextLines; // // because we never go beyond the end of the diff. // with the cutoff/contextlines here the follow is never true; // // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { // $contextEndOffset = count($diff) - 1; // } // // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } $this->changed = true; if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { // added $toRange++; } if (Differ::REMOVED === $entry[1]) { // removed $fromRange++; } } if (false === $hunkCapture) { return; } // we end here when cutoff (commonLineThreshold) was not reached, but we were capturing a hunk, // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; // prevent trying to write out more common lines than there are in the diff _and_ // do not write more than configured through the context lines $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { fwrite($output, ',' . $fromRange); } fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { fwrite($output, ',' . $toRange); } fwrite($output, " @@\n"); for ($i = $diffStartIndex; $i < $diffEndIndex; $i++) { if ($diff[$i][1] === Differ::ADDED) { $this->changed = true; fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { $this->changed = true; fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { $this->changed = true; fwrite($output, $diff[$i][0]); } //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package // skip //} else { // unknown/invalid //} } } private function assertString(array $options, string $option): void { if (!is_string($options[$option])) { throw new ConfigurationException($option, 'a string', $options[$option]); } } private function assertStringOrNull(array $options, string $option): void { if (null !== $options[$option] && !is_string($options[$option])) { throw new ConfigurationException($option, 'a string or ', $options[$option]); } } } PKAZ0  .diff/src/Output/DiffOutputBuilderInterface.phpnu[ * * 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; } PKAZ# ,diff/src/Output/UnifiedDiffOutputBuilder.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff\Output; use function array_splice; use function count; use function fclose; use function fopen; use function fwrite; use function max; use function min; use function str_ends_with; use function stream_get_contents; use function strlen; use function substr; use SebastianBergmann\Diff\Differ; /** * Builds a diff string representation in unified diff format in chunks. */ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder { private bool $collapseRanges = true; private int $commonLineThreshold = 6; /** * @psalm-var positive-int */ private int $contextLines = 3; private string $header; private bool $addLineNumbers; public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false) { $this->header = $header; $this->addLineNumbers = $addLineNumbers; } public function getDiff(array $diff): string { $buffer = fopen('php://memory', 'r+b'); if ('' !== $this->header) { fwrite($buffer, $this->header); if (!str_ends_with($this->header, "\n")) { fwrite($buffer, "\n"); } } if (0 !== count($diff)) { $this->writeDiffHunks($buffer, $diff); } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); // If the diff is non-empty and last char is not a linebreak: add it. // This might happen when both the `from` and `to` do not have a trailing linebreak $last = substr($diff, -1); return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } private function writeDiffHunks($output, array $diff): void { // detect "No newline at end of file" and insert into `$diff` if needed $upperLimit = count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { // search back for the last `+` and `-` line, // check if it has trailing linebreak, else add a warning under it $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; $i--) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = substr($diff[$i][0], -1); if ("\n" !== $lc) { array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!count($toFind)) { break; } } } } // write hunks to output buffer $cutOff = max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; $i = 0; /** @var int $i */ foreach ($diff as $i => $entry) { if (0 === $entry[1]) { // same if (false === $hunkCapture) { $fromStart++; $toStart++; continue; } $sameCount++; $toRange++; $fromRange++; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines; // note: $contextEndOffset = $this->contextLines; // // because we never go beyond the end of the diff. // with the cutoff/contextlines here the follow is never true; // // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { // $contextEndOffset = count($diff) - 1; // } // // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { $toRange++; } if (Differ::REMOVED === $entry[1]) { $fromRange++; } } if (false === $hunkCapture) { return; } // we end here when cutoff (commonLineThreshold) was not reached, but we were capturing a hunk, // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; // prevent trying to write out more common lines than there are in the diff _and_ // do not write more than configured through the context lines $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { if ($this->addLineNumbers) { fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { fwrite($output, ',' . $fromRange); } fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { fwrite($output, ',' . $toRange); } fwrite($output, " @@\n"); } else { fwrite($output, "@@ @@\n"); } for ($i = $diffStartIndex; $i < $diffEndIndex; $i++) { if ($diff[$i][1] === Differ::ADDED) { fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { fwrite($output, "\n"); // $diff[$i][0] } else { /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ fwrite($output, ' ' . $diff[$i][0]); } } } } PKAZuP^^diff/src/Diff.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; final class Diff { private string $from; private string $to; /** * @psalm-var list */ private array $chunks; /** * @psalm-param list $chunks */ public function __construct(string $from, string $to, array $chunks = []) { $this->from = $from; $this->to = $to; $this->chunks = $chunks; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } /** * @psalm-return list */ public function getChunks(): array { return $this->chunks; } /** * @psalm-param list $chunks */ public function setChunks(array $chunks): void { $this->chunks = $chunks; } } PKAZ-Jq diff/src/Parser.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\Diff; use function array_pop; use function count; use function max; use function preg_match; use function preg_split; /** * Unified diff parser. */ final class Parser { /** * @return Diff[] */ public function parse(string $string): array { $lines = preg_split('(\r\n|\r|\n)', $string); if (!empty($lines) && $lines[count($lines) - 1] === '') { array_pop($lines); } $lineCount = count($lines); $diffs = []; $diff = null; $collected = []; for ($i = 0; $i < $lineCount; $i++) { if (preg_match('#^---\h+"?(?P[^\\v\\t"]+)#', $lines[$i], $fromMatch) && preg_match('#^\\+\\+\\+\\h+"?(?P[^\\v\\t"]+)#', $lines[$i + 1], $toMatch)) { if ($diff !== null) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; $collected = []; } $diff = new Diff($fromMatch['file'], $toMatch['file']); $i++; } else { if (preg_match('/^(?:diff --git |index [\da-f.]+|[+-]{3} [ab])/', $lines[$i])) { continue; } $collected[] = $lines[$i]; } } if ($diff !== null && count($collected)) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; } return $diffs; } private function parseFileDiff(Diff $diff, array $lines): void { $chunks = []; $chunk = null; $diffLines = []; foreach ($lines as $line) { if (preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match, PREG_UNMATCHED_AS_NULL)) { $chunk = new Chunk( (int) $match['start'], isset($match['startrange']) ? max(0, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? max(0, (int) $match['endrange']) : 1 ); $chunks[] = $chunk; $diffLines = []; continue; } if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { $type = Line::UNCHANGED; if ($match['type'] === '+') { $type = Line::ADDED; } elseif ($match['type'] === '-') { $type = Line::REMOVED; } $diffLines[] = new Line($type, $match['line']); $chunk?->setLines($diffLines); } } $diff->setChunks($chunks); } } PKAZZx x diff/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/diff/v/stable.png)](https://packagist.org/packages/sebastian/diff) [![CI Status](https://github.com/sebastianbergmann/diff/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/diff/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/diff/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/diff) [![codecov](https://codecov.io/gh/sebastianbergmann/diff/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/diff) # sebastian/diff Diff implementation for PHP, factored out of PHPUnit into a stand-alone component. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/diff ``` If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency: ``` composer require --dev sebastian/diff ``` ### Usage #### Generating diff The `Differ` class can be used to generate a textual representation of the difference between two strings: ```php 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 diff('foo', 'bar'); ``` #### StrictUnifiedDiffOutputBuilder Generates (strict) Unified diff's (unidiffs) with hunks, similar to `diff -u` and compatible with `patch` and `git apply`. ```php true, // ranges of length one are rendered with the trailing `,1` 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 'fromFile' => '', 'fromFileDate' => null, 'toFile' => '', 'toFileDate' => null, ]); $differ = new Differ($builder); print $differ->diff('foo', 'bar'); ``` #### DiffOnlyOutputBuilder Output only the lines that differ. ```php diff('foo', 'bar'); ``` #### DiffOutputBuilderInterface You can pass any output builder to the `Differ` class as longs as it implements the `DiffOutputBuilderInterface`. #### Parsing diff The `Parser` class can be used to parse a unified diff into an object graph: ```php use SebastianBergmann\Diff\Parser; use SebastianBergmann\Git; $git = new Git('/usr/local/src/money'); $diff = $git->getDiff( '948a1a07768d8edd10dcefa8315c1cbeffb31833', 'c07a373d2399f3e686234c4f7f088d635eb9641b' ); $parser = new Parser; print_r($parser->parse($diff)); ``` The code above yields the output below: Array ( [0] => SebastianBergmann\Diff\Diff Object ( [from:SebastianBergmann\Diff\Diff:private] => a/tests/MoneyTest.php [to:SebastianBergmann\Diff\Diff:private] => b/tests/MoneyTest.php [chunks:SebastianBergmann\Diff\Diff:private] => Array ( [0] => SebastianBergmann\Diff\Chunk Object ( [start:SebastianBergmann\Diff\Chunk:private] => 87 [startRange:SebastianBergmann\Diff\Chunk:private] => 7 [end:SebastianBergmann\Diff\Chunk:private] => 87 [endRange:SebastianBergmann\Diff\Chunk:private] => 7 [lines:SebastianBergmann\Diff\Chunk:private] => Array ( [0] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::add ) [1] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => * @covers SebastianBergmann\Money\Money::newMoney ) [2] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => */ ) [3] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 2 [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyWithSameCurrencyObjectCanBeAdded() ) [4] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 1 [content:SebastianBergmann\Diff\Line:private] => public function testAnotherMoneyObjectWithSameCurrencyCanBeAdded() ) [5] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => { ) [6] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => $a = new Money(1, new Currency('EUR')); ) [7] => SebastianBergmann\Diff\Line Object ( [type:SebastianBergmann\Diff\Line:private] => 3 [content:SebastianBergmann\Diff\Line:private] => $b = new Money(2, new Currency('EUR')); ) ) ) ) ) ) Note: If the chunk size is 0 lines, i.e., `getStartRange()` or `getEndRange()` return 0, the number of line returned by `getStart()` or `getEnd()` is one lower than one would expect. It is the line number after which the chunk should be inserted or deleted; in all other cases, it gives the first line number of the replaced range of lines. PKAZj8({ diff/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [5.0.3] - 2023-05-01 ### Changed * [#119](https://github.com/sebastianbergmann/diff/pull/119): Improve performance of `TimeEfficientLongestCommonSubsequenceCalculator` ## [5.0.2] - 2023-05-01 ### Changed * [#118](https://github.com/sebastianbergmann/diff/pull/118): Improve performance of `MemoryEfficientLongestCommonSubsequenceCalculator` ## [5.0.1] - 2023-03-23 ### Fixed * [#115](https://github.com/sebastianbergmann/diff/pull/115): `Parser::parseFileDiff()` does not handle diffs correctly that only add lines or only remove lines ## [5.0.0] - 2023-02-03 ### Changed * Passing a `DiffOutputBuilderInterface` instance to `Differ::__construct()` is no longer optional ### Removed * Removed support for PHP 7.3, PHP 7.4, and PHP 8.0 ## [4.0.4] - 2020-10-26 ### Fixed * `SebastianBergmann\Diff\Exception` now correctly extends `\Throwable` ## [4.0.3] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [4.0.2] - 2020-06-30 ### Added * This component is now supported on PHP 8 ## [4.0.1] - 2020-05-08 ### Fixed * [#99](https://github.com/sebastianbergmann/diff/pull/99): Regression in unified diff output of identical strings ## [4.0.0] - 2020-02-07 ### Removed * Removed support for PHP 7.1 and PHP 7.2 ## [3.0.2] - 2019-02-04 ### Changed * `Chunk::setLines()` now ensures that the `$lines` array only contains `Line` objects ## [3.0.1] - 2018-06-10 ### Fixed * Removed `"minimum-stability": "dev",` from `composer.json` ## [3.0.0] - 2018-02-01 * The `StrictUnifiedDiffOutputBuilder` implementation of the `DiffOutputBuilderInterface` was added ### Changed * The default `DiffOutputBuilderInterface` implementation now generates context lines (unchanged lines) ### Removed * Removed support for PHP 7.0 ### Fixed * [#70](https://github.com/sebastianbergmann/diff/issues/70): Diffing of arrays no longer works ## [2.0.1] - 2017-08-03 ### Fixed * [#66](https://github.com/sebastianbergmann/diff/pull/66): Restored backwards compatibility for PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 ## [2.0.0] - 2017-07-11 [YANKED] ### Added * [#64](https://github.com/sebastianbergmann/diff/pull/64): Show line numbers for chunks of a diff ### Removed * This component is no longer supported on PHP 5.6 [5.0.3]: https://github.com/sebastianbergmann/diff/compare/5.0.2...5.0.3 [5.0.2]: https://github.com/sebastianbergmann/diff/compare/5.0.1...5.0.2 [5.0.1]: https://github.com/sebastianbergmann/diff/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/sebastianbergmann/diff/compare/4.0.4...5.0.0 [4.0.4]: https://github.com/sebastianbergmann/diff/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/diff/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/diff/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/diff/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/diff/compare/3.0.2...4.0.0 [3.0.2]: https://github.com/sebastianbergmann/diff/compare/3.0.1...3.0.2 [3.0.1]: https://github.com/sebastianbergmann/diff/compare/3.0.0...3.0.1 [3.0.0]: https://github.com/sebastianbergmann/diff/compare/2.0...3.0.0 [2.0.1]: https://github.com/sebastianbergmann/diff/compare/c341c98ce083db77f896a0aa64f5ee7652915970...2.0.1 [2.0.0]: https://github.com/sebastianbergmann/diff/compare/1.4...c341c98ce083db77f896a0aa64f5ee7652915970 PKAZT diff/LICENSEnu[BSD 3-Clause License Copyright (c) 2002-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZT>>lines-of-code/composer.jsonnu[{ "name": "sebastian/lines-of-code", "description": "Library for counting the lines of code in PHP source code", "type": "library", "homepage": "https://github.com/sebastianbergmann/lines-of-code", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy" }, "prefer-stable": true, "require": { "php": ">=8.1", "nikic/php-parser": "^4.10" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "config": { "platform": { "php": "8.1" }, "optimize-autoloader": true, "sort-packages": true }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-main": "2.0-dev" } } } PKAZKJuulines-of-code/SECURITY.mdnu[# Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. PKAZ>« lines-of-code/src/Counter.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\LinesOfCode; use function assert; use function file_get_contents; use function substr_count; use PhpParser\Error; use PhpParser\Lexer; use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\Parser; use PhpParser\ParserFactory; final class Counter { /** * @throws RuntimeException */ public function countInSourceFile(string $sourceFile): LinesOfCode { return $this->countInSourceString(file_get_contents($sourceFile)); } /** * @throws RuntimeException */ public function countInSourceString(string $source): LinesOfCode { $linesOfCode = substr_count($source, "\n"); if ($linesOfCode === 0 && !empty($source)) { $linesOfCode = 1; } assert($linesOfCode >= 0); try { $nodes = $this->parser()->parse($source); assert($nodes !== null); return $this->countInAbstractSyntaxTree($linesOfCode, $nodes); // @codeCoverageIgnoreStart } catch (Error $error) { throw new RuntimeException( $error->getMessage(), $error->getCode(), $error, ); } // @codeCoverageIgnoreEnd } /** * @psalm-param non-negative-int $linesOfCode * * @param Node[] $nodes * * @throws RuntimeException */ public function countInAbstractSyntaxTree(int $linesOfCode, array $nodes): LinesOfCode { $traverser = new NodeTraverser; $visitor = new LineCountingVisitor($linesOfCode); $traverser->addVisitor($visitor); try { /* @noinspection UnusedFunctionResultInspection */ $traverser->traverse($nodes); // @codeCoverageIgnoreStart } catch (Error $error) { throw new RuntimeException( $error->getMessage(), $error->getCode(), $error, ); } // @codeCoverageIgnoreEnd return $visitor->result(); } private function parser(): Parser { return (new ParserFactory)->create(ParserFactory::PREFER_PHP7, new Lexer); } } PKAZkJM  )lines-of-code/src/LineCountingVisitor.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\LinesOfCode; use function array_merge; use function array_unique; use function assert; use function count; use PhpParser\Comment; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\NodeVisitorAbstract; final class LineCountingVisitor extends NodeVisitorAbstract { /** * @psalm-var non-negative-int */ private readonly int $linesOfCode; /** * @var Comment[] */ private array $comments = []; /** * @var int[] */ private array $linesWithStatements = []; /** * @psalm-param non-negative-int $linesOfCode */ public function __construct(int $linesOfCode) { $this->linesOfCode = $linesOfCode; } public function enterNode(Node $node): void { $this->comments = array_merge($this->comments, $node->getComments()); if (!$node instanceof Expr) { return; } $this->linesWithStatements[] = $node->getStartLine(); } public function result(): LinesOfCode { $commentLinesOfCode = 0; foreach ($this->comments() as $comment) { $commentLinesOfCode += ($comment->getEndLine() - $comment->getStartLine() + 1); } $nonCommentLinesOfCode = $this->linesOfCode - $commentLinesOfCode; $logicalLinesOfCode = count(array_unique($this->linesWithStatements)); assert($commentLinesOfCode >= 0); assert($nonCommentLinesOfCode >= 0); assert($logicalLinesOfCode >= 0); return new LinesOfCode( $this->linesOfCode, $commentLinesOfCode, $nonCommentLinesOfCode, $logicalLinesOfCode, ); } /** * @return Comment[] */ private function comments(): array { $comments = []; foreach ($this->comments as $comment) { $comments[$comment->getStartLine() . '_' . $comment->getStartTokenPos() . '_' . $comment->getEndLine() . '_' . $comment->getEndTokenPos()] = $comment; } return $comments; } } PKAZ/0lines-of-code/src/Exception/RuntimeException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\LinesOfCode; final class RuntimeException extends \RuntimeException implements Exception { } PKAZ0+qq)lines-of-code/src/Exception/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\LinesOfCode; use Throwable; interface Exception extends Throwable { } PKAZy<6lines-of-code/src/Exception/NegativeValueException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\LinesOfCode; use InvalidArgumentException; final class NegativeValueException extends InvalidArgumentException implements Exception { } PKAZ 8lines-of-code/src/Exception/IllogicalValuesException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\LinesOfCode; use LogicException; final class IllogicalValuesException extends LogicException implements Exception { } PKAZs^ ^ !lines-of-code/src/LinesOfCode.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\LinesOfCode; /** * @psalm-immutable */ final class LinesOfCode { /** * @psalm-var non-negative-int */ private readonly int $linesOfCode; /** * @psalm-var non-negative-int */ private readonly int $commentLinesOfCode; /** * @psalm-var non-negative-int */ private readonly int $nonCommentLinesOfCode; /** * @psalm-var non-negative-int */ private readonly int $logicalLinesOfCode; /** * @psalm-param non-negative-int $linesOfCode * @psalm-param non-negative-int $commentLinesOfCode * @psalm-param non-negative-int $nonCommentLinesOfCode * @psalm-param non-negative-int $logicalLinesOfCode * * @throws IllogicalValuesException * @throws NegativeValueException */ public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode, int $logicalLinesOfCode) { /** @psalm-suppress DocblockTypeContradiction */ if ($linesOfCode < 0) { throw new NegativeValueException('$linesOfCode must not be negative'); } /** @psalm-suppress DocblockTypeContradiction */ if ($commentLinesOfCode < 0) { throw new NegativeValueException('$commentLinesOfCode must not be negative'); } /** @psalm-suppress DocblockTypeContradiction */ if ($nonCommentLinesOfCode < 0) { throw new NegativeValueException('$nonCommentLinesOfCode must not be negative'); } /** @psalm-suppress DocblockTypeContradiction */ if ($logicalLinesOfCode < 0) { throw new NegativeValueException('$logicalLinesOfCode must not be negative'); } if ($linesOfCode - $commentLinesOfCode !== $nonCommentLinesOfCode) { throw new IllogicalValuesException('$linesOfCode !== $commentLinesOfCode + $nonCommentLinesOfCode'); } $this->linesOfCode = $linesOfCode; $this->commentLinesOfCode = $commentLinesOfCode; $this->nonCommentLinesOfCode = $nonCommentLinesOfCode; $this->logicalLinesOfCode = $logicalLinesOfCode; } /** * @psalm-return non-negative-int */ public function linesOfCode(): int { return $this->linesOfCode; } /** * @psalm-return non-negative-int */ public function commentLinesOfCode(): int { return $this->commentLinesOfCode; } /** * @psalm-return non-negative-int */ public function nonCommentLinesOfCode(): int { return $this->nonCommentLinesOfCode; } /** * @psalm-return non-negative-int */ public function logicalLinesOfCode(): int { return $this->logicalLinesOfCode; } public function plus(self $other): self { return new self( $this->linesOfCode() + $other->linesOfCode(), $this->commentLinesOfCode() + $other->commentLinesOfCode(), $this->nonCommentLinesOfCode() + $other->nonCommentLinesOfCode(), $this->logicalLinesOfCode() + $other->logicalLinesOfCode(), ); } } PKAZ==lines-of-code/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/lines-of-code/v/stable.png)](https://packagist.org/packages/sebastian/lines-of-code) [![CI Status](https://github.com/sebastianbergmann/lines-of-code/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/lines-of-code/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/lines-of-code/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/lines-of-code) [![codecov](https://codecov.io/gh/sebastianbergmann/lines-of-code/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/lines-of-code) # sebastian/lines-of-code Library for counting the lines of code in PHP source code. ## Installation You can add this library as a local, per-project dependency to your project using [Composer](https://getcomposer.org/): ``` composer require sebastian/lines-of-code ``` 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/lines-of-code ``` PKAZk??lines-of-code/ChangeLog.mdnu[# ChangeLog All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. ## [2.0.1] - 2023-08-31 ### Changed * Improved type information ## [2.0.0] - 2023-02-03 ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0 ## [1.0.3] - 2020-11-28 ### Fixed * Files that do not contain a newline were not handled correctly ### Changed * A line of code is no longer considered to be a Logical Line of Code if it does not contain an `Expr` node ## [1.0.2] - 2020-10-26 ### Fixed * `SebastianBergmann\LinesOfCode\Exception` now correctly extends `\Throwable` ## [1.0.1] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [1.0.0] - 2020-07-22 * Initial release [2.0.1]: https://github.com/sebastianbergmann/lines-of-code/compare/2.0.0...2.0.1 [2.0.0]: https://github.com/sebastianbergmann/lines-of-code/compare/1.0.3...2.0.0 [1.0.3]: https://github.com/sebastianbergmann/lines-of-code/compare/1.0.2...1.0.3 [1.0.2]: https://github.com/sebastianbergmann/lines-of-code/compare/1.0.1...1.0.2 [1.0.1]: https://github.com/sebastianbergmann/lines-of-code/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/sebastianbergmann/lines-of-code/compare/f959e71f00e591288acc024afe9cb966c6cf9bd6...1.0.0 PKAZP@lines-of-code/LICENSEnu[BSD 3-Clause License Copyright (c) 2020-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZN  global-state/composer.jsonnu[{ "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" } ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy" }, "prefer-stable": true, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "require": { "php": ">=8.1", "sebastian/object-reflector": "^3.0", "sebastian/recursion-context": "^5.0" }, "require-dev": { "ext-dom": "*", "phpunit/phpunit": "^10.0" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture/" ], "files": [ "tests/_fixture/SnapshotFunctions.php" ] }, "extra": { "branch-alias": { "dev-main": "6.0-dev" } } } PKAZKJuuglobal-state/SECURITY.mdnu[# Security Policy If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: * The type of issue * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. ## Web Context The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. PKAZ}'f(f(global-state/src/Snapshot.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use function array_keys; use function array_merge; use function array_reverse; use function assert; use function func_get_args; use function get_declared_classes; use function get_declared_interfaces; use function get_declared_traits; use function get_defined_constants; use function get_defined_functions; use function get_included_files; use function in_array; use function ini_get_all; use function is_array; use function is_object; use function is_resource; use function is_scalar; use function serialize; use function unserialize; use ReflectionClass; use SebastianBergmann\ObjectReflector\ObjectReflector; use SebastianBergmann\RecursionContext\Context; use Throwable; /** * A snapshot of global state. */ class Snapshot { private ExcludeList $excludeList; private array $globalVariables = []; private array $superGlobalArrays = []; private array $superGlobalVariables = []; private array $staticProperties = []; private array $iniSettings = []; private array $includedFiles = []; private array $constants = []; private array $functions = []; private array $interfaces = []; private array $classes = []; private array $traits = []; public function __construct(ExcludeList $excludeList = null, bool $includeGlobalVariables = true, bool $includeStaticProperties = true, bool $includeConstants = true, bool $includeFunctions = true, bool $includeClasses = true, bool $includeInterfaces = true, bool $includeTraits = true, bool $includeIniSettings = true, bool $includeIncludedFiles = true) { $this->excludeList = $excludeList ?: new ExcludeList; if ($includeConstants) { $this->snapshotConstants(); } if ($includeFunctions) { $this->snapshotFunctions(); } if ($includeClasses || $includeStaticProperties) { $this->snapshotClasses(); } if ($includeInterfaces) { $this->snapshotInterfaces(); } if ($includeGlobalVariables) { $this->setupSuperGlobalArrays(); $this->snapshotGlobals(); } if ($includeStaticProperties) { $this->snapshotStaticProperties(); } 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 excludeList(): ExcludeList { return $this->excludeList; } public function globalVariables(): array { return $this->globalVariables; } public function superGlobalVariables(): array { return $this->superGlobalVariables; } public function superGlobalArrays(): array { return $this->superGlobalArrays; } public function staticProperties(): array { return $this->staticProperties; } 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; } private function snapshotConstants(): void { $constants = get_defined_constants(true); if (isset($constants['user'])) { $this->constants = $constants['user']; } } private function snapshotFunctions(): void { $functions = get_defined_functions(); $this->functions = $functions['user']; } 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); } 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); } 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, true) && $this->canBeSerialized($GLOBALS[$key]) && !$this->excludeList->isGlobalVariableExcluded($key)) { /* @noinspection UnserializeExploitsInspection */ $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); } } } 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)); } } } private function snapshotStaticProperties(): void { foreach ($this->classes as $className) { $class = new ReflectionClass($className); $snapshot = []; foreach ($class->getProperties() as $property) { if ($property->isStatic()) { $name = $property->getName(); if ($this->excludeList->isStaticPropertyExcluded($className, $name)) { continue; } if (!$property->isInitialized()) { continue; } $value = $property->getValue(); if ($this->canBeSerialized($value)) { /* @noinspection UnserializeExploitsInspection */ $snapshot[$name] = unserialize(serialize($value)); } } } if (!empty($snapshot)) { $this->staticProperties[$className] = $snapshot; } } } private function setupSuperGlobalArrays(): void { $this->superGlobalArrays = [ '_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', ]; } private function canBeSerialized(mixed $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(mixed $variable): array { if (isset(func_get_args()[1])) { $processed = func_get_args()[1]; } else { $processed = new Context; } assert($processed instanceof Context); $result = []; if ($processed->contains($variable)) { return $result; } $array = $variable; /* @noinspection UnusedFunctionResultInspection */ $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)->getProperties($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; } } PKAZG global-state/src/ExcludeList.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use function in_array; use function strpos; use ReflectionClass; final class ExcludeList { private array $globalVariables = []; private array $classes = []; private array $classNamePrefixes = []; private array $parentClasses = []; private array $interfaces = []; private array $staticProperties = []; 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 addStaticProperty(string $className, string $propertyName): void { if (!isset($this->staticProperties[$className])) { $this->staticProperties[$className] = []; } $this->staticProperties[$className][$propertyName] = true; } public function isGlobalVariableExcluded(string $variableName): bool { return isset($this->globalVariables[$variableName]); } /** * @psalm-param class-string $className */ public function isStaticPropertyExcluded(string $className, string $propertyName): bool { if (in_array($className, $this->classes, true)) { 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; } } return isset($this->staticProperties[$className][$propertyName]); } } PKAZKP; ; !global-state/src/CodeExporter.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use const PHP_EOL; use function is_array; use function is_scalar; use function serialize; use function sprintf; use function var_export; 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 = <<<'EOT' call_user_func( function () { foreach (array_keys($GLOBALS) as $key) { unset($GLOBALS[$key]); } } ); EOT; 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(mixed $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; } } PKAZjE global-state/src/Restorer.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use function array_diff; use function array_key_exists; use function array_keys; use function array_merge; use function in_array; use function is_array; use ReflectionClass; use ReflectionProperty; final class Restorer { 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, true) && !$snapshot->excludeList()->isGlobalVariableExcluded($key)) { if (array_key_exists($key, $globalVariables)) { $GLOBALS[$key] = $globalVariables[$key]; } else { unset($GLOBALS[$key]); } } } } public function restoreStaticProperties(Snapshot $snapshot): void { $current = new Snapshot($snapshot->excludeList(), false, false, false, false, true, false, false, false, false); $newClasses = array_diff($current->classes(), $snapshot->classes()); unset($current); foreach ($snapshot->staticProperties() as $className => $staticProperties) { foreach ($staticProperties as $name => $value) { $reflector = new ReflectionProperty($className, $name); $reflector->setValue(null, $value); } } foreach ($newClasses as $className) { $class = new ReflectionClass($className); $defaults = $class->getDefaultProperties(); foreach ($class->getProperties() as $property) { if (!$property->isStatic()) { continue; } $name = $property->getName(); if ($snapshot->excludeList()->isStaticPropertyExcluded($className, $name)) { continue; } if (!isset($defaults[$name])) { continue; } $property->setValue(null, $defaults[$name]); } } } private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray): void { $superGlobalVariables = $snapshot->superGlobalVariables(); if (isset($GLOBALS[$superGlobalArray], $superGlobalVariables[$superGlobalArray]) && is_array($GLOBALS[$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]); } } } } } PKAZpƆ0global-state/src/exceptions/RuntimeException.phpnu[ * * 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 { } PKAZ pp)global-state/src/exceptions/Exception.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\GlobalState; use Throwable; interface Exception extends Throwable { } PKAZMKKglobal-state/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/global-state/v/stable.png)](https://packagist.org/packages/sebastian/global-state) [![CI Status](https://github.com/sebastianbergmann/global-state/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/global-state/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/global-state/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/global-state) [![codecov](https://codecov.io/gh/sebastianbergmann/global-state/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/global-state) # sebastian/global-state Snapshotting of global state, 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/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 ``` PKAZN global-state/ChangeLog.mdnu[# 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. ## [6.0.1] - 2023-07-19 ### Changed * Changed usage of `ReflectionProperty::setValue()` to be compatible with PHP 8.3 ## [6.0.0] - 2023-02-03 ### Changed * Renamed `SebastianBergmann\GlobalState\ExcludeList::addStaticAttribute()` to `SebastianBergmann\GlobalState\ExcludeList::addStaticProperty()` * Renamed `SebastianBergmann\GlobalState\ExcludeList::isStaticAttributeExcluded()` to `SebastianBergmann\GlobalState\ExcludeList::isStaticPropertyExcluded()` * Renamed `SebastianBergmann\GlobalState\Restorer::restoreStaticAttributes()` to `SebastianBergmann\GlobalState\Restorer::restoreStaticProperties()` * Renamed `SebastianBergmann\GlobalState\Snapshot::staticAttributes()` to `SebastianBergmann\GlobalState\Snapshot::staticProperties()` ### Removed * Removed `SebastianBergmann\GlobalState\Restorer::restoreFunctions()` * This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0 ## [5.0.5] - 2022-02-14 ### Fixed * [#34](https://github.com/sebastianbergmann/global-state/pull/34): Uninitialised typed static properties are not handled correctly ## [5.0.4] - 2022-02-10 ### Fixed * The `$includeTraits` parameter of `SebastianBergmann\GlobalState\Snapshot::__construct()` is not respected ## [5.0.3] - 2021-06-11 ### Changed * `SebastianBergmann\GlobalState\CodeExporter::globalVariables()` now generates code that is compatible with PHP 8.1 ## [5.0.2] - 2020-10-26 ### Fixed * `SebastianBergmann\GlobalState\Exception` now correctly extends `\Throwable` ## [5.0.1] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [5.0.0] - 2020-08-07 ### Changed * The `SebastianBergmann\GlobalState\Blacklist` class has been renamed to `SebastianBergmann\GlobalState\ExcludeList` ## [4.0.0] - 2020-02-07 ### Removed * This component is no longer supported on PHP 7.2 ## [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 [6.0.1]: https://github.com/sebastianbergmann/global-state/compare/6.0.0...6.0.1 [6.0.0]: https://github.com/sebastianbergmann/global-state/compare/5.0.5...6.0.0 [5.0.5]: https://github.com/sebastianbergmann/global-state/compare/5.0.4...5.0.5 [5.0.4]: https://github.com/sebastianbergmann/global-state/compare/5.0.3...5.0.4 [5.0.3]: https://github.com/sebastianbergmann/global-state/compare/5.0.2...5.0.3 [5.0.2]: https://github.com/sebastianbergmann/global-state/compare/5.0.1...5.0.2 [5.0.1]: https://github.com/sebastianbergmann/global-state/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/sebastianbergmann/global-state/compare/4.0.0...5.0.0 [4.0.0]: https://github.com/sebastianbergmann/global-state/compare/3.0.2...4.0.0 [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 PKAZ$global-state/LICENSEnu[BSD 3-Clause License Copyright (c) 2001-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZa&object-enumerator/composer.jsonnu[{ "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" } ], "prefer-stable": true, "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "require": { "php": ">=8.1", "sebastian/object-reflector": "^3.0", "sebastian/recursion-context": "^5.0" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/_fixture/" ] }, "extra": { "branch-alias": { "dev-main": "5.0-dev" } } } PKAZE >PPobject-enumerator/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZ3))$object-enumerator/src/Enumerator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\ObjectEnumerator; use function array_merge; use function is_array; use function is_object; use SebastianBergmann\ObjectReflector\ObjectReflector; use SebastianBergmann\RecursionContext\Context; final class Enumerator { /** * @psalm-return list */ public function enumerate(array|object $variable, Context $processed = new Context): array { $objects = []; if ($processed->contains($variable)) { return $objects; } $array = $variable; /* @noinspection UnusedFunctionResultInspection */ $processed->add($variable); if (is_array($variable)) { foreach ($array as $element) { if (!is_array($element) && !is_object($element)) { continue; } /** @noinspection SlowArrayOperationsInLoopInspection */ $objects = array_merge( $objects, $this->enumerate($element, $processed) ); } return $objects; } $objects[] = $variable; foreach ((new ObjectReflector)->getProperties($variable) as $value) { if (!is_array($value) && !is_object($value)) { continue; } /** @noinspection SlowArrayOperationsInLoopInspection */ $objects = array_merge( $objects, $this->enumerate($value, $processed) ); } return $objects; } } PKAZ_`Uobject-enumerator/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/object-enumerator/v/stable.png)](https://packagist.org/packages/sebastian/object-enumerator) [![CI Status](https://github.com/sebastianbergmann/object-enumerator/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/object-enumerator/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/object-enumerator/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/object-enumerator) [![codecov](https://codecov.io/gh/sebastianbergmann/object-enumerator/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/object-enumerator) # sebastian/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 ``` PKAZ object-enumerator/ChangeLog.mdnu[# Change Log All notable changes to `sebastianbergmann/object-enumerator` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [5.0.0] - 2023-02-03 ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0 ## [4.0.4] - 2020-10-26 ### Fixed * `SebastianBergmann\ObjectEnumerator\Exception` now correctly extends `\Throwable` ## [4.0.3] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [4.0.2] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [4.0.1] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports ## [4.0.0] - 2020-02-07 ### Removed * This component is no longer supported on PHP 7.0, PHP 7.1, and PHP 7.2 ## [3.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 [5.0.0]: https://github.com/sebastianbergmann/object-enumerator/compare/4.0.4...5.0.0 [4.0.4]: https://github.com/sebastianbergmann/object-enumerator/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/object-enumerator/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/object-enumerator/compare/4.0.1...4.0.2 [4.0.1]: https://github.com/sebastianbergmann/object-enumerator/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/sebastianbergmann/object-enumerator/compare/3.0.3...4.0.0 [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 PKAZfobject-enumerator/LICENSEnu[BSD 3-Clause License Copyright (c) 2016-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZxobject-enumerator/phpunit.xmlnu[ tests src PKAZTAA&code-unit-reverse-lookup/composer.jsonnu[{ "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" } ], "config": { "platform": { "php": "8.1.0" }, "optimize-autoloader": true, "sort-packages": true }, "prefer-stable": true, "require": { "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" }, "autoload": { "classmap": [ "src/" ] }, "extra": { "branch-alias": { "dev-main": "3.0-dev" } } } PKAZE >PP$code-unit-reverse-lookup/SECURITY.mdnu[# Security Policy This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver. **If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.** ## Security Contact Information After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`. PKAZ 2 'code-unit-reverse-lookup/src/Wizard.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\CodeUnitReverseLookup; use function array_merge; use function assert; use function get_declared_classes; use function get_declared_traits; use function get_defined_functions; use function is_array; use function range; use ReflectionClass; use ReflectionFunction; use ReflectionFunctionAbstract; use ReflectionMethod; class Wizard { /** * @psalm-var array> */ private array $lookupTable = []; /** * @psalm-var array */ private array $processedClasses = []; /** * @psalm-var array */ private array $processedFunctions = []; public function lookup(string $filename, int $lineNumber): string { if (!isset($this->lookupTable[$filename][$lineNumber])) { $this->updateLookupTable(); } if (isset($this->lookupTable[$filename][$lineNumber])) { return $this->lookupTable[$filename][$lineNumber]; } return $filename . ':' . $lineNumber; } private function updateLookupTable(): void { $this->processClassesAndTraits(); $this->processFunctions(); } private function processClassesAndTraits(): void { $classes = get_declared_classes(); $traits = get_declared_traits(); /* @noinspection PhpConditionAlreadyCheckedInspection */ assert(is_array($traits)); foreach (array_merge($classes, $traits) as $classOrTrait) { if (isset($this->processedClasses[$classOrTrait])) { continue; } foreach ((new ReflectionClass($classOrTrait))->getMethods() as $method) { $this->processFunctionOrMethod($method); } $this->processedClasses[$classOrTrait] = true; } } private function processFunctions(): void { foreach (get_defined_functions()['user'] as $function) { if (isset($this->processedFunctions[$function])) { continue; } $this->processFunctionOrMethod(new ReflectionFunction($function)); $this->processedFunctions[$function] = true; } } private function processFunctionOrMethod(ReflectionFunctionAbstract $functionOrMethod): void { if ($functionOrMethod->isInternal()) { return; } $name = $functionOrMethod->getName(); if ($functionOrMethod instanceof ReflectionMethod) { $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; } if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { $this->lookupTable[$functionOrMethod->getFileName()] = []; } foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; } } } PKAZ=8 "code-unit-reverse-lookup/README.mdnu[[![Latest Stable Version](https://poser.pugx.org/sebastian/code-unit-reverse-lookup/v/stable.png)](https://packagist.org/packages/sebastian/code-unit-reverse-lookup) [![CI Status](https://github.com/sebastianbergmann/code-unit-reverse-lookup/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/code-unit-reverse-lookup/actions) [![Type Coverage](https://shepherd.dev/github/sebastianbergmann/code-unit-reverse-lookup/coverage.svg)](https://shepherd.dev/github/sebastianbergmann/code-unit-reverse-lookup) [![codecov](https://codecov.io/gh/sebastianbergmann/code-unit-reverse-lookup/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/code-unit-reverse-lookup) # sebastian/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 ``` PKAZO *code-unit-reverse-lookup/.psalm/config.xmlnu[ PKAZaB]$$,code-unit-reverse-lookup/.psalm/baseline.xmlnu[ assert(is_array($traits)) is_array($traits) PKAZцd%code-unit-reverse-lookup/ChangeLog.mdnu[# 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. ## [3.0.0] - 2023-02-03 ### Removed * This component is no longer supported on PHP 7.3, PHP 7.4, and PHP 8.0 ## [2.0.3] - 2020-09-28 ### Changed * Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3` ## [2.0.2] - 2020-06-26 ### Added * This component is now supported on PHP 8 ## [2.0.1] - 2020-06-15 ### Changed * Tests etc. are now ignored for archive exports ## 2.0.0 - 2020-02-07 ### Removed * This component is no longer supported on PHP 5.6, PHP 7.0, PHP 7.1, and PHP 7.2 ## 1.0.0 - 2016-02-13 ### Added * Initial release [3.0.0]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/2.0.3...3.0.0 [2.0.3]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/2.0.2...2.0.3 [2.0.2]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/2.0.1...2.0.2 [2.0.1]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/2.0.0...2.0.1 [2.0.0]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/1.0.0...2.0.0 PKAZf code-unit-reverse-lookup/LICENSEnu[BSD 3-Clause License Copyright (c) 2016-2023, Sebastian Bergmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PKAZnnversion/composer.jsonnu[PKAZE >PPversion/SECURITY.mdnu[PKAZBthversion/ChangeLog.mdnu[PKAZVo.version/LICENSEnu[PKAZAAh"exporter/composer.jsonnu[PKAZKJuu(exporter/SECURITY.mdnu[PKAZak;~&~&0exporter/src/Exporter.phpnu[PKAZ_P oWexporter/README.mdnu[PKAZՅV V dexporter/ChangeLog.mdnu[PKAZT/qexporter/LICENSEnu[PKAZ??jwenvironment/composer.jsonnu[PKAZKJuu{environment/SECURITY.mdnu[PKAZb6environment/src/Console.phpnu[PKAZ>$$environment/src/Runtime.phpnu[PKAZffenvironment/README.mdnu[PKAZX88environment/ChangeLog.mdnu[PKAZWj-environment/LICENSEnu[PKAZ #krecursion-context/composer.jsonnu[PKAZE >PPrecursion-context/SECURITY.mdnu[PKAZ!Lrecursion-context/src/Context.phpnu[PKAZ >--recursion-context/README.mdnu[PKAZ <[&recursion-context/ChangeLog.mdnu[PKAZT/recursion-context/LICENSEnu[PKAZ#=e$$scomparator/composer.jsonnu[PKAZKJuucomparator/SECURITY.mdnu[PKAZ` # comparator/src/ScalarComparator.phpnu[PKAZ32!comparator/src/TypeComparator.phpnu[PKAZG3WB B $comparator/src/DOMNodeComparator.phpnu[PKAZ88''comparator/src/MockObjectComparator.phpnu[PKAZC-$",comparator/src/ComparisonFailure.phpnu[PKAZ#-x3comparator/src/SplObjectStorageComparator.phpnu[PKAZ+ [X X :comparator/src/Factory.phpnu[PKAZQ6$jHcomparator/src/NumericComparator.phpnu[PKAZqb  %{Pcomparator/src/DateTimeComparator.phpnu[PKAZ%Ycomparator/src/ResourceComparator.phpnu[PKAZӃ.^comparator/src/exceptions/RuntimeException.phpnu[PKAZt mm'`comparator/src/exceptions/Exception.phpnu[PKAZ2h&Tbcomparator/src/ExceptionComparator.phpnu[PKAZǫ33"zfcomparator/src/ArrayComparator.phpnu[PKAZR7øA A #ucomparator/src/ObjectComparator.phpnu[PKAZ0tFFcomparator/src/Comparator.phpnu[PKAZ;;&comparator/README.mdnu[PKAZgJJcomparator/ChangeLog.mdnu[PKAZT6comparator/LICENSEnu[PKAZpsobject-reflector/composer.jsonnu[PKAZE >PPaobject-reflector/SECURITY.mdnu[PKAZt(object-reflector/src/ObjectReflector.phpnu[PKAZɆKobject-reflector/README.mdnu[PKAZaobject-reflector/ChangeLog.mdnu[PKAZR6object-reflector/LICENSEnu[PKAZˆIcode-unit/composer.jsonnu[PKAZE >PP+code-unit/SECURITY.mdnu[PKAZ%|UU,code-unit/src/CodeUnitCollectionIterator.phpnu[PKAZ 1Z  !qcode-unit/src/TraitMethodUnit.phpnu[PKAZN'$code-unit/src/CodeUnitCollection.phpnu[PKAZJ  !>code-unit/src/ClassMethodUnit.phpnu[PKAZm=code-unit/src/ClassUnit.phpnu[PKAZl`%code-unit/src/InterfaceMethodUnit.phpnu[PKAZ=%tFcode-unit/src/InterfaceUnit.phpnu[PKAZcode-unit/src/FileUnit.phpnu[PKAZTJ˿code-unit/src/TraitUnit.phpnu[PKAZ6*a5code-unit/src/exceptions/InvalidCodeUnitException.phpnu[PKAZO0!code-unit/src/exceptions/ReflectionException.phpnu[PKAZwkjj&code-unit/src/exceptions/Exception.phpnu[PKAZΖ-code-unit/src/exceptions/NoTraitException.phpnu[PKAZ=i3030code-unit/src/CodeUnit.phpnu[PKAZXVXJ code-unit/src/Mapper.phpnu[PKAZrYi=code-unit/src/FunctionUnit.phpnu[PKAZsρ?code-unit/README.mdnu[PKAZG3 Dcode-unit/ChangeLog.mdnu[PKAZP@Ncode-unit/LICENSEnu[PKAZy}33ZTcomplexity/composer.jsonnu[PKAZKJuuXcomplexity/SECURITY.mdnu[PKAZ%#2 7`complexity/src/Visitor/ComplexityCalculatingVisitor.phpnu[PKAZ;ɽ,Ancomplexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.phpnu[PKAZ ;;(ucomplexity/src/Complexity/Complexity.phpnu[PKAZZ 28{complexity/src/Complexity/ComplexityCollection.phpnu[PKAZh:[complexity/src/Complexity/ComplexityCollectionIterator.phpnu[PKAZlE-܉complexity/src/Exception/RuntimeException.phpnu[PKAZ-mm&complexity/src/Exception/Exception.phpnu[PKAZDZ complexity/src/Calculator.phpnu[PKAZ[complexity/README.mdnu[PKAZP?complexity/ChangeLog.mdnu[PKAZP@Ңcomplexity/LICENSEnu[PKAZS깞cli-parser/composer.jsonnu[PKAZE >PPcli-parser/SECURITY.mdnu[PKAZ@1ll'cli-parser/src/exceptions/Exception.phpnu[PKAZ5ANcli-parser/src/exceptions/OptionDoesNotAllowArgumentException.phpnu[PKAZw~DYcli-parser/src/exceptions/RequiredOptionArgumentMissingException.phpnu[PKAZ=6pcli-parser/src/exceptions/AmbiguousOptionException.phpnu[PKAZzz4Wcli-parser/src/exceptions/UnknownOptionException.phpnu[PKAZ5cli-parser/src/Parser.phpnu[PKAZb++&cli-parser/README.mdnu[PKAZBcli-parser/ChangeLog.mdnu[PKAZP@cli-parser/LICENSEnu[PKAZMjtype/composer.jsonnu[PKAZE >PPtype/SECURITY.mdnu[PKAZ@i..>type/src/ReflectionMapper.phpnu[PKAZtype/src/type/VoidType.phpnu[PKAZ@5#type/src/type/GenericObjectType.phpnu[PKAZP^+1type/src/type/Type.phpnu[PKAZ?   type/src/type/UnknownType.phpnu[PKAZo|xtype/src/type/NullType.phpnu[PKAZ-{type/src/type/ObjectType.phpnu[PKAZi @$type/src/type/UnionType.phpnu[PKAZ660type/src/type/SimpleType.phpnu[PKAZ/_7type/src/type/CallableType.phpnu[PKAZL/Htype/src/type/MixedType.phpnu[PKAZV!pp Ltype/src/type/FalseType.phpnu[PKAZkkOtype/src/type/TrueType.phpnu[PKAZӆsStype/src/type/StaticType.phpnu[PKAZ2AYtype/src/type/IterableType.phpnu[PKAZ _type/src/type/NeverType.phpnu[PKAZ< "btype/src/type/IntersectionType.phpnu[PKAZ56ww'ntype/src/exception/RuntimeException.phpnu[PKAZg@ɼaa otype/src/exception/Exception.phpnu[PKAZ qtype/src/Parameter.phpnu[PKAZX@@ttype/src/TypeName.phpnu[PKAZM˳Y|type/README.mdnu[PKAZ type/infection.jsonnu[PKAZyttype/ChangeLog.mdnu[PKAZ  Dtype/LICENSEnu[PKAZӾ{diff/composer.jsonnu[PKAZKJuu{diff/SECURITY.mdnu[PKAZ;L0diff/src/Line.phpnu[PKAZES$#KKdiff/src/Differ.phpnu[PKAZ>faa diff/src/Exception/Exception.phpnu[PKAZ\2-diff/src/Exception/ConfigurationException.phpnu[PKAZL}f/diff/src/Exception/InvalidArgumentException.phpnu[PKAZN diff/src/Chunk.phpnu[PKAZ! >diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.phpnu[PKAZ>D/%diff/src/LongestCommonSubsequenceCalculator.phpnu[PKAZKU{@ @ <jdiff/src/TimeEfficientLongestCommonSubsequenceCalculator.phpnu[PKAZw.diff/src/Output/AbstractChunkOutputBuilder.phpnu[PKAZM'11)vdiff/src/Output/DiffOnlyOutputBuilder.phpnu[PKAZfܢ**2diff/src/Output/StrictUnifiedDiffOutputBuilder.phpnu[PKAZ0  .'diff/src/Output/DiffOutputBuilderInterface.phpnu[PKAZ# ,k)diff/src/Output/UnifiedDiffOutputBuilder.phpnu[PKAZuP^^Jdiff/src/Diff.phpnu[PKAZ-Jq 9Odiff/src/Parser.phpnu[PKAZZx x m[diff/README.mdnu[PKAZj8({ #|diff/ChangeLog.mdnu[PKAZT )diff/LICENSEnu[PKAZT>>`lines-of-code/composer.jsonnu[PKAZKJuulines-of-code/SECURITY.mdnu[PKAZ>« lines-of-code/src/Counter.phpnu[PKAZkJM  )lines-of-code/src/LineCountingVisitor.phpnu[PKAZ/0lines-of-code/src/Exception/RuntimeException.phpnu[PKAZ0+qq)lines-of-code/src/Exception/Exception.phpnu[PKAZy<6ųlines-of-code/src/Exception/NegativeValueException.phpnu[PKAZ 8޵lines-of-code/src/Exception/IllogicalValuesException.phpnu[PKAZs^ ^ !lines-of-code/src/LinesOfCode.phpnu[PKAZ==lines-of-code/README.mdnu[PKAZk??lines-of-code/ChangeLog.mdnu[PKAZP@lines-of-code/LICENSEnu[PKAZN  global-state/composer.jsonnu[PKAZKJuu:global-state/SECURITY.mdnu[PKAZ}'f(f(global-state/src/Snapshot.phpnu[PKAZG  global-state/src/ExcludeList.phpnu[PKAZKP; ; !global-state/src/CodeExporter.phpnu[PKAZjE  global-state/src/Restorer.phpnu[PKAZpƆ0.global-state/src/exceptions/RuntimeException.phpnu[PKAZ pp){0global-state/src/exceptions/Exception.phpnu[PKAZMKKD2global-state/README.mdnu[PKAZN 6global-state/ChangeLog.mdnu[PKAZ$Dglobal-state/LICENSEnu[PKAZa&Kobject-enumerator/composer.jsonnu[PKAZE >PPUOobject-enumerator/SECURITY.mdnu[PKAZ3))$Qobject-enumerator/src/Enumerator.phpnu[PKAZ_`UoYobject-enumerator/README.mdnu[PKAZ :^object-enumerator/ChangeLog.mdnu[PKAZfhobject-enumerator/LICENSEnu[PKAZxnobject-enumerator/phpunit.xmlnu[PKAZTAA&&rcode-unit-reverse-lookup/composer.jsonnu[PKAZE >PP$ucode-unit-reverse-lookup/SECURITY.mdnu[PKAZ 2 'axcode-unit-reverse-lookup/src/Wizard.phpnu[PKAZ=8 "ccode-unit-reverse-lookup/README.mdnu[PKAZO *mcode-unit-reverse-lookup/.psalm/config.xmlnu[PKAZaB]$$,code-unit-reverse-lookup/.psalm/baseline.xmlnu[PKAZцd%code-unit-reverse-lookup/ChangeLog.mdnu[PKAZf 1code-unit-reverse-lookup/LICENSEnu[PKC|