This commit is contained in:
2025-02-04 12:40:43 +03:00
parent 4bcb4c60dd
commit 50343d5a87
372 changed files with 9019 additions and 6684 deletions

View File

@@ -0,0 +1,74 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [11.0.8] - 2024-12-11
### Changed
* [#1054](https://github.com/sebastianbergmann/php-code-coverage/pull/1054): Use click event for toggling "tests covering this line" popover in HTML report
## [11.0.7] - 2024-10-09
### Changed
* [#1037](https://github.com/sebastianbergmann/php-code-coverage/pull/1037): Upgrade Bootstrap to version 5.3.3 for HTML report
* [#1046](https://github.com/sebastianbergmann/php-code-coverage/pull/1046): CSS fixes for HTML report
### Deprecated
* The `SebastianBergmann\CodeCoverage\Filter::includeUncoveredFiles()`, `SebastianBergmann\CodeCoverage\Filter::excludeUncoveredFiles()`, and `SebastianBergmann\CodeCoverage\Filter::excludeFile()` methods have been deprecated
## [11.0.6] - 2024-08-22
### Changed
* Updated dependencies (so that users that install using Composer's `--prefer-lowest` CLI option also get recent versions)
## [11.0.5] - 2024-07-03
### Changed
* This project now uses PHPStan instead of Psalm for static analysis
## [11.0.4] - 2024-06-29
### Fixed
* [#967](https://github.com/sebastianbergmann/php-code-coverage/issues/967): Identification of executable lines for `match` expressions does not work correctly
## [11.0.3] - 2024-03-12
### Fixed
* [#1033](https://github.com/sebastianbergmann/php-code-coverage/issues/1033): `@codeCoverageIgnore` annotation does not work on `enum`
## [11.0.2] - 2024-03-09
### Changed
* [#1032](https://github.com/sebastianbergmann/php-code-coverage/pull/1032): Pad lines in code coverage report only when colors are shown
## [11.0.1] - 2024-03-02
### Changed
* Do not use implicitly nullable parameters
## [11.0.0] - 2024-02-02
### Removed
* The `SebastianBergmann\CodeCoverage\Filter::includeDirectory()`, `SebastianBergmann\CodeCoverage\Filter::excludeDirectory()`, and `SebastianBergmann\CodeCoverage\Filter::excludeFile()` methods have been removed
* This component now requires PHP-Parser 5
* This component is no longer supported on PHP 8.1
[11.0.8]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.7...11.0.8
[11.0.7]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.6...11.0.7
[11.0.6]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.5...11.0.6
[11.0.5]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.4...11.0.5
[11.0.4]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.3...11.0.4
[11.0.3]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.2...11.0.3
[11.0.2]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.1...11.0.2
[11.0.1]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0.0...11.0.1
[11.0.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/10.1...11.0.0

View File

@@ -1,19 +0,0 @@
# ChangeLog
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [12.0.0] - 2025-02-07
### Changed
* `CodeCoverage::stop()` and `CodeCoverage::append()` now expect arguments of type `TargetCollection` instead of `array` to configure code coverage targets
### Removed
* Methods `CodeCoverage::includeUncoveredFiles()` and `CodeCoverage::excludeUncoveredFiles()`
* Method `CodeCoverage::detectsDeadCode()`
* Optional argument `$linesToBeUsed` of `CodeCoverage::stop()` and `CodeCoverage::append()` methods
* This component is no longer supported on PHP 8.2
* This component no longer supports Xdebug versions before Xdebug 3.1
[12.0.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/11.0...main

View File

@@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2009-2025, Sebastian Bergmann
Copyright (c) 2009-2024, Sebastian Bergmann
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -22,29 +22,29 @@
},
"config": {
"platform": {
"php": "8.3.0"
"php": "8.2.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.3",
"php": ">=8.2",
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^5.4.0",
"phpunit/php-file-iterator": "^6.0-dev",
"phpunit/php-text-template": "^5.0-dev",
"sebastian/complexity": "^5.0-dev",
"sebastian/environment": "^8.0-dev",
"sebastian/lines-of-code": "^4.0-dev",
"sebastian/version": "^6.0-dev",
"nikic/php-parser": "^5.3.1",
"phpunit/php-file-iterator": "^5.1.0",
"phpunit/php-text-template": "^4.0.1",
"sebastian/code-unit-reverse-lookup": "^4.0.1",
"sebastian/complexity": "^4.0.1",
"sebastian/environment": "^7.2.0",
"sebastian/lines-of-code": "^3.0.1",
"sebastian/version": "^5.0.2",
"theseer/tokenizer": "^1.2.3"
},
"require-dev": {
"phpunit/phpunit": "^12.0-dev"
"phpunit/phpunit": "^11.5.0"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@@ -62,7 +62,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "12.0.x-dev"
"dev-main": "11.0.x-dev"
}
}
}

View File

@@ -14,9 +14,11 @@ use function array_diff_key;
use function array_flip;
use function array_keys;
use function array_merge;
use function array_merge_recursive;
use function array_unique;
use function count;
use function explode;
use function is_array;
use function is_file;
use function sort;
use ReflectionClass;
@@ -28,33 +30,37 @@ use SebastianBergmann\CodeCoverage\Node\Directory;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingFileAnalyser;
use SebastianBergmann\CodeCoverage\Test\Target\MapBuilder;
use SebastianBergmann\CodeCoverage\Test\Target\Mapper;
use SebastianBergmann\CodeCoverage\Test\Target\TargetCollection;
use SebastianBergmann\CodeCoverage\Test\Target\TargetCollectionValidator;
use SebastianBergmann\CodeCoverage\Test\Target\ValidationResult;
use SebastianBergmann\CodeCoverage\Test\TestSize\TestSize;
use SebastianBergmann\CodeCoverage\Test\TestStatus\TestStatus;
use SebastianBergmann\CodeUnitReverseLookup\Wizard;
/**
* Provides collection functionality for PHP code coverage information.
*
* @phpstan-type TestType array{size: string, status: string}
* @phpstan-type TargetedLines array<non-empty-string, list<positive-int>>
* @phpstan-type TestType = array{
* size: string,
* status: string,
* }
*/
final class CodeCoverage
{
private const string UNCOVERED_FILES = 'UNCOVERED_FILES';
private const UNCOVERED_FILES = 'UNCOVERED_FILES';
private readonly Driver $driver;
private readonly Filter $filter;
private ?Mapper $targetMapper = null;
private readonly Wizard $wizard;
private bool $checkForUnintentionallyCoveredCode = false;
private bool $includeUncoveredFiles = true;
private bool $ignoreDeprecatedCode = false;
private ?string $currentId = null;
private ?TestSize $currentSize = null;
private ProcessedCodeCoverageData $data;
private bool $useAnnotationsForIgnoringCode = true;
/**
* @var array<string,list<int>>
*/
private array $linesToBeIgnored = [];
/**
* @var array<string, TestType>
*/
@@ -73,6 +79,7 @@ final class CodeCoverage
$this->driver = $driver;
$this->filter = $filter;
$this->data = new ProcessedCodeCoverageData;
$this->wizard = new Wizard;
}
/**
@@ -121,7 +128,9 @@ final class CodeCoverage
public function getData(bool $raw = false): ProcessedCodeCoverageData
{
if (!$raw) {
$this->addUncoveredFilesFromFilter();
if ($this->includeUncoveredFiles) {
$this->addUncoveredFilesFromFilter();
}
}
return $this->data;
@@ -165,11 +174,19 @@ final class CodeCoverage
$this->cachedReport = null;
}
public function stop(bool $append = true, ?TestStatus $status = null, null|false|TargetCollection $covers = null, ?TargetCollection $uses = null): RawCodeCoverageData
/**
* @param array<string,list<int>> $linesToBeIgnored
*/
public function stop(bool $append = true, ?TestStatus $status = null, array|false $linesToBeCovered = [], array $linesToBeUsed = [], array $linesToBeIgnored = []): RawCodeCoverageData
{
$data = $this->driver->stop();
$this->append($data, null, $append, $status, $covers, $uses);
$this->linesToBeIgnored = array_merge_recursive(
$this->linesToBeIgnored,
$linesToBeIgnored,
);
$this->append($data, null, $append, $status, $linesToBeCovered, $linesToBeUsed, $linesToBeIgnored);
$this->currentId = null;
$this->currentSize = null;
@@ -179,11 +196,13 @@ final class CodeCoverage
}
/**
* @param array<string,list<int>> $linesToBeIgnored
*
* @throws ReflectionException
* @throws TestIdMissingException
* @throws UnintentionallyCoveredCodeException
*/
public function append(RawCodeCoverageData $rawData, ?string $id = null, bool $append = true, ?TestStatus $status = null, null|false|TargetCollection $covers = null, ?TargetCollection $uses = null): void
public function append(RawCodeCoverageData $rawData, ?string $id = null, bool $append = true, ?TestStatus $status = null, array|false $linesToBeCovered = [], array $linesToBeUsed = [], array $linesToBeIgnored = []): void
{
if ($id === null) {
$id = $this->currentId;
@@ -193,32 +212,24 @@ final class CodeCoverage
throw new TestIdMissingException;
}
$this->cachedReport = null;
if ($status === null) {
$status = TestStatus::unknown();
}
if ($covers === null) {
$covers = TargetCollection::fromArray([]);
}
if ($uses === null) {
$uses = TargetCollection::fromArray([]);
}
$size = $this->currentSize;
if ($size === null) {
$size = TestSize::unknown();
}
$this->cachedReport = null;
$this->applyFilter($rawData);
$this->applyExecutableLinesFilter($rawData);
if ($this->useAnnotationsForIgnoringCode) {
$this->applyIgnoredLinesFilter($rawData);
$this->applyIgnoredLinesFilter($rawData, $linesToBeIgnored);
}
$this->data->initializeUnseenData($rawData);
@@ -231,17 +242,6 @@ final class CodeCoverage
return;
}
$linesToBeCovered = false;
$linesToBeUsed = [];
if ($covers !== false) {
$linesToBeCovered = $this->targetMapper()->mapTargets($covers);
}
if ($linesToBeCovered !== false) {
$linesToBeUsed = $this->targetMapper()->mapTargets($uses);
}
$this->applyCoversAndUsesFilter(
$rawData,
$linesToBeCovered,
@@ -287,6 +287,22 @@ final class CodeCoverage
$this->checkForUnintentionallyCoveredCode = false;
}
/**
* @deprecated
*/
public function includeUncoveredFiles(): void
{
$this->includeUncoveredFiles = true;
}
/**
* @deprecated
*/
public function excludeUncoveredFiles(): void
{
$this->includeUncoveredFiles = false;
}
public function enableAnnotationsForIgnoringCode(): void
{
$this->useAnnotationsForIgnoringCode = true;
@@ -362,15 +378,12 @@ final class CodeCoverage
return $this->driver->collectsBranchAndPathCoverage();
}
public function validate(TargetCollection $targets): ValidationResult
public function detectsDeadCode(): bool
{
return (new TargetCollectionValidator)->validate($this->targetMapper(), $targets);
return $this->driver->detectsDeadCode();
}
/**
* @param false|TargetedLines $linesToBeCovered
* @param TargetedLines $linesToBeUsed
*
* @throws ReflectionException
* @throws UnintentionallyCoveredCodeException
*/
@@ -397,9 +410,11 @@ final class CodeCoverage
$rawData->removeCoverageDataForFile($fileWithNoCoverage);
}
foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) {
$rawData->keepLineCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
$rawData->keepFunctionCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
if (is_array($linesToBeCovered)) {
foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) {
$rawData->keepLineCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
$rawData->keepFunctionCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
}
}
}
@@ -437,13 +452,23 @@ final class CodeCoverage
}
}
private function applyIgnoredLinesFilter(RawCodeCoverageData $data): void
/**
* @param array<string,list<int>> $linesToBeIgnored
*/
private function applyIgnoredLinesFilter(RawCodeCoverageData $data, array $linesToBeIgnored): void
{
foreach (array_keys($data->lineCoverage()) as $filename) {
if (!$this->filter->isFile($filename)) {
continue;
}
if (isset($linesToBeIgnored[$filename])) {
$data->removeCoverageDataForLines(
$filename,
$linesToBeIgnored[$filename],
);
}
$data->removeCoverageDataForLines(
$filename,
$this->analyser()->ignoredLinesFor($filename),
@@ -469,15 +494,13 @@ final class CodeCoverage
$this->analyser(),
),
self::UNCOVERED_FILES,
linesToBeIgnored: $this->linesToBeIgnored,
);
}
}
}
/**
* @param TargetedLines $linesToBeCovered
* @param TargetedLines $linesToBeUsed
*
* @throws ReflectionException
* @throws UnintentionallyCoveredCodeException
*/
@@ -493,7 +516,7 @@ final class CodeCoverage
foreach ($data->lineCoverage() as $file => $_data) {
foreach ($_data as $line => $flag) {
if ($flag === 1 && !isset($allowedLines[$file][$line])) {
$unintentionallyCoveredUnits[] = $this->targetMapper->lookup($file, $line);
$unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line);
}
}
}
@@ -507,12 +530,6 @@ final class CodeCoverage
}
}
/**
* @param TargetedLines $linesToBeCovered
* @param TargetedLines $linesToBeUsed
*
* @return TargetedLines
*/
private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array
{
$allowedLines = [];
@@ -595,19 +612,6 @@ final class CodeCoverage
return $processed;
}
private function targetMapper(): Mapper
{
if ($this->targetMapper !== null) {
return $this->targetMapper;
}
$this->targetMapper = new Mapper(
(new MapBuilder)->build($this->filter, $this->analyser()),
);
return $this->targetMapper;
}
private function analyser(): FileAnalyser
{
if ($this->analyser !== null) {

View File

@@ -17,31 +17,13 @@ use function count;
use function is_array;
use function ksort;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\Driver\XdebugDriver;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type XdebugFunctionCoverageType from XdebugDriver
* @phpstan-import-type XdebugFunctionCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver
*
* @phpstan-type TestIdType string
* @phpstan-type FunctionCoverageDataType array{
* branches: array<int, array{
* op_start: int,
* op_end: int,
* line_start: int,
* line_end: int,
* hit: list<TestIdType>,
* out: array<int, int>,
* out_hit: array<int, int>,
* }>,
* paths: array<int, array{
* path: array<int, int>,
* hit: list<TestIdType>,
* }>,
* hit: list<TestIdType>
* }
* @phpstan-type FunctionCoverageType array<string, array<string, FunctionCoverageDataType>>
* @phpstan-type TestIdType = string
*/
final class ProcessedCodeCoverageData
{
@@ -58,7 +40,22 @@ final class ProcessedCodeCoverageData
* Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array
* of testcase ids.
*
* @var FunctionCoverageType
* @var array<string, array<string, array{
* branches: array<int, array{
* op_start: int,
* op_end: int,
* line_start: int,
* line_end: int,
* hit: list<TestIdType>,
* out: array<int, int>,
* out_hit: array<int, int>,
* }>,
* paths: array<int, array{
* path: array<int, int>,
* hit: list<TestIdType>,
* }>,
* hit: list<TestIdType>
* }>>
*/
private array $functionCoverage = [];
@@ -219,8 +216,6 @@ final class ProcessedCodeCoverageData
* 4 = the line has been tested
*
* During a merge, a higher number is better.
*
* @return 1|2|3|4
*/
private function priorityForLine(array $data, int $line): int
{
@@ -242,7 +237,7 @@ final class ProcessedCodeCoverageData
/**
* For a function we have never seen before, copy all data over and simply init the 'hit' array.
*
* @param FunctionCoverageDataType|XdebugFunctionCoverageType $functionData
* @param XdebugFunctionCoverageType $functionData
*/
private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void
{
@@ -262,7 +257,7 @@ final class ProcessedCodeCoverageData
* Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling
* containers) mean that the functions inside a file cannot be relied upon to be static.
*
* @param FunctionCoverageDataType|XdebugFunctionCoverageType $functionData
* @param XdebugFunctionCoverageType $functionData
*/
private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void
{

View File

@@ -14,7 +14,6 @@ use function array_diff_key;
use function array_flip;
use function array_intersect;
use function array_intersect_key;
use function array_map;
use function count;
use function explode;
use function file_get_contents;
@@ -26,15 +25,14 @@ use function str_ends_with;
use function str_starts_with;
use function trim;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\Driver\XdebugDriver;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type XdebugFunctionsCoverageType from XdebugDriver
* @phpstan-import-type XdebugCodeCoverageWithoutPathCoverageType from XdebugDriver
* @phpstan-import-type XdebugCodeCoverageWithPathCoverageType from XdebugDriver
* @phpstan-import-type XdebugFunctionsCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver
* @phpstan-import-type XdebugCodeCoverageWithoutPathCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver
* @phpstan-import-type XdebugCodeCoverageWithPathCoverageType from \SebastianBergmann\CodeCoverage\Driver\XdebugDriver
*/
final class RawCodeCoverageData
{
@@ -88,10 +86,11 @@ final class RawCodeCoverageData
public static function fromUncoveredFile(string $filename, FileAnalyser $analyser): self
{
$lineCoverage = array_map(
static fn (): int => Driver::LINE_NOT_EXECUTED,
$analyser->executableLinesIn($filename),
);
$lineCoverage = [];
foreach ($analyser->executableLinesIn($filename) as $line => $branch) {
$lineCoverage[$line] = Driver::LINE_NOT_EXECUTED;
}
return new self([$filename => $lineCoverage], []);
}
@@ -261,9 +260,6 @@ final class RawCodeCoverageData
}
}
/**
* @return array<int>
*/
private function getEmptyLinesForFile(string $filename): array
{
if (!isset(self::$emptyLineCache[$filename])) {

View File

@@ -12,6 +12,7 @@ namespace SebastianBergmann\CodeCoverage\Driver;
use function sprintf;
use SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException;
use SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData;
use SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -23,36 +24,37 @@ abstract class Driver
*
* @see http://xdebug.org/docs/code_coverage
*/
public const int LINE_NOT_EXECUTABLE = -2;
public const LINE_NOT_EXECUTABLE = -2;
/**
* @var int
*
* @see http://xdebug.org/docs/code_coverage
*/
public const int LINE_NOT_EXECUTED = -1;
public const LINE_NOT_EXECUTED = -1;
/**
* @var int
*
* @see http://xdebug.org/docs/code_coverage
*/
public const int LINE_EXECUTED = 1;
public const LINE_EXECUTED = 1;
/**
* @var int
*
* @see http://xdebug.org/docs/code_coverage
*/
public const int BRANCH_NOT_HIT = 0;
public const BRANCH_NOT_HIT = 0;
/**
* @var int
*
* @see http://xdebug.org/docs/code_coverage
*/
public const int BRANCH_HIT = 1;
public const BRANCH_HIT = 1;
private bool $collectBranchAndPathCoverage = false;
private bool $detectDeadCode = false;
public function canCollectBranchAndPathCoverage(): bool
{
@@ -86,6 +88,38 @@ abstract class Driver
$this->collectBranchAndPathCoverage = false;
}
public function canDetectDeadCode(): bool
{
return false;
}
public function detectsDeadCode(): bool
{
return $this->detectDeadCode;
}
/**
* @throws DeadCodeDetectionNotSupportedException
*/
public function enableDeadCodeDetection(): void
{
if (!$this->canDetectDeadCode()) {
throw new DeadCodeDetectionNotSupportedException(
sprintf(
'%s does not support dead code detection',
$this->nameAndVersion(),
),
);
}
$this->detectDeadCode = true;
}
public function disableDeadCodeDetection(): void
{
$this->detectDeadCode = false;
}
abstract public function nameAndVersion(): string;
abstract public function start(): void;

View File

@@ -38,9 +38,6 @@ final class PcovDriver extends Driver
$this->filter = $filter;
}
/**
* @codeCoverageIgnore
*/
public function start(): void
{
start();
@@ -50,7 +47,6 @@ final class PcovDriver extends Driver
{
stop();
// @codeCoverageIgnoreStart
$filesToCollectCoverageFor = waiting();
$collected = [];
@@ -65,7 +61,6 @@ final class PcovDriver extends Driver
}
return RawCodeCoverageData::fromXdebugWithoutPathCoverage($collected);
// @codeCoverageIgnoreEnd
}
public function nameAndVersion(): string

View File

@@ -21,7 +21,6 @@ final class Selector
* @throws PcovNotAvailableException
* @throws XdebugNotAvailableException
* @throws XdebugNotEnabledException
* @throws XdebugVersionNotSupportedException
*/
public function forLineCoverage(Filter $filter): Driver
{
@@ -32,7 +31,11 @@ final class Selector
}
if ($runtime->hasXdebug()) {
return new XdebugDriver($filter);
$driver = new XdebugDriver($filter);
$driver->enableDeadCodeDetection();
return $driver;
}
throw new NoCodeCoverageDriverAvailableException;
@@ -42,13 +45,13 @@ final class Selector
* @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException
* @throws XdebugNotAvailableException
* @throws XdebugNotEnabledException
* @throws XdebugVersionNotSupportedException
*/
public function forLineAndPathCoverage(Filter $filter): Driver
{
if ((new Runtime)->hasXdebug()) {
$driver = new XdebugDriver($filter);
$driver->enableDeadCodeDetection();
$driver->enableBranchAndPathCoverage();
return $driver;

View File

@@ -14,8 +14,11 @@ use const XDEBUG_CC_DEAD_CODE;
use const XDEBUG_CC_UNUSED;
use const XDEBUG_FILTER_CODE_COVERAGE;
use const XDEBUG_PATH_INCLUDE;
use function explode;
use function extension_loaded;
use function getenv;
use function in_array;
use function ini_get;
use function phpversion;
use function version_compare;
use function xdebug_get_code_coverage;
@@ -31,8 +34,8 @@ use SebastianBergmann\CodeCoverage\Filter;
*
* @see https://xdebug.org/docs/code_coverage#xdebug_get_code_coverage
*
* @phpstan-type XdebugLinesCoverageType array<int, int>
* @phpstan-type XdebugBranchCoverageType array{
* @phpstan-type XdebugLinesCoverageType = array<int, int>
* @phpstan-type XdebugBranchCoverageType = array{
* op_start: int,
* op_end: int,
* line_start: int,
@@ -41,32 +44,32 @@ use SebastianBergmann\CodeCoverage\Filter;
* out: array<int, int>,
* out_hit: array<int, int>,
* }
* @phpstan-type XdebugPathCoverageType array{
* @phpstan-type XdebugPathCoverageType = array{
* path: array<int, int>,
* hit: int,
* }
* @phpstan-type XdebugFunctionCoverageType array{
* @phpstan-type XdebugFunctionCoverageType = array{
* branches: array<int, XdebugBranchCoverageType>,
* paths: array<int, XdebugPathCoverageType>,
* }
* @phpstan-type XdebugFunctionsCoverageType array<string, XdebugFunctionCoverageType>
* @phpstan-type XdebugPathAndBranchesCoverageType array{
* @phpstan-type XdebugFunctionsCoverageType = array<string, XdebugFunctionCoverageType>
* @phpstan-type XdebugPathAndBranchesCoverageType = array{
* lines: XdebugLinesCoverageType,
* functions: XdebugFunctionsCoverageType,
* }
* @phpstan-type XdebugCodeCoverageWithoutPathCoverageType array<string, XdebugLinesCoverageType>
* @phpstan-type XdebugCodeCoverageWithPathCoverageType array<string, XdebugPathAndBranchesCoverageType>
* @phpstan-type XdebugCodeCoverageWithoutPathCoverageType = array<string, XdebugLinesCoverageType>
* @phpstan-type XdebugCodeCoverageWithPathCoverageType = array<string, XdebugPathAndBranchesCoverageType>
*/
final class XdebugDriver extends Driver
{
/**
* @throws XdebugNotAvailableException
* @throws XdebugNotEnabledException
* @throws XdebugVersionNotSupportedException
*/
public function __construct(Filter $filter)
{
$this->ensureXdebugIsAvailable();
$this->ensureXdebugCodeCoverageFeatureIsEnabled();
if (!$filter->isEmpty()) {
xdebug_set_filter(
@@ -82,9 +85,18 @@ final class XdebugDriver extends Driver
return true;
}
public function canDetectDeadCode(): bool
{
return true;
}
public function start(): void
{
$flags = XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE;
$flags = XDEBUG_CC_UNUSED;
if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) {
$flags |= XDEBUG_CC_DEAD_CODE;
}
if ($this->collectsBranchAndPathCoverage()) {
$flags |= XDEBUG_CC_BRANCH_CHECK;
@@ -115,20 +127,35 @@ final class XdebugDriver extends Driver
/**
* @throws XdebugNotAvailableException
* @throws XdebugNotEnabledException
* @throws XdebugVersionNotSupportedException
*/
private function ensureXdebugIsAvailable(): void
{
if (!extension_loaded('xdebug')) {
throw new XdebugNotAvailableException;
}
}
if (!version_compare(phpversion('xdebug'), '3.1', '>=')) {
throw new XdebugVersionNotSupportedException(phpversion('xdebug'));
/**
* @throws XdebugNotEnabledException
*/
private function ensureXdebugCodeCoverageFeatureIsEnabled(): void
{
if (version_compare(phpversion('xdebug'), '3.1', '>=')) {
if (!in_array('coverage', xdebug_info('mode'), true)) {
throw new XdebugNotEnabledException;
}
return;
}
if (!in_array('coverage', xdebug_info('mode'), true)) {
$mode = getenv('XDEBUG_MODE');
if ($mode === false || $mode === '') {
$mode = ini_get('xdebug.mode');
}
if ($mode === false ||
!in_array('coverage', explode(',', $mode), true)) {
throw new XdebugNotEnabledException;
}
}

View File

@@ -0,0 +1,16 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
use RuntimeException;
final class DeadCodeDetectionNotSupportedException extends RuntimeException implements Exception
{
}

View File

@@ -1,27 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
use function sprintf;
use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;
final class InvalidCodeCoverageTargetException extends RuntimeException implements Exception
{
public function __construct(Target $target)
{
parent::__construct(
sprintf(
'%s is not a valid target for code coverage',
$target->description(),
),
);
}
}

View File

@@ -1,30 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Driver;
use function sprintf;
use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;
final class XdebugVersionNotSupportedException extends RuntimeException implements Exception
{
/**
* @param non-empty-string $version
*/
public function __construct(string $version)
{
parent::__construct(
sprintf(
'Version %s of the Xdebug extension is not supported',
$version,
),
);
}
}

View File

@@ -15,24 +15,20 @@ use function str_ends_with;
use function str_replace;
use function substr;
use Countable;
use SebastianBergmann\CodeCoverage\StaticAnalysis\LinesOfCode;
use SebastianBergmann\CodeCoverage\Util\Percentage;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type ProcessedFunctionType from File
* @phpstan-import-type ProcessedClassType from File
* @phpstan-import-type ProcessedTraitType from File
* @phpstan-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
* @phpstan-import-type ProcessedFunctionType from \SebastianBergmann\CodeCoverage\Node\File
* @phpstan-import-type ProcessedClassType from \SebastianBergmann\CodeCoverage\Node\File
* @phpstan-import-type ProcessedTraitType from \SebastianBergmann\CodeCoverage\Node\File
*/
abstract class AbstractNode implements Countable
{
private readonly string $name;
private string $pathAsString;
/**
* @var non-empty-list<self>
*/
private array $pathAsArray;
private readonly ?AbstractNode $parent;
private string $id;
@@ -65,9 +61,6 @@ abstract class AbstractNode implements Countable
return $this->pathAsString;
}
/**
* @return non-empty-list<self>
*/
public function pathAsArray(): array
{
return $this->pathAsArray;
@@ -193,7 +186,10 @@ abstract class AbstractNode implements Countable
*/
abstract public function functions(): array;
abstract public function linesOfCode(): LinesOfCode;
/**
* @return LinesOfCodeType
*/
abstract public function linesOfCode(): array;
abstract public function numberOfExecutableLines(): int;

View File

@@ -28,11 +28,11 @@ use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type TestType from CodeCoverage
* @phpstan-import-type TestType from \SebastianBergmann\CodeCoverage\CodeCoverage
*/
final readonly class Builder
final class Builder
{
private FileAnalyser $analyser;
private readonly FileAnalyser $analyser;
public function __construct(FileAnalyser $analyser)
{
@@ -223,7 +223,6 @@ final readonly class Builder
$paths[$i] = substr($paths[$i], 7);
$paths[$i] = str_replace('/', DIRECTORY_SEPARATOR, $paths[$i]);
}
$paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);
if (empty($paths[$i][0])) {

View File

@@ -14,10 +14,10 @@ use function sprintf;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class CrapIndex
final class CrapIndex
{
private int $cyclomaticComplexity;
private float $codeCoverage;
private readonly int $cyclomaticComplexity;
private readonly float $codeCoverage;
public function __construct(int $cyclomaticComplexity, float $codeCoverage)
{

View File

@@ -14,16 +14,11 @@ use function assert;
use function count;
use IteratorAggregate;
use RecursiveIteratorIterator;
use SebastianBergmann\CodeCoverage\StaticAnalysis\LinesOfCode;
/**
* @template-implements IteratorAggregate<int, AbstractNode>
*
* @phpstan-import-type ProcessedFunctionType from File
* @phpstan-import-type ProcessedClassType from File
* @phpstan-import-type ProcessedTraitType from File
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
*/
final class Directory extends AbstractNode implements IteratorAggregate
{
@@ -40,23 +35,15 @@ final class Directory extends AbstractNode implements IteratorAggregate
/**
* @var list<File>
*/
private array $files = [];
private array $files = [];
private ?array $classes = null;
private ?array $traits = null;
private ?array $functions = null;
/**
* @var ?array<string, ProcessedClassType>
* @var null|LinesOfCodeType
*/
private ?array $classes = null;
/**
* @var ?array<string, ProcessedTraitType>
*/
private ?array $traits = null;
/**
* @var ?array<string, ProcessedFunctionType>
*/
private ?array $functions = null;
private ?LinesOfCode $linesOfCode = null;
private ?array $linesOfCode = null;
private int $numFiles = -1;
private int $numExecutableLines = -1;
private int $numExecutedLines = -1;
@@ -86,9 +73,6 @@ final class Directory extends AbstractNode implements IteratorAggregate
return $this->numFiles;
}
/**
* @return RecursiveIteratorIterator<Iterator<AbstractNode>>
*/
public function getIterator(): RecursiveIteratorIterator
{
return new RecursiveIteratorIterator(
@@ -118,33 +102,21 @@ final class Directory extends AbstractNode implements IteratorAggregate
$this->numExecutedLines = -1;
}
/**
* @return list<Directory>
*/
public function directories(): array
{
return $this->directories;
}
/**
* @return list<File>
*/
public function files(): array
{
return $this->files;
}
/**
* @return list<Directory|File>
*/
public function children(): array
{
return $this->children;
}
/**
* @return array<string, ProcessedClassType>
*/
public function classes(): array
{
if ($this->classes === null) {
@@ -161,9 +133,6 @@ final class Directory extends AbstractNode implements IteratorAggregate
return $this->classes;
}
/**
* @return array<string, ProcessedTraitType>
*/
public function traits(): array
{
if ($this->traits === null) {
@@ -180,9 +149,6 @@ final class Directory extends AbstractNode implements IteratorAggregate
return $this->traits;
}
/**
* @return array<string, ProcessedFunctionType>
*/
public function functions(): array
{
if ($this->functions === null) {
@@ -199,22 +165,25 @@ final class Directory extends AbstractNode implements IteratorAggregate
return $this->functions;
}
public function linesOfCode(): LinesOfCode
/**
* @return LinesOfCodeType
*/
public function linesOfCode(): array
{
if ($this->linesOfCode === null) {
$linesOfCode = 0;
$commentLinesOfCode = 0;
$nonCommentLinesOfCode = 0;
$this->linesOfCode = [
'linesOfCode' => 0,
'commentLinesOfCode' => 0,
'nonCommentLinesOfCode' => 0,
];
foreach ($this->children as $child) {
$childLinesOfCode = $child->linesOfCode();
$linesOfCode += $childLinesOfCode->linesOfCode();
$commentLinesOfCode += $childLinesOfCode->commentLinesOfCode();
$nonCommentLinesOfCode += $childLinesOfCode->nonCommentLinesOfCode();
$this->linesOfCode['linesOfCode'] += $childLinesOfCode['linesOfCode'];
$this->linesOfCode['commentLinesOfCode'] += $childLinesOfCode['commentLinesOfCode'];
$this->linesOfCode['nonCommentLinesOfCode'] += $childLinesOfCode['nonCommentLinesOfCode'];
}
$this->linesOfCode = new LinesOfCode($linesOfCode, $commentLinesOfCode, $nonCommentLinesOfCode);
}
return $this->linesOfCode;

View File

@@ -12,21 +12,18 @@ namespace SebastianBergmann\CodeCoverage\Node;
use function array_filter;
use function count;
use function range;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\StaticAnalysis\Class_;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\Function_;
use SebastianBergmann\CodeCoverage\StaticAnalysis\LinesOfCode;
use SebastianBergmann\CodeCoverage\StaticAnalysis\Method;
use SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type TestType from CodeCoverage
* @phpstan-import-type LinesType from FileAnalyser
* @phpstan-import-type CodeUnitFunctionType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitMethodType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitClassType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitTraitType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
* @phpstan-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
*
* @phpstan-type ProcessedFunctionType array{
* @phpstan-type ProcessedFunctionType = array{
* functionName: string,
* namespace: string,
* signature: string,
@@ -43,7 +40,7 @@ use SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_;
* crap: int|string,
* link: string
* }
* @phpstan-type ProcessedMethodType array{
* @phpstan-type ProcessedMethodType = array{
* methodName: string,
* visibility: string,
* signature: string,
@@ -60,7 +57,7 @@ use SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_;
* crap: int|string,
* link: string
* }
* @phpstan-type ProcessedClassType array{
* @phpstan-type ProcessedClassType = array{
* className: string,
* namespace: string,
* methods: array<string, ProcessedMethodType>,
@@ -76,7 +73,7 @@ use SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_;
* crap: int|string,
* link: string
* }
* @phpstan-type ProcessedTraitType array{
* @phpstan-type ProcessedTraitType = array{
* traitName: string,
* namespace: string,
* methods: array<string, ProcessedMethodType>,
@@ -100,10 +97,6 @@ final class File extends AbstractNode
*/
private array $lineCoverageData;
private array $functionCoverageData;
/**
* @var array<string, TestType>
*/
private readonly array $testData;
private int $numExecutableLines = 0;
private int $numExecutedLines = 0;
@@ -126,7 +119,11 @@ final class File extends AbstractNode
* @var array<string, ProcessedFunctionType>
*/
private array $functions = [];
private readonly LinesOfCode $linesOfCode;
/**
* @var LinesOfCodeType
*/
private readonly array $linesOfCode;
private ?int $numClasses = null;
private int $numTestedClasses = 0;
private ?int $numTraits = null;
@@ -136,18 +133,18 @@ final class File extends AbstractNode
private ?int $numTestedFunctions = null;
/**
* @var array<int, array|array{0: Class_, 1: string}|array{0: Function_}|array{0: Trait_, 1: string}>
* @var array<int, array|array{0: CodeUnitClassType, 1: string}|array{0: CodeUnitFunctionType}|array{0: CodeUnitTraitType, 1: string}>
*/
private array $codeUnitsByLine = [];
/**
* @param array<int, ?list<non-empty-string>> $lineCoverageData
* @param array<string, TestType> $testData
* @param array<string, Class_> $classes
* @param array<string, Trait_> $traits
* @param array<string, Function_> $functions
* @param array<string, CodeUnitClassType> $classes
* @param array<string, CodeUnitTraitType> $traits
* @param array<string, CodeUnitFunctionType> $functions
* @param LinesOfCodeType $linesOfCode
*/
public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, LinesOfCode $linesOfCode)
public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, array $linesOfCode)
{
parent::__construct($name, $parent);
@@ -177,9 +174,6 @@ final class File extends AbstractNode
return $this->functionCoverageData;
}
/**
* @return array<string, TestType>
*/
public function testData(): array
{
return $this->testData;
@@ -209,7 +203,7 @@ final class File extends AbstractNode
return $this->functions;
}
public function linesOfCode(): LinesOfCode
public function linesOfCode(): array
{
return $this->linesOfCode;
}
@@ -366,13 +360,13 @@ final class File extends AbstractNode
}
/**
* @param array<string, Class_> $classes
* @param array<string, Trait_> $traits
* @param array<string, Function_> $functions
* @param array<string, CodeUnitClassType> $classes
* @param array<string, CodeUnitTraitType> $traits
* @param array<string, CodeUnitFunctionType> $functions
*/
private function calculateStatistics(array $classes, array $traits, array $functions): void
{
foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) {
foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [];
}
@@ -380,7 +374,7 @@ final class File extends AbstractNode
$this->processTraits($traits);
$this->processFunctions($functions);
foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) {
foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
if (isset($this->lineCoverageData[$lineNumber])) {
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
$codeUnit['executableLines']++;
@@ -473,7 +467,7 @@ final class File extends AbstractNode
}
/**
* @param array<string, Class_> $classes
* @param array<string, CodeUnitClassType> $classes
*/
private function processClasses(array $classes): void
{
@@ -482,9 +476,9 @@ final class File extends AbstractNode
foreach ($classes as $className => $class) {
$this->classes[$className] = [
'className' => $className,
'namespace' => $class->namespace(),
'namespace' => $class['namespace'],
'methods' => [],
'startLine' => $class->startLine(),
'startLine' => $class['startLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
@@ -494,11 +488,11 @@ final class File extends AbstractNode
'ccn' => 0,
'coverage' => 0,
'crap' => 0,
'link' => $link . $class->startLine(),
'link' => $link . $class['startLine'],
];
foreach ($class->methods() as $methodName => $method) {
$methodData = $this->newMethod($className, $method, $link);
foreach ($class['methods'] as $methodName => $method) {
$methodData = $this->newMethod($className, $methodName, $method, $link);
$this->classes[$className]['methods'][$methodName] = $methodData;
$this->classes[$className]['executableBranches'] += $methodData['executableBranches'];
@@ -511,7 +505,7 @@ final class File extends AbstractNode
$this->numExecutablePaths += $methodData['executablePaths'];
$this->numExecutedPaths += $methodData['executedPaths'];
foreach (range($method->startLine(), $method->endLine()) as $lineNumber) {
foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [
&$this->classes[$className],
&$this->classes[$className]['methods'][$methodName],
@@ -522,7 +516,7 @@ final class File extends AbstractNode
}
/**
* @param array<string, Trait_> $traits
* @param array<string, CodeUnitTraitType> $traits
*/
private function processTraits(array $traits): void
{
@@ -531,9 +525,9 @@ final class File extends AbstractNode
foreach ($traits as $traitName => $trait) {
$this->traits[$traitName] = [
'traitName' => $traitName,
'namespace' => $trait->namespace(),
'namespace' => $trait['namespace'],
'methods' => [],
'startLine' => $trait->startLine(),
'startLine' => $trait['startLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
@@ -543,11 +537,11 @@ final class File extends AbstractNode
'ccn' => 0,
'coverage' => 0,
'crap' => 0,
'link' => $link . $trait->startLine(),
'link' => $link . $trait['startLine'],
];
foreach ($trait->methods() as $methodName => $method) {
$methodData = $this->newMethod($traitName, $method, $link);
foreach ($trait['methods'] as $methodName => $method) {
$methodData = $this->newMethod($traitName, $methodName, $method, $link);
$this->traits[$traitName]['methods'][$methodName] = $methodData;
$this->traits[$traitName]['executableBranches'] += $methodData['executableBranches'];
@@ -560,7 +554,7 @@ final class File extends AbstractNode
$this->numExecutablePaths += $methodData['executablePaths'];
$this->numExecutedPaths += $methodData['executedPaths'];
foreach (range($method->startLine(), $method->endLine()) as $lineNumber) {
foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [
&$this->traits[$traitName],
&$this->traits[$traitName]['methods'][$methodName],
@@ -571,7 +565,7 @@ final class File extends AbstractNode
}
/**
* @param array<string, Function_> $functions
* @param array<string, CodeUnitFunctionType> $functions
*/
private function processFunctions(array $functions): void
{
@@ -580,23 +574,23 @@ final class File extends AbstractNode
foreach ($functions as $functionName => $function) {
$this->functions[$functionName] = [
'functionName' => $functionName,
'namespace' => $function->namespace(),
'signature' => $function->signature(),
'startLine' => $function->startLine(),
'endLine' => $function->endLine(),
'namespace' => $function['namespace'],
'signature' => $function['signature'],
'startLine' => $function['startLine'],
'endLine' => $function['endLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
'executedBranches' => 0,
'executablePaths' => 0,
'executedPaths' => 0,
'ccn' => $function->cyclomaticComplexity(),
'ccn' => $function['ccn'],
'coverage' => 0,
'crap' => 0,
'link' => $link . $function->startLine(),
'link' => $link . $function['startLine'],
];
foreach (range($function->startLine(), $function->endLine()) as $lineNumber) {
foreach (range($function['startLine'], $function['endLine']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]];
}
@@ -640,29 +634,31 @@ final class File extends AbstractNode
}
/**
* @param CodeUnitMethodType $method
*
* @return ProcessedMethodType
*/
private function newMethod(string $className, Method $method, string $link): array
private function newMethod(string $className, string $methodName, array $method, string $link): array
{
$methodData = [
'methodName' => $method->name(),
'visibility' => $method->visibility()->value,
'signature' => $method->signature(),
'startLine' => $method->startLine(),
'endLine' => $method->endLine(),
'methodName' => $methodName,
'visibility' => $method['visibility'],
'signature' => $method['signature'],
'startLine' => $method['startLine'],
'endLine' => $method['endLine'],
'executableLines' => 0,
'executedLines' => 0,
'executableBranches' => 0,
'executedBranches' => 0,
'executablePaths' => 0,
'executedPaths' => 0,
'ccn' => $method->cyclomaticComplexity(),
'ccn' => $method['ccn'],
'coverage' => 0,
'crap' => 0,
'link' => $link . $method->startLine(),
'link' => $link . $method['startLine'],
];
$key = $className . '->' . $method->name();
$key = $className . '->' . $methodName;
if (isset($this->functionCoverageData[$key]['branches'])) {
$methodData['executableBranches'] = count(

View File

@@ -9,13 +9,10 @@
*/
namespace SebastianBergmann\CodeCoverage\Node;
use function assert;
use function count;
use RecursiveIterator;
/**
* @template-implements RecursiveIterator<int, AbstractNode>
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Iterator implements RecursiveIterator
@@ -32,38 +29,57 @@ final class Iterator implements RecursiveIterator
$this->nodes = $node->children();
}
/**
* Rewinds the Iterator to the first element.
*/
public function rewind(): void
{
$this->position = 0;
}
/**
* Checks if there is a current element after calls to rewind() or next().
*/
public function valid(): bool
{
return $this->position < count($this->nodes);
}
/**
* Returns the key of the current element.
*/
public function key(): int
{
return $this->position;
}
/**
* Returns the current element.
*/
public function current(): ?AbstractNode
{
return $this->valid() ? $this->nodes[$this->position] : null;
}
/**
* Moves forward to next element.
*/
public function next(): void
{
$this->position++;
}
/**
* Returns the sub iterator for the current element.
*/
public function getChildren(): self
{
assert($this->nodes[$this->position] instanceof Directory);
return new self($this->nodes[$this->position]);
}
/**
* Checks whether the current element has children.
*/
public function hasChildren(): bool
{
return $this->nodes[$this->position] instanceof Directory;

View File

@@ -168,8 +168,8 @@ final class Clover
$linesOfCode = $item->linesOfCode();
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode());
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode());
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['linesOfCode']);
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['nonCommentLinesOfCode']);
$xmlMetrics->setAttribute('classes', (string) $item->numberOfClassesAndTraits());
$xmlMetrics->setAttribute('methods', (string) $item->numberOfMethods());
$xmlMetrics->setAttribute('coveredmethods', (string) $item->numberOfTestedMethods());
@@ -201,8 +201,8 @@ final class Clover
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('files', (string) count($report));
$xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode());
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode());
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['linesOfCode']);
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['nonCommentLinesOfCode']);
$xmlMetrics->setAttribute('classes', (string) $report->numberOfClassesAndTraits());
$xmlMetrics->setAttribute('methods', (string) $report->numberOfMethods());
$xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods());

View File

@@ -153,7 +153,7 @@ final class Cobertura
$linesValid = $method['executableLines'];
$linesCovered = $method['executedLines'];
$lineRate = $linesCovered / $linesValid;
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
$branchesValid = $method['executableBranches'];
$branchesCovered = $method['executedBranches'];
@@ -228,7 +228,7 @@ final class Cobertura
$linesValid = $function['executableLines'];
$linesCovered = $function['executedLines'];
$lineRate = $linesCovered / $linesValid;
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
$functionsLinesValid += $linesValid;
$functionsLinesCovered += $linesCovered;

View File

@@ -22,9 +22,9 @@ use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
final readonly class Crap4j
final class Crap4j
{
private int $threshold;
private readonly int $threshold;
public function __construct(int $threshold = 30)
{

View File

@@ -12,13 +12,13 @@ namespace SebastianBergmann\CodeCoverage\Report\Html;
/**
* @immutable
*/
final readonly class Colors
final class Colors
{
private string $successLow;
private string $successMedium;
private string $successHigh;
private string $warning;
private string $danger;
private readonly string $successLow;
private readonly string $successMedium;
private readonly string $successHigh;
private readonly string $warning;
private readonly string $danger;
public static function default(): self
{

View File

@@ -15,9 +15,9 @@ use SebastianBergmann\CodeCoverage\InvalidArgumentException;
/**
* @immutable
*/
final readonly class CustomCssFile
final class CustomCssFile
{
private string $path;
private readonly string $path;
public static function default(): self
{

View File

@@ -22,13 +22,13 @@ use SebastianBergmann\CodeCoverage\Util\Filesystem;
use SebastianBergmann\Template\Exception;
use SebastianBergmann\Template\Template;
final readonly class Facade
final class Facade
{
private string $templatePath;
private string $generator;
private Colors $colors;
private Thresholds $thresholds;
private CustomCssFile $customCssFile;
private readonly string $templatePath;
private readonly string $generator;
private readonly Colors $colors;
private readonly Thresholds $thresholds;
private readonly CustomCssFile $customCssFile;
public function __construct(string $generator = '', ?Colors $colors = null, ?Thresholds $thresholds = null, ?CustomCssFile $customCssFile = null)
{

View File

@@ -44,9 +44,6 @@ abstract class Renderer
$this->hasBranchCoverage = $hasBranchCoverage;
}
/**
* @param array<non-empty-string, float|int|string> $data
*/
protected function renderItemTemplate(Template $template, array $data): string
{
$numSeparator = '&nbsp;/&nbsp;';
@@ -174,8 +171,8 @@ abstract class Renderer
'version' => $this->version,
'runtime' => $this->runtimeString(),
'generator' => $this->generator,
'low_upper_bound' => (string) $this->thresholds->lowUpperBound(),
'high_lower_bound' => (string) $this->thresholds->highLowerBound(),
'low_upper_bound' => $this->thresholds->lowUpperBound(),
'high_lower_bound' => $this->thresholds->highLowerBound(),
],
);
}

View File

@@ -12,7 +12,6 @@ namespace SebastianBergmann\CodeCoverage\Report\Html;
use function array_values;
use function arsort;
use function asort;
use function assert;
use function count;
use function explode;
use function floor;
@@ -22,14 +21,10 @@ use function str_replace;
use SebastianBergmann\CodeCoverage\FileCouldNotBeWrittenException;
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\Template\Exception;
use SebastianBergmann\Template\Template;
/**
* @phpstan-import-type ProcessedClassType from FileNode
* @phpstan-import-type ProcessedTraitType from FileNode
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Dashboard extends Renderer
@@ -86,9 +81,7 @@ final class Dashboard extends Renderer
}
/**
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
*
* @return array{class: non-empty-string, method: non-empty-string}
* Returns the data for the Class/Method Complexity charts.
*/
private function complexity(array $classes, string $baseLink): array
{
@@ -122,21 +115,14 @@ final class Dashboard extends Renderer
];
}
$class = json_encode($result['class']);
assert($class !== false);
$method = json_encode($result['method']);
assert($method !== false);
return ['class' => $class, 'method' => $method];
return [
'class' => json_encode($result['class']),
'method' => json_encode($result['method']),
];
}
/**
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
*
* @return array{class: non-empty-string, method: non-empty-string}
* Returns the data for the Class / Method Coverage Distribution chart.
*/
private function coverageDistribution(array $classes): array
{
@@ -195,21 +181,14 @@ final class Dashboard extends Renderer
}
}
$class = json_encode(array_values($result['class']));
assert($class !== false);
$method = json_encode(array_values($result['method']));
assert($method !== false);
return ['class' => $class, 'method' => $method];
return [
'class' => json_encode(array_values($result['class'])),
'method' => json_encode(array_values($result['method'])),
];
}
/**
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
*
* @return array{class: string, method: string}
* Returns the classes / methods with insufficient coverage.
*/
private function insufficientCoverage(array $classes, string $baseLink): array
{
@@ -263,9 +242,7 @@ final class Dashboard extends Renderer
}
/**
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
*
* @return array{class: string, method: string}
* Returns the project risks according to the CRAP index.
*/
private function projectRisks(array $classes, string $baseLink): array
{

View File

@@ -108,11 +108,6 @@ use SebastianBergmann\Template\Exception;
use SebastianBergmann\Template\Template;
/**
* @phpstan-import-type ProcessedClassType from FileNode
* @phpstan-import-type ProcessedTraitType from FileNode
* @phpstan-import-type ProcessedMethodType from FileNode
* @phpstan-import-type ProcessedFunctionType from FileNode
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class File extends Renderer
@@ -120,7 +115,7 @@ final class File extends Renderer
/**
* @var array<int,true>
*/
private const array KEYWORD_TOKENS = [
private const KEYWORD_TOKENS = [
T_ABSTRACT => true,
T_ARRAY => true,
T_AS => true,
@@ -190,13 +185,8 @@ final class File extends Renderer
T_YIELD => true,
T_YIELD_FROM => true,
];
private const int HTML_SPECIAL_CHARS_FLAGS = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE;
/**
* @var array<non-empty-string, list<string>>
*/
private static array $formattedSourceCache = [];
private int $htmlSpecialCharsFlags = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE;
public function render(FileNode $node, string $file): void
{
@@ -324,9 +314,6 @@ final class File extends Renderer
return $items;
}
/**
* @param array<string, ProcessedClassType|ProcessedTraitType> $items
*/
private function renderTraitOrClassItems(array $items, Template $template, Template $methodItemTemplate): string
{
$buffer = '';
@@ -431,9 +418,6 @@ final class File extends Renderer
return $buffer;
}
/**
* @param array<string, ProcessedFunctionType> $functions
*/
private function renderFunctionItems(array $functions, Template $template): string
{
if (empty($functions)) {
@@ -452,9 +436,6 @@ final class File extends Renderer
return $buffer;
}
/**
* @param ProcessedFunctionType|ProcessedMethodType $item
*/
private function renderFunctionOrMethodItem(Template $template, array $item, string $indent = ''): string
{
$numMethods = 0;
@@ -495,7 +476,7 @@ final class File extends Renderer
'%s<a href="#%d"><abbr title="%s">%s</abbr></a>',
$indent,
$item['startLine'],
htmlspecialchars($item['signature'], self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags),
$item['functionName'] ?? $item['methodName'],
),
'numMethods' => $numMethods,
@@ -573,7 +554,7 @@ final class File extends Renderer
$popover = sprintf(
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
);
}
@@ -660,7 +641,7 @@ final class File extends Renderer
$popover = sprintf(
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
);
}
@@ -750,7 +731,7 @@ final class File extends Renderer
$popover = sprintf(
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
);
}
@@ -787,7 +768,7 @@ final class File extends Renderer
}
if ($branchStructure !== '') { // don't show empty branches
$branches .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
$branches .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, $this->htmlSpecialCharsFlags) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
$branches .= $branchStructure;
}
}
@@ -797,9 +778,6 @@ final class File extends Renderer
return $branchesTemplate->render();
}
/**
* @param list<string> $codeLines
*/
private function renderBranchLines(array $branch, array $codeLines, array $testData): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
@@ -851,7 +829,7 @@ final class File extends Renderer
$popover = sprintf(
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
);
}
@@ -896,7 +874,7 @@ final class File extends Renderer
}
if ($pathStructure !== '') {
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, $this->htmlSpecialCharsFlags) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
$paths .= $pathStructure;
}
}
@@ -906,9 +884,6 @@ final class File extends Renderer
return $pathsTemplate->render();
}
/**
* @param list<string> $codeLines
*/
private function renderPathLines(array $path, array $branches, array $codeLines, array $testData): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
@@ -969,7 +944,7 @@ final class File extends Renderer
$popover = sprintf(
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
);
}
@@ -990,7 +965,7 @@ final class File extends Renderer
{
$template->setVar(
[
'lineNumber' => (string) $lineNumber,
'lineNumber' => $lineNumber,
'lineContent' => $lineContent,
'class' => $class,
'popover' => $popover,
@@ -1000,11 +975,6 @@ final class File extends Renderer
return $template->render();
}
/**
* @param non-empty-string $file
*
* @return list<string>
*/
private function loadFile(string $file): array
{
if (isset(self::$formattedSourceCache[$file])) {
@@ -1025,14 +995,14 @@ final class File extends Renderer
if ($token === '"' && $tokens[$j - 1] !== '\\') {
$result[$i] .= sprintf(
'<span class="string">%s</span>',
htmlspecialchars($token, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($token, $this->htmlSpecialCharsFlags),
);
$stringFlag = !$stringFlag;
} else {
$result[$i] .= sprintf(
'<span class="keyword">%s</span>',
htmlspecialchars($token, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($token, $this->htmlSpecialCharsFlags),
);
}
@@ -1044,7 +1014,7 @@ final class File extends Renderer
$value = str_replace(
["\t", ' '],
['&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'],
htmlspecialchars($value, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($value, $this->htmlSpecialCharsFlags),
);
if ($value === "\n") {
@@ -1143,7 +1113,7 @@ final class File extends Renderer
return sprintf(
'<li%s>%s</li>',
$testCSS,
htmlspecialchars($test, self::HTML_SPECIAL_CHARS_FLAGS),
htmlspecialchars($test, $this->htmlSpecialCharsFlags),
);
}

View File

@@ -26,27 +26,27 @@ final class Text
/**
* @var string
*/
private const string COLOR_GREEN = "\x1b[30;42m";
private const COLOR_GREEN = "\x1b[30;42m";
/**
* @var string
*/
private const string COLOR_YELLOW = "\x1b[30;43m";
private const COLOR_YELLOW = "\x1b[30;43m";
/**
* @var string
*/
private const string COLOR_RED = "\x1b[37;41m";
private const COLOR_RED = "\x1b[37;41m";
/**
* @var string
*/
private const string COLOR_HEADER = "\x1b[1;37;40m";
private const COLOR_HEADER = "\x1b[1;37;40m";
/**
* @var string
*/
private const string COLOR_RESET = "\x1b[0m";
private const COLOR_RESET = "\x1b[0m";
private readonly Thresholds $thresholds;
private readonly bool $showUncoveredFiles;
private readonly bool $showOnlySummary;

View File

@@ -14,10 +14,10 @@ use SebastianBergmann\CodeCoverage\InvalidArgumentException;
/**
* @immutable
*/
final readonly class Thresholds
final class Thresholds
{
private int $lowUpperBound;
private int $highLowerBound;
private readonly int $lowUpperBound;
private readonly int $highLowerBound;
public static function default(): self
{

View File

@@ -18,9 +18,9 @@ use SebastianBergmann\Environment\Runtime;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class BuildInformation
final class BuildInformation
{
private DOMElement $contextNode;
private readonly DOMElement $contextNode;
public function __construct(DOMElement $contextNode)
{

View File

@@ -32,19 +32,12 @@ use SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Util\Filesystem as DirectoryUtil;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\CodeCoverage\XmlException;
use SebastianBergmann\Environment\Runtime;
/**
* @phpstan-import-type ProcessedClassType from File
* @phpstan-import-type ProcessedTraitType from File
* @phpstan-import-type ProcessedFunctionType from File
* @phpstan-import-type TestType from CodeCoverage
*/
final class Facade
{
private string $target;
@@ -182,9 +175,6 @@ final class Facade
$this->saveDocument($fileReport->asDom(), $file->id());
}
/**
* @param ProcessedClassType|ProcessedTraitType $unit
*/
private function processUnit(array $unit, Report $report): void
{
if (isset($unit['className'])) {
@@ -215,9 +205,6 @@ final class Facade
}
}
/**
* @param ProcessedFunctionType $function
*/
private function processFunction(array $function, Report $report): void
{
$functionObject = $report->functionObject($function['functionName']);
@@ -228,9 +215,6 @@ final class Facade
$functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']);
}
/**
* @param array<string, TestType> $tests
*/
private function processTests(array $tests): void
{
$testsObject = $this->project->tests();
@@ -245,9 +229,9 @@ final class Facade
$loc = $node->linesOfCode();
$totals->setNumLines(
$loc->linesOfCode(),
$loc->commentLinesOfCode(),
$loc->nonCommentLinesOfCode(),
$loc['linesOfCode'],
$loc['commentLinesOfCode'],
$loc['nonCommentLinesOfCode'],
$node->numberOfExecutableLines(),
$node->numberOfExecutedLines(),
);

View File

@@ -9,7 +9,6 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use DOMDocument;
use DOMElement;
@@ -40,8 +39,6 @@ class File
);
}
assert($totalsContainer instanceof DOMElement);
return new Totals($totalsContainer);
}
@@ -68,8 +65,6 @@ class File
),
);
assert($lineNode instanceof DOMElement);
return new Coverage($lineNode, $line);
}

View File

@@ -14,9 +14,9 @@ use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Method
final class Method
{
private DOMElement $contextNode;
private readonly DOMElement $contextNode;
public function __construct(DOMElement $context, string $name)
{

View File

@@ -9,7 +9,6 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use DOMDocument;
use DOMElement;
@@ -44,8 +43,6 @@ abstract class Node
);
}
assert($totalsContainer instanceof DOMElement);
return new Totals($totalsContainer);
}

View File

@@ -9,9 +9,7 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use DOMDocument;
use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -45,8 +43,6 @@ final class Project extends Node
);
}
assert($buildNode instanceof DOMElement);
return new BuildInformation($buildNode);
}
@@ -66,8 +62,6 @@ final class Project extends Node
);
}
assert($testsNode instanceof DOMElement);
return new Tests($testsNode);
}

View File

@@ -9,11 +9,9 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use function basename;
use function dirname;
use DOMDocument;
use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -40,7 +38,7 @@ final class Report extends File
return $this->dom();
}
public function functionObject(string $name): Method
public function functionObject($name): Method
{
$node = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
@@ -49,17 +47,15 @@ final class Report extends File
),
);
assert($node instanceof DOMElement);
return new Method($node, $name);
}
public function classObject(string $name): Unit
public function classObject($name): Unit
{
return $this->unitObject('class', $name);
}
public function traitObject(string $name): Unit
public function traitObject($name): Unit
{
return $this->unitObject('trait', $name);
}
@@ -80,8 +76,6 @@ final class Report extends File
);
}
assert($source instanceof DOMElement);
return new Source($source);
}
@@ -91,7 +85,7 @@ final class Report extends File
$this->contextNode()->setAttribute('path', dirname($name));
}
private function unitObject(string $tagName, string $name): Unit
private function unitObject(string $tagName, $name): Unit
{
$node = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
@@ -100,8 +94,6 @@ final class Report extends File
),
);
assert($node instanceof DOMElement);
return new Unit($node, $name);
}
}

View File

@@ -17,9 +17,9 @@ use TheSeer\Tokenizer\XMLSerializer;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Source
final class Source
{
private DOMElement $context;
private readonly DOMElement $context;
public function __construct(DOMElement $context)
{

View File

@@ -11,16 +11,15 @@ namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use DOMElement;
use SebastianBergmann\CodeCoverage\CodeCoverage;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type TestType from CodeCoverage
* @phpstan-import-type TestType from \SebastianBergmann\CodeCoverage\CodeCoverage
*/
final readonly class Tests
final class Tests
{
private DOMElement $contextNode;
private readonly DOMElement $contextNode;
public function __construct(DOMElement $context)
{

View File

@@ -17,14 +17,14 @@ use SebastianBergmann\CodeCoverage\Util\Percentage;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Totals
final class Totals
{
private DOMNode $container;
private DOMElement $linesNode;
private DOMElement $methodsNode;
private DOMElement $functionsNode;
private DOMElement $classesNode;
private DOMElement $traitsNode;
private readonly DOMNode $container;
private readonly DOMElement $linesNode;
private readonly DOMElement $methodsNode;
private readonly DOMElement $functionsNode;
private readonly DOMElement $classesNode;
private readonly DOMElement $traitsNode;
public function __construct(DOMElement $container)
{

View File

@@ -15,9 +15,9 @@ use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Unit
final class Unit
{
private DOMElement $contextNode;
private readonly DOMElement $contextNode;
public function __construct(DOMElement $context, string $name)
{
@@ -68,8 +68,6 @@ final readonly class Unit
),
);
assert($node instanceof DOMElement);
return new Method($node, $name);
}

View File

@@ -23,17 +23,12 @@ use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-type CachedDataForFile array{
* interfacesIn: array<string, Interface_>,
* classesIn: array<string, Class_>,
* traitsIn: array<string, Trait_>,
* functionsIn: array<string, Function_>,
* linesOfCodeFor: LinesOfCode,
* ignoredLinesFor: LinesType,
* executableLinesIn: LinesType
* }
*
* @phpstan-import-type LinesType from FileAnalyser
* @phpstan-import-type CodeUnitFunctionType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitMethodType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitClassType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitTraitType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
* @phpstan-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
*/
final class CachingFileAnalyser implements FileAnalyser
{
@@ -42,10 +37,6 @@ final class CachingFileAnalyser implements FileAnalyser
private readonly FileAnalyser $analyser;
private readonly bool $useAnnotationsForIgnoringCode;
private readonly bool $ignoreDeprecatedCode;
/**
* @var array<non-empty-string, CachedDataForFile>
*/
private array $cache = [];
public function __construct(string $directory, FileAnalyser $analyser, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode)
@@ -59,19 +50,7 @@ final class CachingFileAnalyser implements FileAnalyser
}
/**
* @return array<string, Interface_>
*/
public function interfacesIn(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}
return $this->cache[$filename]['interfacesIn'];
}
/**
* @return array<string, Class_>
* @return array<string, CodeUnitClassType>
*/
public function classesIn(string $filename): array
{
@@ -83,7 +62,7 @@ final class CachingFileAnalyser implements FileAnalyser
}
/**
* @return array<string, Trait_>
* @return array<string, CodeUnitTraitType>
*/
public function traitsIn(string $filename): array
{
@@ -95,7 +74,7 @@ final class CachingFileAnalyser implements FileAnalyser
}
/**
* @return array<string, Function_>
* @return array<string, CodeUnitFunctionType>
*/
public function functionsIn(string $filename): array
{
@@ -106,7 +85,10 @@ final class CachingFileAnalyser implements FileAnalyser
return $this->cache[$filename]['functionsIn'];
}
public function linesOfCodeFor(string $filename): LinesOfCode
/**
* @return LinesOfCodeType
*/
public function linesOfCodeFor(string $filename): array
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
@@ -150,7 +132,6 @@ final class CachingFileAnalyser implements FileAnalyser
}
$this->cache[$filename] = [
'interfacesIn' => $this->analyser->interfacesIn($filename),
'classesIn' => $this->analyser->classesIn($filename),
'traitsIn' => $this->analyser->traitsIn($filename),
'functionsIn' => $this->analyser->functionsIn($filename),
@@ -162,9 +143,6 @@ final class CachingFileAnalyser implements FileAnalyser
$this->write($filename, $this->cache[$filename]);
}
/**
* @return CachedDataForFile|false
*/
private function read(string $filename): array|false
{
$cacheFile = $this->cacheFile($filename);
@@ -175,22 +153,10 @@ final class CachingFileAnalyser implements FileAnalyser
return unserialize(
file_get_contents($cacheFile),
[
'allowed_classes' => [
Class_::class,
Function_::class,
Interface_::class,
LinesOfCode::class,
Method::class,
Trait_::class,
],
],
['allowed_classes' => false],
);
}
/**
* @param CachedDataForFile $data
*/
private function write(string $filename, array $data): void
{
file_put_contents(

View File

@@ -21,6 +21,7 @@ use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
@@ -31,51 +32,63 @@ use SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-type CodeUnitFunctionType = array{
* name: string,
* namespacedName: string,
* namespace: string,
* signature: string,
* startLine: int,
* endLine: int,
* ccn: int
* }
* @phpstan-type CodeUnitMethodType = array{
* methodName: string,
* signature: string,
* visibility: string,
* startLine: int,
* endLine: int,
* ccn: int
* }
* @phpstan-type CodeUnitClassType = array{
* name: string,
* namespacedName: string,
* namespace: string,
* startLine: int,
* endLine: int,
* methods: array<string, CodeUnitMethodType>
* }
* @phpstan-type CodeUnitTraitType = array{
* name: string,
* namespacedName: string,
* namespace: string,
* startLine: int,
* endLine: int,
* methods: array<string, CodeUnitMethodType>
* }
*/
final class CodeUnitFindingVisitor extends NodeVisitorAbstract
{
/**
* @var non-empty-string
*/
private string $file;
/**
* @var array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Interface_>
*/
private array $interfaces = [];
/**
* @var array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_>
* @var array<string, CodeUnitClassType>
*/
private array $classes = [];
/**
* @var array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_>
* @var array<string, CodeUnitTraitType>
*/
private array $traits = [];
/**
* @var array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Function_>
* @var array<string, CodeUnitFunctionType>
*/
private array $functions = [];
/**
* @param non-empty-string $file
*/
public function __construct(string $file)
public function enterNode(Node $node): void
{
$this->file = $file;
}
public function enterNode(Node $node): null
{
if ($node instanceof Interface_) {
$this->processInterface($node);
}
if ($node instanceof Class_) {
if ($node->isAnonymous()) {
return null;
return;
}
$this->processClass($node);
@@ -85,52 +98,27 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
$this->processTrait($node);
}
if (!$node instanceof Function_) {
return null;
if (!$node instanceof ClassMethod && !$node instanceof Function_) {
return;
}
if ($node instanceof ClassMethod) {
$parentNode = $node->getAttribute('parent');
if ($parentNode instanceof Class_ && $parentNode->isAnonymous()) {
return;
}
$this->processMethod($node);
return;
}
$this->processFunction($node);
return null;
}
public function leaveNode(Node $node): null
{
if ($node instanceof Class_ && $node->isAnonymous()) {
return null;
}
if (!$node instanceof Class_ && !$node instanceof Trait_) {
return null;
}
$traits = [];
foreach ($node->getTraitUses() as $traitUse) {
foreach ($traitUse->traits as $trait) {
$traits[] = $trait->toString();
}
}
if (empty($traits)) {
return null;
}
$this->postProcessClassOrTrait($node, $traits);
return null;
}
/**
* @return array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Interface_>
*/
public function interfaces(): array
{
return $this->interfaces;
}
/**
* @return array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_>
* @return array<string, CodeUnitClassType>
*/
public function classes(): array
{
@@ -138,7 +126,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
}
/**
* @return array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_>
* @return array<string, CodeUnitTraitType>
*/
public function traits(): array
{
@@ -146,7 +134,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
}
/**
* @return array<string, \SebastianBergmann\CodeCoverage\StaticAnalysis\Function_>
* @return array<string, CodeUnitFunctionType>
*/
public function functions(): array
{
@@ -222,66 +210,32 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
return $type->toString();
}
private function visibility(ClassMethod $node): Visibility
private function visibility(ClassMethod $node): string
{
if ($node->isPrivate()) {
return Visibility::Private;
return 'private';
}
if ($node->isProtected()) {
return Visibility::Protected;
return 'protected';
}
return Visibility::Public;
}
private function processInterface(Interface_ $node): void
{
$name = $node->name->toString();
$namespacedName = $node->namespacedName->toString();
$parentInterfaces = [];
foreach ($node->extends as $parentInterface) {
$parentInterfaces[] = $parentInterface->toString();
}
$this->interfaces[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Interface_(
$name,
$namespacedName,
$this->namespace($namespacedName, $name),
$node->getStartLine(),
$node->getEndLine(),
$parentInterfaces,
);
return 'public';
}
private function processClass(Class_ $node): void
{
$name = $node->name->toString();
$namespacedName = $node->namespacedName->toString();
$parentClass = null;
$interfaces = [];
if ($node->extends instanceof Name) {
$parentClass = $node->extends->toString();
}
foreach ($node->implements as $interface) {
$interfaces[] = $interface->toString();
}
$this->classes[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_(
$name,
$namespacedName,
$this->namespace($namespacedName, $name),
$this->file,
$node->getStartLine(),
$node->getEndLine(),
$parentClass,
$interfaces,
[],
$this->processMethods($node->getMethods()),
);
$this->classes[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'methods' => [],
];
}
private function processTrait(Trait_ $node): void
@@ -289,39 +243,57 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
$name = $node->name->toString();
$namespacedName = $node->namespacedName->toString();
$this->traits[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_(
$name,
$namespacedName,
$this->namespace($namespacedName, $name),
$this->file,
$node->getStartLine(),
$node->getEndLine(),
[],
$this->processMethods($node->getMethods()),
);
$this->traits[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'methods' => [],
];
}
/**
* @param list<ClassMethod> $nodes
*
* @return array<non-empty-string, Method>
*/
private function processMethods(array $nodes): array
private function processMethod(ClassMethod $node): void
{
$methods = [];
$parentNode = $node->getAttribute('parent');
foreach ($nodes as $node) {
$methods[$node->name->toString()] = new Method(
$node->name->toString(),
$node->getStartLine(),
$node->getEndLine(),
$this->signature($node),
$this->visibility($node),
$this->cyclomaticComplexity($node),
);
if ($parentNode instanceof Interface_) {
return;
}
return $methods;
assert($parentNode instanceof Class_ || $parentNode instanceof Trait_ || $parentNode instanceof Enum_);
assert(isset($parentNode->name));
assert(isset($parentNode->namespacedName));
assert($parentNode->namespacedName instanceof Name);
$parentName = $parentNode->name->toString();
$parentNamespacedName = $parentNode->namespacedName->toString();
if ($parentNode instanceof Class_) {
$storage = &$this->classes;
} else {
$storage = &$this->traits;
}
if (!isset($storage[$parentNamespacedName])) {
$storage[$parentNamespacedName] = [
'name' => $parentName,
'namespacedName' => $parentNamespacedName,
'namespace' => $this->namespace($parentNamespacedName, $parentName),
'startLine' => $parentNode->getStartLine(),
'endLine' => $parentNode->getEndLine(),
'methods' => [],
];
}
$storage[$parentNamespacedName]['methods'][$node->name->toString()] = [
'methodName' => $node->name->toString(),
'signature' => $this->signature($node),
'visibility' => $this->visibility($node),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'ccn' => $this->cyclomaticComplexity($node),
];
}
private function processFunction(Function_ $node): void
@@ -333,15 +305,15 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
$name = $node->name->toString();
$namespacedName = $node->namespacedName->toString();
$this->functions[$namespacedName] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Function_(
$name,
$namespacedName,
$this->namespace($namespacedName, $name),
$node->getStartLine(),
$node->getEndLine(),
$this->signature($node),
$this->cyclomaticComplexity($node),
);
$this->functions[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'signature' => $this->signature($node),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'ccn' => $this->cyclomaticComplexity($node),
];
}
private function namespace(string $namespacedName, string $name): string
@@ -385,44 +357,4 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
return $node->toString();
}
/**
* @param list<non-empty-string> $traits
*/
private function postProcessClassOrTrait(Class_|Trait_ $node, array $traits): void
{
$name = $node->namespacedName->toString();
if ($node instanceof Class_) {
assert(isset($this->classes[$name]));
$this->classes[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Class_(
$this->classes[$name]->name(),
$this->classes[$name]->namespacedName(),
$this->classes[$name]->namespace(),
$this->classes[$name]->file(),
$this->classes[$name]->startLine(),
$this->classes[$name]->endLine(),
$this->classes[$name]->parentClass(),
$this->classes[$name]->interfaces(),
$traits,
$this->classes[$name]->methods(),
);
return;
}
assert(isset($this->traits[$name]));
$this->traits[$name] = new \SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_(
$this->traits[$name]->name(),
$this->traits[$name]->namespacedName(),
$this->traits[$name]->namespace(),
$this->traits[$name]->file(),
$this->traits[$name]->startLine(),
$this->traits[$name]->endLine(),
$traits,
$this->traits[$name]->methods(),
);
}
}

View File

@@ -27,7 +27,7 @@ use PhpParser\NodeVisitorAbstract;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type LinesType from FileAnalyser
* @phpstan-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
*/
final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
{
@@ -54,7 +54,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$this->source = $source;
}
public function enterNode(Node $node): null
public function enterNode(Node $node): void
{
foreach ($node->getComments() as $comment) {
$commentLine = $comment->getStartLine();
@@ -80,7 +80,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
}
}
return null;
return;
}
if ($node instanceof Node\Stmt\Interface_) {
@@ -88,7 +88,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$this->unsets[$line] = true;
}
return null;
return;
}
if ($node instanceof Node\Stmt\Declare_ ||
@@ -113,7 +113,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$node instanceof Node\Name ||
$node instanceof Node\Param ||
$node instanceof Node\Scalar) {
return null;
return;
}
if ($node instanceof Node\Expr\Match_) {
@@ -125,13 +125,13 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
);
}
return null;
return;
}
if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Node\Expr\Throw_) {
$this->setLineBranch($node->expr->expr->getEndLine(), $node->expr->expr->getEndLine(), ++$this->nextBranch);
return null;
return;
}
if ($node instanceof Node\Stmt\Enum_ ||
@@ -176,7 +176,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
}
if ($isConcreteClassLike) {
return null;
return;
}
$hasEmptyBody = [] === $node->stmts ||
@@ -188,15 +188,15 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
if ($hasEmptyBody) {
if ($node->getEndLine() === $node->getStartLine() && isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
return null;
return;
}
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
return null;
return;
}
return null;
return;
}
if ($node instanceof Node\Expr\ArrowFunction) {
@@ -208,12 +208,12 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$endLine = $node->expr->getEndLine();
if ($endLine < $startLine) {
return null;
return;
}
$this->setLineBranch($startLine, $endLine, ++$this->nextBranch);
return null;
return;
}
if ($node instanceof Node\Expr\Ternary) {
@@ -226,7 +226,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch);
}
return null;
return;
}
if ($node instanceof Node\Expr\BinaryOp\Coalesce) {
@@ -234,14 +234,14 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
}
return null;
return;
}
if ($node instanceof Node\Stmt\If_ ||
$node instanceof Node\Stmt\ElseIf_ ||
$node instanceof Node\Stmt\Case_) {
if (null === $node->cond) {
return null;
return;
}
$this->setLineBranch(
@@ -250,7 +250,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
++$this->nextBranch,
);
return null;
return;
}
if ($node instanceof Node\Stmt\For_) {
@@ -292,7 +292,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
}
if (null === $startLine || null === $endLine) {
return null;
return;
}
$this->setLineBranch(
@@ -301,7 +301,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
++$this->nextBranch,
);
return null;
return;
}
if ($node instanceof Node\Stmt\Foreach_) {
@@ -311,7 +311,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
++$this->nextBranch,
);
return null;
return;
}
if ($node instanceof Node\Stmt\While_ ||
@@ -322,7 +322,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
++$this->nextBranch,
);
return null;
return;
}
if ($node instanceof Node\Stmt\Catch_) {
@@ -337,7 +337,7 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
++$this->nextBranch,
);
return null;
return;
}
if ($node instanceof Node\Expr\CallLike) {
@@ -349,19 +349,17 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch);
return null;
return;
}
if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
return null;
return;
}
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch);
return null;
}
public function afterTraverse(array $nodes): null
public function afterTraverse(array $nodes): void
{
$lines = explode("\n", $this->source);
@@ -381,8 +379,6 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
$this->executableLinesGroupedByBranch,
$this->unsets,
);
return null;
}
/**

View File

@@ -12,31 +12,39 @@ namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-type LinesType array<int, int>
* @phpstan-import-type CodeUnitFunctionType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitMethodType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitClassType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitTraitType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
*
* @phpstan-type LinesOfCodeType = array{
* linesOfCode: int,
* commentLinesOfCode: int,
* nonCommentLinesOfCode: int
* }
* @phpstan-type LinesType = array<int, int>
*/
interface FileAnalyser
{
/**
* @return array<string, Interface_>
*/
public function interfacesIn(string $filename): array;
/**
* @return array<string, Class_>
* @return array<string, CodeUnitClassType>
*/
public function classesIn(string $filename): array;
/**
* @return array<string, Trait_>
* @return array<string, CodeUnitTraitType>
*/
public function traitsIn(string $filename): array;
/**
* @return array<string, Function_>
* @return array<string, CodeUnitFunctionType>
*/
public function functionsIn(string $filename): array;
public function linesOfCodeFor(string $filename): LinesOfCode;
/**
* @return LinesOfCodeType
*/
public function linesOfCodeFor(string $filename): array;
/**
* @return LinesType

View File

@@ -39,7 +39,7 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
$this->ignoreDeprecated = $ignoreDeprecated;
}
public function enterNode(Node $node): null
public function enterNode(Node $node): void
{
if (!$node instanceof Class_ &&
!$node instanceof Trait_ &&
@@ -48,11 +48,11 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
!$node instanceof ClassMethod &&
!$node instanceof Function_ &&
!$node instanceof Attribute) {
return null;
return;
}
if ($node instanceof Class_ && $node->isAnonymous()) {
return null;
return;
}
if ($node instanceof Class_ ||
@@ -68,11 +68,11 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
}
if (!$this->useAnnotationsForIgnoringCode) {
return null;
return;
}
if ($node instanceof Interface_) {
return null;
return;
}
if ($node instanceof Attribute &&
@@ -84,12 +84,10 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
$this->ignoredLines[] = $line;
}
return null;
return;
}
$this->processDocComment($node);
return null;
}
/**

View File

@@ -34,32 +34,32 @@ use SebastianBergmann\LinesOfCode\LineCountingVisitor;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type LinesType from FileAnalyser
* @phpstan-import-type CodeUnitFunctionType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitMethodType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitClassType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type CodeUnitTraitType from \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor
* @phpstan-import-type LinesOfCodeType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
* @phpstan-import-type LinesType from \SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser
*/
final class ParsingFileAnalyser implements FileAnalyser
{
/**
* @var array<string, array<string, Interface_>>
*/
private array $interfaces = [];
/**
* @var array<string, array<string, Class_>>
* @var array<string, array<string, CodeUnitClassType>>
*/
private array $classes = [];
/**
* @var array<string, array<string, Trait_>>
* @var array<string, array<string, CodeUnitTraitType>>
*/
private array $traits = [];
/**
* @var array<string, array<string, Function_>>
* @var array<string, array<string, CodeUnitFunctionType>>
*/
private array $functions = [];
/**
* @var array<string, LinesOfCode>
* @var array<string, LinesOfCodeType>
*/
private array $linesOfCode = [];
@@ -82,17 +82,7 @@ final class ParsingFileAnalyser implements FileAnalyser
}
/**
* @return array<string, Interface_>
*/
public function interfacesIn(string $filename): array
{
$this->analyse($filename);
return $this->interfaces[$filename];
}
/**
* @return array<string, Class_>
* @return array<string, CodeUnitClassType>
*/
public function classesIn(string $filename): array
{
@@ -102,7 +92,7 @@ final class ParsingFileAnalyser implements FileAnalyser
}
/**
* @return array<string, Trait_>
* @return array<string, CodeUnitTraitType>
*/
public function traitsIn(string $filename): array
{
@@ -112,7 +102,7 @@ final class ParsingFileAnalyser implements FileAnalyser
}
/**
* @return array<string, Function_>
* @return array<string, CodeUnitFunctionType>
*/
public function functionsIn(string $filename): array
{
@@ -121,7 +111,10 @@ final class ParsingFileAnalyser implements FileAnalyser
return $this->functions[$filename];
}
public function linesOfCodeFor(string $filename): LinesOfCode
/**
* @return LinesOfCodeType
*/
public function linesOfCodeFor(string $filename): array
{
$this->analyse($filename);
@@ -153,7 +146,7 @@ final class ParsingFileAnalyser implements FileAnalyser
*/
private function analyse(string $filename): void
{
if (isset($this->interfaces[$filename])) {
if (isset($this->classes[$filename])) {
return;
}
@@ -174,7 +167,7 @@ final class ParsingFileAnalyser implements FileAnalyser
assert($nodes !== null);
$traverser = new NodeTraverser;
$codeUnitFindingVisitor = new CodeUnitFindingVisitor($filename);
$codeUnitFindingVisitor = new CodeUnitFindingVisitor;
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode);
$executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($source);
@@ -202,7 +195,6 @@ final class ParsingFileAnalyser implements FileAnalyser
}
// @codeCoverageIgnoreEnd
$this->interfaces[$filename] = $codeUnitFindingVisitor->interfaces();
$this->classes[$filename] = $codeUnitFindingVisitor->classes();
$this->traits[$filename] = $codeUnitFindingVisitor->traits();
$this->functions[$filename] = $codeUnitFindingVisitor->functions();
@@ -222,11 +214,11 @@ final class ParsingFileAnalyser implements FileAnalyser
$result = $lineCountingVisitor->result();
$this->linesOfCode[$filename] = new LinesOfCode(
$result->linesOfCode(),
$result->commentLinesOfCode(),
$result->nonCommentLinesOfCode(),
);
$this->linesOfCode[$filename] = [
'linesOfCode' => $result->linesOfCode(),
'commentLinesOfCode' => $result->commentLinesOfCode(),
'nonCommentLinesOfCode' => $result->nonCommentLinesOfCode(),
];
}
private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): void

View File

@@ -1,174 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Class_
{
/**
* @var non-empty-string
*/
private string $name;
/**
* @var non-empty-string
*/
private string $namespacedName;
private string $namespace;
/**
* @var non-empty-string
*/
private string $file;
/**
* @var non-negative-int
*/
private int $startLine;
/**
* @var non-negative-int
*/
private int $endLine;
/**
* @var ?non-empty-string
*/
private ?string $parentClass;
/**
* @var list<non-empty-string>
*/
private array $interfaces;
/**
* @var list<non-empty-string>
*/
private array $traits;
/**
* @var array<non-empty-string, Method>
*/
private array $methods;
/**
* @param non-empty-string $name
* @param non-empty-string $namespacedName
* @param non-empty-string $file
* @param non-negative-int $startLine
* @param non-negative-int $endLine
* @param ?non-empty-string $parentClass
* @param list<non-empty-string> $interfaces
* @param list<non-empty-string> $traits
* @param array<non-empty-string, Method> $methods
*/
public function __construct(string $name, string $namespacedName, string $namespace, string $file, int $startLine, int $endLine, ?string $parentClass, array $interfaces, array $traits, array $methods)
{
$this->name = $name;
$this->namespacedName = $namespacedName;
$this->namespace = $namespace;
$this->file = $file;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->parentClass = $parentClass;
$this->interfaces = $interfaces;
$this->traits = $traits;
$this->methods = $methods;
}
/**
* @return non-empty-string
*/
public function name(): string
{
return $this->name;
}
/**
* @return non-empty-string
*/
public function namespacedName(): string
{
return $this->namespacedName;
}
public function isNamespaced(): bool
{
return $this->namespace !== '';
}
public function namespace(): string
{
return $this->namespace;
}
/**
* @return non-empty-string
*/
public function file(): string
{
return $this->file;
}
/**
* @return non-negative-int
*/
public function startLine(): int
{
return $this->startLine;
}
/**
* @return non-negative-int
*/
public function endLine(): int
{
return $this->endLine;
}
public function hasParent(): bool
{
return $this->parentClass !== null;
}
/**
* @return ?non-empty-string
*/
public function parentClass(): ?string
{
return $this->parentClass;
}
/**
* @return list<non-empty-string>
*/
public function interfaces(): array
{
return $this->interfaces;
}
/**
* @return list<non-empty-string>
*/
public function traits(): array
{
return $this->traits;
}
/**
* @return array<non-empty-string, Method>
*/
public function methods(): array
{
return $this->methods;
}
}

View File

@@ -1,124 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Function_
{
/**
* @var non-empty-string
*/
private string $name;
/**
* @var non-empty-string
*/
private string $namespacedName;
private string $namespace;
/**
* @var non-negative-int
*/
private int $startLine;
/**
* @var non-negative-int
*/
private int $endLine;
/**
* @var non-empty-string
*/
private string $signature;
/**
* @var positive-int
*/
private int $cyclomaticComplexity;
/**
* @param non-empty-string $name
* @param non-empty-string $namespacedName
* @param non-negative-int $startLine
* @param non-negative-int $endLine
* @param non-empty-string $signature
* @param positive-int $cyclomaticComplexity
*/
public function __construct(string $name, string $namespacedName, string $namespace, int $startLine, int $endLine, string $signature, int $cyclomaticComplexity)
{
$this->name = $name;
$this->namespacedName = $namespacedName;
$this->namespace = $namespace;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->signature = $signature;
$this->cyclomaticComplexity = $cyclomaticComplexity;
}
/**
* @return non-empty-string
*/
public function name(): string
{
return $this->name;
}
/**
* @return non-empty-string
*/
public function namespacedName(): string
{
return $this->namespacedName;
}
public function isNamespaced(): bool
{
return $this->namespace !== '';
}
public function namespace(): string
{
return $this->namespace;
}
/**
* @return non-negative-int
*/
public function startLine(): int
{
return $this->startLine;
}
/**
* @return non-negative-int
*/
public function endLine(): int
{
return $this->endLine;
}
/**
* @return non-empty-string
*/
public function signature(): string
{
return $this->signature;
}
/**
* @return positive-int
*/
public function cyclomaticComplexity(): int
{
return $this->cyclomaticComplexity;
}
}

View File

@@ -1,109 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Interface_
{
/**
* @var non-empty-string
*/
private string $name;
/**
* @var non-empty-string
*/
private string $namespacedName;
private string $namespace;
/**
* @var non-negative-int
*/
private int $startLine;
/**
* @var non-negative-int
*/
private int $endLine;
/**
* @var list<non-empty-string>
*/
private array $parentInterfaces;
/**
* @param non-empty-string $name
* @param non-empty-string $namespacedName
* @param non-negative-int $startLine
* @param non-negative-int $endLine
* @param list<non-empty-string> $parentInterfaces
*/
public function __construct(string $name, string $namespacedName, string $namespace, int $startLine, int $endLine, array $parentInterfaces)
{
$this->name = $name;
$this->namespacedName = $namespacedName;
$this->namespace = $namespace;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->parentInterfaces = $parentInterfaces;
}
/**
* @return non-empty-string
*/
public function name(): string
{
return $this->name;
}
/**
* @return non-empty-string
*/
public function namespacedName(): string
{
return $this->namespacedName;
}
public function isNamespaced(): bool
{
return $this->namespace !== '';
}
public function namespace(): string
{
return $this->namespace;
}
/**
* @return non-negative-int
*/
public function startLine(): int
{
return $this->startLine;
}
/**
* @return non-negative-int
*/
public function endLine(): int
{
return $this->endLine;
}
/**
* @return list<non-empty-string>
*/
public function parentInterfaces(): array
{
return $this->parentInterfaces;
}
}

View File

@@ -1,67 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class LinesOfCode
{
/**
* @var non-negative-int
*/
private int $linesOfCode;
/**
* @var non-negative-int
*/
private int $commentLinesOfCode;
/**
* @var non-negative-int
*/
private int $nonCommentLinesOfCode;
/**
* @param non-negative-int $linesOfCode
* @param non-negative-int $commentLinesOfCode
* @param non-negative-int $nonCommentLinesOfCode
*/
public function __construct(int $linesOfCode, int $commentLinesOfCode, int $nonCommentLinesOfCode)
{
$this->linesOfCode = $linesOfCode;
$this->commentLinesOfCode = $commentLinesOfCode;
$this->nonCommentLinesOfCode = $nonCommentLinesOfCode;
}
/**
* @return non-negative-int
*/
public function linesOfCode(): int
{
return $this->linesOfCode;
}
/**
* @return non-negative-int
*/
public function commentLinesOfCode(): int
{
return $this->commentLinesOfCode;
}
/**
* @return non-negative-int
*/
public function nonCommentLinesOfCode(): int
{
return $this->nonCommentLinesOfCode;
}
}

View File

@@ -1,104 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Method
{
/**
* @var non-empty-string
*/
private string $name;
/**
* @var non-negative-int
*/
private int $startLine;
/**
* @var non-negative-int
*/
private int $endLine;
private Visibility $visibility;
/**
* @var non-empty-string
*/
private string $signature;
/**
* @var positive-int
*/
private int $cyclomaticComplexity;
/**
* @param non-empty-string $name
* @param non-negative-int $startLine
* @param non-negative-int $endLine
* @param non-empty-string $signature
* @param positive-int $cyclomaticComplexity
*/
public function __construct(string $name, int $startLine, int $endLine, string $signature, Visibility $visibility, int $cyclomaticComplexity)
{
$this->name = $name;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->signature = $signature;
$this->visibility = $visibility;
$this->cyclomaticComplexity = $cyclomaticComplexity;
}
/**
* @return non-empty-string
*/
public function name(): string
{
return $this->name;
}
/**
* @return non-negative-int
*/
public function startLine(): int
{
return $this->startLine;
}
/**
* @return non-negative-int
*/
public function endLine(): int
{
return $this->endLine;
}
/**
* @return non-empty-string
*/
public function signature(): string
{
return $this->signature;
}
public function visibility(): Visibility
{
return $this->visibility;
}
/**
* @return positive-int
*/
public function cyclomaticComplexity(): int
{
return $this->cyclomaticComplexity;
}
}

View File

@@ -1,139 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Trait_
{
/**
* @var non-empty-string
*/
private string $name;
/**
* @var non-empty-string
*/
private string $namespacedName;
private string $namespace;
/**
* @var non-empty-string
*/
private string $file;
/**
* @var non-negative-int
*/
private int $startLine;
/**
* @var non-negative-int
*/
private int $endLine;
/**
* @var list<non-empty-string>
*/
private array $traits;
/**
* @var array<non-empty-string, Method>
*/
private array $methods;
/**
* @param non-empty-string $name
* @param non-empty-string $namespacedName
* @param non-empty-string $file
* @param non-negative-int $startLine
* @param non-negative-int $endLine
* @param list<non-empty-string> $traits
* @param array<non-empty-string, Method> $methods
*/
public function __construct(string $name, string $namespacedName, string $namespace, string $file, int $startLine, int $endLine, array $traits, array $methods)
{
$this->name = $name;
$this->namespacedName = $namespacedName;
$this->namespace = $namespace;
$this->file = $file;
$this->startLine = $startLine;
$this->endLine = $endLine;
$this->traits = $traits;
$this->methods = $methods;
}
/**
* @return non-empty-string
*/
public function name(): string
{
return $this->name;
}
/**
* @return non-empty-string
*/
public function namespacedName(): string
{
return $this->namespacedName;
}
public function isNamespaced(): bool
{
return $this->namespace !== '';
}
public function namespace(): string
{
return $this->namespace;
}
/**
* @return non-empty-string
*/
public function file(): string
{
return $this->file;
}
/**
* @return non-negative-int
*/
public function startLine(): int
{
return $this->startLine;
}
/**
* @return non-negative-int
*/
public function endLine(): int
{
return $this->endLine;
}
/**
* @return list<non-empty-string>
*/
public function traits(): array
{
return $this->traits;
}
/**
* @return array<non-empty-string, Method>
*/
public function methods(): array
{
return $this->methods;
}
}

View File

@@ -1,20 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
/**
* @internal This enumeration is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
enum Visibility: string
{
case Public = 'public';
case Protected = 'protected';
case Private = 'private';
}

View File

@@ -1,68 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Class_ extends Target
{
/**
* @var class-string
*/
private string $className;
/**
* @param class-string $className
*/
protected function __construct(string $className)
{
$this->className = $className;
}
public function isClass(): true
{
return true;
}
/**
* @return class-string
*/
public function className(): string
{
return $this->className;
}
/**
* @return non-empty-string
*/
public function key(): string
{
return 'classes';
}
/**
* @return non-empty-string
*/
public function target(): string
{
return $this->className;
}
/**
* @return non-empty-string
*/
public function description(): string
{
return 'Class ' . $this->target();
}
}

View File

@@ -1,68 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class ClassesThatExtendClass extends Target
{
/**
* @var class-string
*/
private string $className;
/**
* @param class-string $className
*/
protected function __construct(string $className)
{
$this->className = $className;
}
public function isClassesThatExtendClass(): true
{
return true;
}
/**
* @return class-string
*/
public function className(): string
{
return $this->className;
}
/**
* @return non-empty-string
*/
public function key(): string
{
return 'classesThatExtendClass';
}
/**
* @return non-empty-string
*/
public function target(): string
{
return $this->className;
}
/**
* @return non-empty-string
*/
public function description(): string
{
return 'Classes that extend class ' . $this->target();
}
}

View File

@@ -1,68 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class ClassesThatImplementInterface extends Target
{
/**
* @var class-string
*/
private string $interfaceName;
/**
* @param class-string $interfaceName
*/
protected function __construct(string $interfaceName)
{
$this->interfaceName = $interfaceName;
}
public function isClassesThatImplementInterface(): true
{
return true;
}
/**
* @return class-string
*/
public function interfaceName(): string
{
return $this->interfaceName;
}
/**
* @return non-empty-string
*/
public function key(): string
{
return 'classesThatImplementInterface';
}
/**
* @return non-empty-string
*/
public function target(): string
{
return $this->interfaceName;
}
/**
* @return non-empty-string
*/
public function description(): string
{
return 'Classes that implement interface ' . $this->target();
}
}

View File

@@ -1,68 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Function_ extends Target
{
/**
* @var non-empty-string
*/
private string $functionName;
/**
* @param non-empty-string $functionName
*/
protected function __construct(string $functionName)
{
$this->functionName = $functionName;
}
public function isFunction(): true
{
return true;
}
/**
* @return non-empty-string
*/
public function functionName(): string
{
return $this->functionName;
}
/**
* @return non-empty-string
*/
public function key(): string
{
return 'functions';
}
/**
* @return non-empty-string
*/
public function target(): string
{
return $this->functionName;
}
/**
* @return non-empty-string
*/
public function description(): string
{
return 'Function ' . $this->target();
}
}

View File

@@ -1,249 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
use function array_keys;
use function array_merge;
use function array_slice;
use function array_unique;
use function count;
use function explode;
use function implode;
use function range;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\StaticAnalysis\Class_;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\Trait_;
/**
* @phpstan-import-type TargetMap from Mapper
* @phpstan-import-type TargetMapPart from Mapper
*
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class MapBuilder
{
/**
* @return TargetMap
*/
public function build(Filter $filter, FileAnalyser $analyser): array
{
$namespaces = [];
$classes = [];
$classDetails = [];
$classesThatExtendClass = [];
$classesThatImplementInterface = [];
$traits = [];
$methods = [];
$functions = [];
$reverseLookup = [];
foreach ($filter->files() as $file) {
foreach ($analyser->traitsIn($file) as $trait) {
if ($trait->isNamespaced()) {
$this->processNamespace($namespaces, $trait->namespace(), $file, $trait->startLine(), $trait->endLine());
}
$this->process($traits, $trait->namespacedName(), $file, $trait->startLine(), $trait->endLine());
$this->processMethods($trait, $file, $methods, $reverseLookup);
}
}
foreach ($filter->files() as $file) {
foreach ($analyser->traitsIn($file) as $trait) {
foreach ($trait->traits() as $traitName) {
if (!isset($traits[$traitName])) {
continue;
}
$file = array_keys($traits[$traitName])[0];
if (!isset($traits[$trait->namespacedName()][$file])) {
$traits[$trait->namespacedName()][$file] = $traits[$traitName][$file];
continue;
}
$traits[$trait->namespacedName()][$file] = array_unique(
array_merge(
$traits[$trait->namespacedName()][$file],
$traits[$traitName][$file],
),
);
}
}
}
foreach ($filter->files() as $file) {
foreach ($analyser->interfacesIn($file) as $interface) {
$classesThatImplementInterface[$interface->namespacedName()] = [];
}
foreach ($analyser->classesIn($file) as $class) {
if ($class->isNamespaced()) {
$this->processNamespace($namespaces, $class->namespace(), $file, $class->startLine(), $class->endLine());
}
$this->process($classes, $class->namespacedName(), $file, $class->startLine(), $class->endLine());
foreach ($class->traits() as $traitName) {
if (!isset($traits[$traitName])) {
continue;
}
foreach ($traits[$traitName] as $file => $lines) {
if (!isset($classes[$class->namespacedName()][$file])) {
$classes[$class->namespacedName()][$file] = $lines;
continue;
}
$classes[$class->namespacedName()][$file] = array_unique(
array_merge(
$classes[$class->namespacedName()][$file],
$lines,
),
);
}
}
$this->processMethods($class, $file, $methods, $reverseLookup);
$classesThatExtendClass[$class->namespacedName()] = [];
$classDetails[] = $class;
}
foreach ($analyser->functionsIn($file) as $function) {
if ($function->isNamespaced()) {
$this->processNamespace($namespaces, $function->namespace(), $file, $function->startLine(), $function->endLine());
}
$this->process($functions, $function->namespacedName(), $file, $function->startLine(), $function->endLine());
foreach (range($function->startLine(), $function->endLine()) as $line) {
$reverseLookup[$file . ':' . $line] = $function->namespacedName();
}
}
}
foreach (array_keys($namespaces) as $namespace) {
foreach (array_keys($namespaces[$namespace]) as $file) {
$namespaces[$namespace][$file] = array_unique($namespaces[$namespace][$file]);
}
}
foreach ($classDetails as $class) {
foreach ($class->interfaces() as $interfaceName) {
if (!isset($classesThatImplementInterface[$interfaceName])) {
continue;
}
$this->process($classesThatImplementInterface, $interfaceName, $class->file(), $class->startLine(), $class->endLine());
}
if (!$class->hasParent()) {
continue;
}
if (!isset($classesThatExtendClass[$class->parentClass()])) {
continue;
}
$this->process($classesThatExtendClass, $class->parentClass(), $class->file(), $class->startLine(), $class->endLine());
}
foreach (array_keys($classesThatImplementInterface) as $className) {
if ($classesThatImplementInterface[$className] !== []) {
continue;
}
unset($classesThatImplementInterface[$className]);
}
foreach (array_keys($classesThatExtendClass) as $className) {
if ($classesThatExtendClass[$className] !== []) {
continue;
}
unset($classesThatExtendClass[$className]);
}
return [
'namespaces' => $namespaces,
'traits' => $traits,
'classes' => $classes,
'classesThatExtendClass' => $classesThatExtendClass,
'classesThatImplementInterface' => $classesThatImplementInterface,
'methods' => $methods,
'functions' => $functions,
'reverseLookup' => $reverseLookup,
];
}
private function processMethods(Class_|Trait_ $classOrTrait, string $file, array &$methods, array &$reverseLookup): void
{
foreach ($classOrTrait->methods() as $method) {
$methodName = $classOrTrait->namespacedName() . '::' . $method->name();
$this->process($methods, $methodName, $file, $method->startLine(), $method->endLine());
foreach (range($method->startLine(), $method->endLine()) as $line) {
$reverseLookup[$file . ':' . $line] = $methodName;
}
}
}
/**
* @param TargetMapPart $data
* @param non-empty-string $namespace
* @param non-empty-string $file
* @param positive-int $startLine
* @param positive-int $endLine
*
* @param-out TargetMapPart $data
*/
private function processNamespace(array &$data, string $namespace, string $file, int $startLine, int $endLine): void
{
$parts = explode('\\', $namespace);
foreach (range(1, count($parts)) as $i) {
$this->process($data, implode('\\', array_slice($parts, 0, $i)), $file, $startLine, $endLine);
}
}
/**
* @param TargetMapPart $data
* @param non-empty-string $unit
* @param non-empty-string $file
* @param positive-int $startLine
* @param positive-int $endLine
*
* @param-out TargetMapPart $data
*/
private function process(array &$data, string $unit, string $file, int $startLine, int $endLine): void
{
if (!isset($data[$unit])) {
$data[$unit] = [];
}
if (!isset($data[$unit][$file])) {
$data[$unit][$file] = [];
}
$data[$unit][$file] = array_merge(
$data[$unit][$file],
range($startLine, $endLine),
);
}
}

View File

@@ -1,93 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
use function array_merge;
use function array_unique;
/**
* @phpstan-type TargetMap array{namespaces: TargetMapPart, traits: TargetMapPart, classes: TargetMapPart, classesThatExtendClass: TargetMapPart, classesThatImplementInterface: TargetMapPart, methods: TargetMapPart, functions: TargetMapPart, reverseLookup: ReverseLookup}
* @phpstan-type TargetMapPart array<non-empty-string, array<non-empty-string, list<positive-int>>>
* @phpstan-type ReverseLookup array<non-empty-string, non-empty-string>
*
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Mapper
{
/**
* @var TargetMap
*/
private array $map;
/**
* @param TargetMap $map
*/
public function __construct(array $map)
{
$this->map = $map;
}
/**
* @return array<non-empty-string, list<positive-int>>
*/
public function mapTargets(TargetCollection $targets): array
{
$result = [];
foreach ($targets as $target) {
foreach ($this->mapTarget($target) as $file => $lines) {
if (!isset($result[$file])) {
$result[$file] = $lines;
continue;
}
$result[$file] = array_unique(array_merge($result[$file], $lines));
}
}
return $result;
}
/**
* @throws InvalidCodeCoverageTargetException
*
* @return array<non-empty-string, list<positive-int>>
*/
public function mapTarget(Target $target): array
{
if (!isset($this->map[$target->key()][$target->target()])) {
throw new InvalidCodeCoverageTargetException($target);
}
return $this->map[$target->key()][$target->target()];
}
/**
* @param non-empty-string $file
* @param positive-int $line
*
* @return non-empty-string
*/
public function lookup(string $file, int $line): string
{
$key = $file . ':' . $line;
if (isset($this->map['reverseLookup'][$key])) {
return $this->map['reverseLookup'][$key];
}
return $key;
}
}

View File

@@ -1,83 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Method extends Target
{
/**
* @var class-string
*/
private string $className;
/**
* @var non-empty-string
*/
private string $methodName;
/**
* @param class-string $className
* @param non-empty-string $methodName
*/
protected function __construct(string $className, string $methodName)
{
$this->className = $className;
$this->methodName = $methodName;
}
public function isMethod(): true
{
return true;
}
/**
* @return class-string
*/
public function className(): string
{
return $this->className;
}
/**
* @return non-empty-string
*/
public function methodName(): string
{
return $this->methodName;
}
/**
* @return non-empty-string
*/
public function key(): string
{
return 'methods';
}
/**
* @return non-empty-string
*/
public function target(): string
{
return $this->className . '::' . $this->methodName;
}
/**
* @return non-empty-string
*/
public function description(): string
{
return 'Method ' . $this->target();
}
}

View File

@@ -1,68 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Namespace_ extends Target
{
/**
* @var non-empty-string
*/
private string $namespace;
/**
* @param non-empty-string $namespace
*/
protected function __construct(string $namespace)
{
$this->namespace = $namespace;
}
public function isNamespace(): true
{
return true;
}
/**
* @return non-empty-string
*/
public function namespace(): string
{
return $this->namespace;
}
/**
* @return non-empty-string
*/
public function key(): string
{
return 'namespaces';
}
/**
* @return non-empty-string
*/
public function target(): string
{
return $this->namespace;
}
/**
* @return non-empty-string
*/
public function description(): string
{
return 'Namespace ' . $this->target();
}
}

View File

@@ -1,125 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
abstract class Target
{
/**
* @param non-empty-string $namespace
*/
public static function forNamespace(string $namespace): Namespace_
{
return new Namespace_($namespace);
}
/**
* @param class-string $className
*/
public static function forClass(string $className): Class_
{
return new Class_($className);
}
/**
* @param class-string $className
* @param non-empty-string $methodName
*/
public static function forMethod(string $className, string $methodName): Method
{
return new Method($className, $methodName);
}
/**
* @param class-string $interfaceName
*/
public static function forClassesThatImplementInterface(string $interfaceName): ClassesThatImplementInterface
{
return new ClassesThatImplementInterface($interfaceName);
}
/**
* @param class-string $className
*/
public static function forClassesThatExtendClass(string $className): ClassesThatExtendClass
{
return new ClassesThatExtendClass($className);
}
/**
* @param non-empty-string $functionName
*/
public static function forFunction(string $functionName): Function_
{
return new Function_($functionName);
}
/**
* @param trait-string $traitName
*/
public static function forTrait(string $traitName): Trait_
{
return new Trait_($traitName);
}
public function isNamespace(): bool
{
return false;
}
public function isClass(): bool
{
return false;
}
public function isMethod(): bool
{
return false;
}
public function isClassesThatImplementInterface(): bool
{
return false;
}
public function isClassesThatExtendClass(): bool
{
return false;
}
public function isFunction(): bool
{
return false;
}
public function isTrait(): bool
{
return false;
}
/**
* @return non-empty-string
*/
abstract public function key(): string;
/**
* @return non-empty-string
*/
abstract public function target(): string;
/**
* @return non-empty-string
*/
abstract public function description(): string;
}

View File

@@ -1,70 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
use function count;
use Countable;
use IteratorAggregate;
/**
* @template-implements IteratorAggregate<int, Target>
*
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class TargetCollection implements Countable, IteratorAggregate
{
/**
* @var list<Target>
*/
private array $targets;
/**
* @param list<Target> $targets
*/
public static function fromArray(array $targets): self
{
return new self(...$targets);
}
private function __construct(Target ...$targets)
{
$this->targets = $targets;
}
/**
* @return list<Target>
*/
public function asArray(): array
{
return $this->targets;
}
public function count(): int
{
return count($this->targets);
}
public function isEmpty(): bool
{
return $this->count() === 0;
}
public function isNotEmpty(): bool
{
return $this->count() > 0;
}
public function getIterator(): TargetCollectionIterator
{
return new TargetCollectionIterator($this);
}
}

View File

@@ -1,57 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
use function count;
use Iterator;
/**
* @template-implements Iterator<int, Target>
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class TargetCollectionIterator implements Iterator
{
/**
* @var list<Target>
*/
private readonly array $targets;
private int $position = 0;
public function __construct(TargetCollection $metadata)
{
$this->targets = $metadata->asArray();
}
public function rewind(): void
{
$this->position = 0;
}
public function valid(): bool
{
return $this->position < count($this->targets);
}
public function key(): int
{
return $this->position;
}
public function current(): Target
{
return $this->targets[$this->position];
}
public function next(): void
{
$this->position++;
}
}

View File

@@ -1,39 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
use function implode;
/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class TargetCollectionValidator
{
public function validate(Mapper $mapper, TargetCollection $targets): ValidationResult
{
$errors = [];
foreach ($targets as $target) {
try {
$mapper->mapTarget($target);
} catch (InvalidCodeCoverageTargetException $e) {
$errors[] = $e->getMessage();
}
}
if ($errors === []) {
return ValidationResult::success();
}
return ValidationResult::failure(implode("\n", $errors));
}
}

View File

@@ -1,68 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Trait_ extends Target
{
/**
* @var trait-string
*/
private string $traitName;
/**
* @param trait-string $traitName
*/
protected function __construct(string $traitName)
{
$this->traitName = $traitName;
}
public function isTrait(): true
{
return true;
}
/**
* @return trait-string
*/
public function traitName(): string
{
return $this->traitName;
}
/**
* @return non-empty-string
*/
public function key(): string
{
return 'traits';
}
/**
* @return non-empty-string
*/
public function target(): string
{
return $this->traitName;
}
/**
* @return non-empty-string
*/
public function description(): string
{
return 'Trait ' . $this->target();
}
}

View File

@@ -1,46 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class ValidationFailure extends ValidationResult
{
/**
* @var non-empty-string
*/
private string $message;
/**
* @param non-empty-string $message
*
* @noinspection PhpMissingParentConstructorInspection
*/
protected function __construct(string $message)
{
$this->message = $message;
}
public function isFailure(): true
{
return true;
}
/**
* @return non-empty-string
*/
public function message(): string
{
return $this->message;
}
}

View File

@@ -1,51 +0,0 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage\Test\Target;
/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
abstract readonly class ValidationResult
{
public static function success(): ValidationSuccess
{
return new ValidationSuccess;
}
/**
* @param non-empty-string $message
*/
public static function failure(string $message): ValidationFailure
{
return new ValidationFailure($message);
}
protected function __construct()
{
}
/**
* @phpstan-assert-if-true ValidationSuccess $this
*/
public function isSuccess(): bool
{
return false;
}
/**
* @phpstan-assert-if-true ValidationFailure $this
*/
public function isFailure(): bool
{
return false;
}
}

View File

@@ -14,10 +14,10 @@ use function sprintf;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Percentage
final class Percentage
{
private float $fraction;
private float $total;
private readonly float $fraction;
private readonly float $total;
public static function fromFractionAndTotal(float $fraction, float $total): self
{

View File

@@ -19,7 +19,7 @@ final class Version
public static function id(): string
{
if (self::$version === '') {
self::$version = (new VersionId('12.0', dirname(__DIR__)))->asString();
self::$version = (new VersionId('11.0.8', dirname(__DIR__)))->asString();
}
return self::$version;

View File

@@ -2,12 +2,6 @@
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
## [6.0.0] - 2025-02-07
### Removed
* This component is no longer supported on PHP 8.2
## [5.1.0] - 2024-08-27
### Added
@@ -174,7 +168,6 @@ No changes
* [#23](https://github.com/sebastianbergmann/php-file-iterator/pull/23): Added support for wildcards (glob) in exclude
[6.0.0]: https://github.com/sebastianbergmann/php-file-iterator/compare/5.1...main
[5.1.0]: https://github.com/sebastianbergmann/php-file-iterator/compare/5.0.1...5.1.0
[5.0.1]: https://github.com/sebastianbergmann/php-file-iterator/compare/5.0.0...5.0.1
[5.0.0]: https://github.com/sebastianbergmann/php-file-iterator/compare/4.1...5.0.0

View File

@@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2009-2025, Sebastian Bergmann
Copyright (c) 2009-2024, Sebastian Bergmann
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -21,18 +21,17 @@
},
"config": {
"platform": {
"php": "8.3.0"
"php": "8.2.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"prefer-stable": true,
"minimum-stability": "dev",
"require": {
"php": ">=8.3"
"php": ">=8.2"
},
"require-dev": {
"phpunit/phpunit": "^12.0"
"phpunit/phpunit": "^11.0"
},
"autoload": {
"classmap": [
@@ -41,7 +40,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "6.0-dev"
"dev-main": "5.0-dev"
}
}
}

View File

@@ -37,8 +37,6 @@ final class Factory
* @param list<non-empty-string>|string $suffixes
* @param list<non-empty-string>|string $prefixes
* @param list<non-empty-string> $exclude
*
* @phpstan-ignore missingType.generics
*/
public function getFileIterator(array|string $paths, array|string $suffixes = '', array|string $prefixes = '', array $exclude = []): AppendIterator
{
@@ -115,7 +113,7 @@ final class Factory
*
* @return list<string>
*/
private function globstar(string $pattern): array
private function globstar(string $pattern)
{
if (stripos($pattern, '**') === false) {
$files = glob($pattern, GLOB_ONLYDIR);
@@ -127,24 +125,23 @@ final class Factory
$patterns = [$rootPattern . $restPattern];
$rootPattern .= '/*';
while ($directories = glob($rootPattern, GLOB_ONLYDIR)) {
while ($dirs = glob($rootPattern, GLOB_ONLYDIR)) {
$rootPattern .= '/*';
foreach ($directories as $directory) {
$patterns[] = $directory . $restPattern;
foreach ($dirs as $dir) {
$patterns[] = $dir . $restPattern;
}
}
$files = [];
foreach ($patterns as $_pattern) {
$files = array_merge($files, $this->globstar($_pattern));
foreach ($patterns as $pat) {
$files = array_merge($files, $this->globstar($pat));
}
}
if ($files !== false) {
$files = array_unique($files);
sort($files);
return $files;

View File

@@ -9,6 +9,7 @@
*/
namespace SebastianBergmann\FileIterator;
use function assert;
use function preg_match;
use function realpath;
use function str_ends_with;
@@ -24,8 +25,8 @@ use SplFileInfo;
*/
final class Iterator extends FilterIterator
{
public const int PREFIX = 0;
public const int SUFFIX = 1;
public const PREFIX = 0;
public const SUFFIX = 1;
private false|string $basePath;
/**
@@ -55,6 +56,8 @@ final class Iterator extends FilterIterator
{
$current = $this->getInnerIterator()->current();
assert($current instanceof SplFileInfo);
$filename = $current->getFilename();
$realPath = $current->getRealPath();

View File

@@ -2,12 +2,6 @@
All notable changes are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [6.0.0] - 2025-02-07
### Removed
* This component is no longer supported on PHP 8.2
## [5.0.1] - 2024-07-03
### Changed
@@ -65,7 +59,6 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt
* This component is no longer supported on PHP 7.1 and PHP 7.2
[6.0.0]: https://github.com/sebastianbergmann/php-invoker/compare/5.0...main
[5.0.1]: https://github.com/sebastianbergmann/php-invoker/compare/5.0.1...5.0.1
[5.0.0]: https://github.com/sebastianbergmann/php-invoker/compare/4.0...5.0.0
[4.0.0]: https://github.com/sebastianbergmann/php-invoker/compare/3.1.1...4.0.0

View File

@@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2011-2025, Sebastian Bergmann
Copyright (c) 2011-2024, Sebastian Bergmann
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -1,6 +1,6 @@
# phpunit/php-invoker
[![Latest Stable Version](https://poser.pugx.org/phpunit/php-invoker/v)](https://packagist.org/packages/phpunit/php-invoker)
[![Latest Stable Version](https://poser.pugx.org/phpunit/php-invoker/v/stable.png)](https://packagist.org/packages/phpunit/php-invoker)
[![CI Status](https://github.com/sebastianbergmann/php-invoker/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/php-invoker/actions)
[![codecov](https://codecov.io/gh/sebastianbergmann/php-invoker/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/php-invoker)

View File

@@ -18,21 +18,20 @@
"issues": "https://github.com/sebastianbergmann/php-invoker/issues",
"security": "https://github.com/sebastianbergmann/php-invoker/security/policy"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"platform": {
"php": "8.3.0"
"php": "8.2.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"require": {
"php": ">=8.3"
"php": ">=8.2"
},
"require-dev": {
"ext-pcntl": "*",
"phpunit/phpunit": "^12.0-dev"
"phpunit/phpunit": "^11.0"
},
"autoload": {
"classmap": [
@@ -49,7 +48,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "6.0-dev"
"dev-main": "5.0-dev"
}
}
}

View File

@@ -9,7 +9,6 @@
*/
namespace SebastianBergmann\Invoker;
use const PHP_EOL;
use function extension_loaded;
use function function_exists;
use function implode;

View File

@@ -2,12 +2,6 @@
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [5.0.0] - 2025-02-07
### Removed
* This component is no longer supported on PHP 8.2
## [4.0.1] - 2024-07-03
### Changed
@@ -67,7 +61,6 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt
* Removed support for PHP 5.3, PHP 5.4, PHP 5.5, PHP 5.6, PHP 7.0, PHP 7.1, and PHP 7.2
[5.0.0]: https://github.com/sebastianbergmann/php-text-template/compare/4.0...main
[4.0.1]: https://github.com/sebastianbergmann/php-text-template/compare/4.0.0...4.0.1
[4.0.0]: https://github.com/sebastianbergmann/php-text-template/compare/3.0...4.0.0
[3.0.1]: https://github.com/sebastianbergmann/php-text-template/compare/3.0.0...3.0.1

View File

@@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2009-2025, Sebastian Bergmann
Copyright (c) 2009-2024, Sebastian Bergmann
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -1,4 +1,4 @@
[![Latest Stable Version](https://poser.pugx.org/phpunit/php-text-template/v)](https://packagist.org/packages/phpunit/php-text-template)
[![Latest Stable Version](https://poser.pugx.org/phpunit/php-text-template/v/stable.png)](https://packagist.org/packages/phpunit/php-text-template)
[![CI Status](https://github.com/sebastianbergmann/php-text-template/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/php-text-template/actions)
[![codecov](https://codecov.io/gh/sebastianbergmann/php-text-template/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/php-text-template)

View File

@@ -20,18 +20,17 @@
},
"config": {
"platform": {
"php": "8.3.0"
"php": "8.2.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.3"
"php": ">=8.2"
},
"require-dev": {
"phpunit/phpunit": "^12.0-dev"
"phpunit/phpunit": "^11.0"
},
"autoload": {
"classmap": [
@@ -40,7 +39,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "5.0-dev"
"dev-main": "4.0-dev"
}
}
}

View File

@@ -2,12 +2,6 @@
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
## [8.0.0] - 2025-02-07
### Removed
* This component is no longer supported on PHP 8.1
## [7.0.1] - 2024-07-03
### Changed
@@ -145,7 +139,6 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt
* This component is no longer supported on PHP 5.3, PHP 5.4, PHP 5.5, PHP 5.6, and PHP 7.0
[8.0.0]: https://github.com/sebastianbergmann/php-timer/compare/7.0...main
[7.0.1]: https://github.com/sebastianbergmann/php-timer/compare/7.0.0...7.0.1
[7.0.0]: https://github.com/sebastianbergmann/php-timer/compare/6.0...7.0.0
[6.0.0]: https://github.com/sebastianbergmann/php-timer/compare/5.0.3...6.0.0

View File

@@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2010-2025, Sebastian Bergmann
Copyright (c) 2010-2024, Sebastian Bergmann
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -1,6 +1,6 @@
# phpunit/php-timer
[![Latest Stable Version](https://poser.pugx.org/phpunit/php-timer/v)](https://packagist.org/packages/phpunit/php-timer)
[![Latest Stable Version](https://poser.pugx.org/phpunit/php-timer/v/stable.png)](https://packagist.org/packages/phpunit/php-timer)
[![CI Status](https://github.com/sebastianbergmann/php-timer/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/php-timer/actions)
[![codecov](https://codecov.io/gh/sebastianbergmann/php-timer/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/php-timer)

View File

@@ -18,17 +18,16 @@
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
"security": "https://github.com/sebastianbergmann/php-timer/security/policy"
},
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.3"
"php": ">=8.2"
},
"require-dev": {
"phpunit/phpunit": "^12.0-dev"
"phpunit/phpunit": "^11.0"
},
"config": {
"platform": {
"php": "8.3.0"
"php": "8.2.0"
},
"optimize-autoloader": true,
"sort-packages": true
@@ -40,7 +39,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "8.0-dev"
"dev-main": "7.0-dev"
}
}
}

View File

@@ -19,7 +19,7 @@ final class ResourceUsageFormatter
/**
* @var array<string,int>
*/
private const array SIZES = [
private const SIZES = [
'GB' => 1073741824,
'MB' => 1048576,
'KB' => 1024,

View File

@@ -0,0 +1 @@
{"version":1,"defects":[],"times":{"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testSort":0.01,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testSearch":0,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testGetRows":0.001,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testGetValue":0.002,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testCount":0.001,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testDelete":0,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testClear":0.001,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testMerge":0,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testMaxBy":0,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testGetColumn":0.002,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testToArray":0,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testMinBy":0,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testIsExist":0.001,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testUpdate":0.001,"goodboyalex\\php_components_pack\\tests\\classes\\ObjectArrayTest::testGetRow":0.002}}

View File

@@ -0,0 +1,97 @@
# Changes in PHPUnit 11.5
All notable changes of the PHPUnit 11.5 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [11.5.6] - 2025-01-31
### Changed
* [#6112](https://github.com/sebastianbergmann/phpunit/pull/6112): Improve performance of `SourceMapper`
### Fixed
* [#6115](https://github.com/sebastianbergmann/phpunit/issues/6115): Backed enumerations with values not of type `string` cannot be used in customized TestDox output
## [11.5.5] - 2025-01-29
### Changed
* Do not skip execution of test that depends on a test that is larger than itself
## [11.5.4] - 2025-01-28
### Changed
* [#5958](https://github.com/sebastianbergmann/phpunit/issues/5958): Support for `#[CoversTrait]` and `#[UsesTrait]` attributes is no longer deprecated
* [#5960](https://github.com/sebastianbergmann/phpunit/issues/5960): Support for targeting trait methods with the `#[CoversMethod]` and `#[UsesMethod]` attributes is no longer deprecated
### Fixed
* [#6103](https://github.com/sebastianbergmann/phpunit/issues/6103): Output from test run in separate process is printed twice
* [#6109](https://github.com/sebastianbergmann/phpunit/issues/6109): Skipping a test in a before-class method crashes JUnit XML logger
* [#6111](https://github.com/sebastianbergmann/phpunit/issues/6111): Deprecations cause `SourceMapper` to scan all `<source/>` files
## [11.5.3] - 2025-01-13
### Added
* `Test\AfterLastTestMethodErrored`, `Test\AfterTestMethodErrored`, `Test\BeforeTestMethodErrored`, `Test\PostConditionErrored`, and `Test\PreConditionErrored` events
### Fixed
* [#6093](https://github.com/sebastianbergmann/phpunit/issues/6093): Test Double Code Generator does not work when PHPUnit is used from PHAR on PHP 8.4
* [#6094](https://github.com/sebastianbergmann/phpunit/issues/6094): Errors in after-last-test methods are not reported
* [#6095](https://github.com/sebastianbergmann/phpunit/issues/6095): Expectation is not counted correctly when a doubled method is called more often than is expected
* [#6096](https://github.com/sebastianbergmann/phpunit/issues/6096): `--list-tests-xml` is broken when a group with a numeric name is defined
* [#6098](https://github.com/sebastianbergmann/phpunit/issues/6098): No `system-out` element in JUnit XML logfile
* [#6100](https://github.com/sebastianbergmann/phpunit/issues/6100): Suppressed deprecations incorrectly stop test execution when execution should be stopped on deprecation
## [11.5.2] - 2024-12-21
### Fixed
* [#6082](https://github.com/sebastianbergmann/phpunit/issues/6082): `assertArrayHasKey()`, `assertArrayNotHasKey()`, `arrayHasKey()`, and `ArrayHasKey::__construct()` do not support all possible key types
* [#6087](https://github.com/sebastianbergmann/phpunit/issues/6087): `--migrate-configuration` does not remove `beStrictAboutTodoAnnotatedTests` attribute from XML configuration file
## [11.5.1] - 2024-12-11
### Added
* [#6081](https://github.com/sebastianbergmann/phpunit/pull/6081): `DefaultResultCache::mergeWith()` for merging result cache instances
### Fixed
* [#6066](https://github.com/sebastianbergmann/phpunit/pull/6066): TeamCity logger does not handle error/skipped events in before-class methods correctly
## [11.5.0] - 2024-12-06
### Added
* [#5948](https://github.com/sebastianbergmann/phpunit/pull/5948): Support for Property Hooks in Test Doubles
* [#5954](https://github.com/sebastianbergmann/phpunit/issues/5954): Provide a way to stop execution at a particular deprecation
* Method `assertContainsNotOnlyInstancesOf()` in the `PHPUnit\Framework\Assert` class as the inverse of the `assertContainsOnlyInstancesOf()` method
* Methods `assertContainsOnlyArray()`, `assertContainsOnlyBool()`, `assertContainsOnlyCallable()`, `assertContainsOnlyFloat()`, `assertContainsOnlyInt()`, `assertContainsOnlyIterable()`, `assertContainsOnlyNull()`, `assertContainsOnlyNumeric()`, `assertContainsOnlyObject()`, `assertContainsOnlyResource()`, `assertContainsOnlyClosedResource()`, `assertContainsOnlyScalar()`, and `assertContainsOnlyString()` in the `PHPUnit\Framework\Assert` class as specialized alternatives for the generic `assertContainsOnly()` method
* Methods `assertContainsNotOnlyArray()`, `assertContainsNotOnlyBool()`, `assertContainsNotOnlyCallable()`, `assertContainsNotOnlyFloat()`, `assertContainsNotOnlyInt()`, `assertContainsNotOnlyIterable()`, `assertContainsNotOnlyNull()`, `assertContainsNotOnlyNumeric()`, `assertContainsNotOnlyObject()`, `assertContainsNotOnlyResource()`, `assertContainsNotOnlyClosedResource()`, `assertContainsNotOnlyScalar()`, and `assertContainsNotOnlyString()` in the `PHPUnit\Framework\Assert` class as specialized alternatives for the generic `assertNotContainsOnly()` method
* Methods `containsOnlyArray()`, `containsOnlyBool()`, `containsOnlyCallable()`, `containsOnlyFloat()`, `containsOnlyInt()`, `containsOnlyIterable()`, `containsOnlyNull()`, `containsOnlyNumeric()`, `containsOnlyObject()`, `containsOnlyResource()`, `containsOnlyClosedResource()`, `containsOnlyScalar()`, and `containsOnlyString()` in the `PHPUnit\Framework\Assert` class as specialized alternatives for the generic `containsOnly()` method
* Methods `isArray()`, `isBool()`, `isCallable()`, `isFloat()`, `isInt()`, `isIterable()`, `isNumeric()`, `isObject()`, `isResource()`, `isClosedResource()`, `isScalar()`, `isString()` in the `PHPUnit\Framework\Assert` class as specialized alternatives for the generic `isType()` method
* `TestRunner\ChildProcessStarted` and `TestRunner\ChildProcessFinished` events
### Changed
* [#5998](https://github.com/sebastianbergmann/phpunit/pull/5998): Do not run `SKIPIF` section of PHPT test in separate process when it is free of side effects
* [#5999](https://github.com/sebastianbergmann/phpunit/pull/5999): Do not run `CLEAN` section of PHPT test in separate process when it is free of side effects that modify the parent process
### Deprecated
* [#6052](https://github.com/sebastianbergmann/phpunit/issues/6052): `isType()` (use `isArray()`, `isBool()`, `isCallable()`, `isFloat()`, `isInt()`, `isIterable()`, `isNull()`, `isNumeric()`, `isObject()`, `isResource()`, `isClosedResource()`, `isScalar()`, or `isString()` instead)
* [#6055](https://github.com/sebastianbergmann/phpunit/issues/6055): `assertContainsOnly()` (use `assertContainsOnlyArray()`, `assertContainsOnlyBool()`, `assertContainsOnlyCallable()`, `assertContainsOnlyFloat()`, `assertContainsOnlyInt()`, `assertContainsOnlyIterable()`, `assertContainsOnlyNumeric()`, `assertContainsOnlyObject()`, `assertContainsOnlyResource()`, `assertContainsOnlyClosedResource()`, `assertContainsOnlyScalar()`, or `assertContainsOnlyString()` instead)
* [#6055](https://github.com/sebastianbergmann/phpunit/issues/6055): `assertNotContainsOnly()` (use `assertContainsNotOnlyArray()`, `assertContainsNotOnlyBool()`, `assertContainsNotOnlyCallable()`, `assertContainsNotOnlyFloat()`, `assertContainsNotOnlyInt()`, `assertContainsNotOnlyIterable()`, `assertContainsNotOnlyNumeric()`, `assertContainsNotOnlyObject()`, `assertContainsNotOnlyResource()`, `assertContainsNotOnlyClosedResource()`, `assertContainsNotOnlyScalar()`, or `assertContainsNotOnlyString()` instead)
* [#6059](https://github.com/sebastianbergmann/phpunit/issues/6059): `containsOnly()` (use `containsOnlyArray()`, `containsOnlyBool()`, `containsOnlyCallable()`, `containsOnlyFloat()`, `containsOnlyInt()`, `containsOnlyIterable()`, `containsOnlyNumeric()`, `containsOnlyObject()`, `containsOnlyResource()`, `containsOnlyClosedResource()`, `containsOnlyScalar()`, or `containsOnlyString()` instead)
[11.5.6]: https://github.com/sebastianbergmann/phpunit/compare/11.5.5...11.5.6
[11.5.5]: https://github.com/sebastianbergmann/phpunit/compare/11.5.4...11.5.5
[11.5.4]: https://github.com/sebastianbergmann/phpunit/compare/11.5.3...11.5.4
[11.5.3]: https://github.com/sebastianbergmann/phpunit/compare/11.5.2...11.5.3
[11.5.2]: https://github.com/sebastianbergmann/phpunit/compare/11.5.1...11.5.2
[11.5.1]: https://github.com/sebastianbergmann/phpunit/compare/11.5.0...11.5.1
[11.5.0]: https://github.com/sebastianbergmann/phpunit/compare/11.4.4...11.5.0

View File

@@ -1,52 +0,0 @@
# Changes in PHPUnit 12.0
All notable changes of the PHPUnit 12.0 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
## [12.0.0] - 2025-02-07
### Added
* [#5984](https://github.com/sebastianbergmann/phpunit/issues/5984): `#[CoversClassesThatExtendClass]` and `#[UsesClassesThatExtendClass]`
* [#5985](https://github.com/sebastianbergmann/phpunit/issues/5985): `#[CoversClassesThatImplementInterface]` and `#[UsesClassesThatImplementInterface]`
* [#6073](https://github.com/sebastianbergmann/phpunit/issues/6073): `#[CoversNamespace]` and `#[UsesNamespace]`
* [#6074](https://github.com/sebastianbergmann/phpunit/pull/6074): `#[RequiresEnvironmentVariable]`
### Changed
* [#5872](https://github.com/sebastianbergmann/phpunit/issues/5872): The default value for `shortenArraysForExportThreshold` is now `10` (limit export of arrays to 10 levels) instead of `0` (do not limit export of arrays)
### Deprecated
* [#6053](https://github.com/sebastianbergmann/phpunit/issues/6053): `Assert::isType()` (was soft-deprecated in PHPUnit 11.5)
* [#6056](https://github.com/sebastianbergmann/phpunit/issues/6056): `assertContainsOnly()` (was soft-deprecated in PHPUnit 11.5)
* [#6056](https://github.com/sebastianbergmann/phpunit/issues/6056): `assertNotContainsOnly()` (was soft-deprecated in PHPUnit 11.5)
* [#6060](https://github.com/sebastianbergmann/phpunit/issues/6060): `containsOnly()` (was soft-deprecated in PHPUnit 11.5)
### Removed
* [#5215](https://github.com/sebastianbergmann/phpunit/issues/5215): `TestCase::iniSet()`
* [#5217](https://github.com/sebastianbergmann/phpunit/issues/5217): `TestCase::setLocale()`
* [#5246](https://github.com/sebastianbergmann/phpunit/issues/5246): `TestCase::createTestProxy()`
* [#5247](https://github.com/sebastianbergmann/phpunit/issues/5247): `TestCase::getMockForAbstractClass()`
* [#5248](https://github.com/sebastianbergmann/phpunit/issues/5248): `TestCase::getMockFromWsdl()`
* [#5249](https://github.com/sebastianbergmann/phpunit/issues/5249): `TestCase::getMockForTrait()`
* [#5250](https://github.com/sebastianbergmann/phpunit/issues/5250): `TestCase::getObjectForTrait()`
* [#5310](https://github.com/sebastianbergmann/phpunit/issues/5310): `MockBuilder::enableAutoload()` and `MockBuilder::disableAutoload()`
* [#5311](https://github.com/sebastianbergmann/phpunit/issues/5311): `MockBuilder::allowMockingUnknownTypes()` and `MockBuilder::disallowMockingUnknownTypes()`
* [#5312](https://github.com/sebastianbergmann/phpunit/issues/5312): `MockBuilder::enableProxyingToOriginalMethods()`, `MockBuilder::disableProxyingToOriginalMethods()`, and `MockBuilder::setProxyTarget()`
* [#5313](https://github.com/sebastianbergmann/phpunit/issues/5313): `MockBuilder::getMockForTrait()`
* [#5314](https://github.com/sebastianbergmann/phpunit/issues/5314): `MockBuilder::getMockForAbstractClass()`
* [#5316](https://github.com/sebastianbergmann/phpunit/issues/5316): `MockBuilder::enableArgumentCloning()` and `MockBuilder::disableArgumentCloning()`
* [#5321](https://github.com/sebastianbergmann/phpunit/issues/5321): `MockBuilder::addMethods()`
* [#5416](https://github.com/sebastianbergmann/phpunit/issues/5416): Support for doubling interfaces (or classes) that have a method named `method`
* [#5424](https://github.com/sebastianbergmann/phpunit/issues/5424): `TestCase` methods for creating return stub configuration objects
* [#5473](https://github.com/sebastianbergmann/phpunit/issues/5473): `assertStringNotMatchesFormat()` and `assertStringNotMatchesFormatFile()`
* [#5536](https://github.com/sebastianbergmann/phpunit/issues/5536): Support for configuring expectations using `expects()` on test stubs
* [#5541](https://github.com/sebastianbergmann/phpunit/issues/5541): Support for metadata in doc-comments
* [#5710](https://github.com/sebastianbergmann/phpunit/issues/5710): Support for using comma-separated values with the `--group`, `--exclude-group`, `--covers`, `--uses`, and `--test-suffix` CLI options
* [#5756](https://github.com/sebastianbergmann/phpunit/issues/5756): Support for the `restrictDeprecations` attribute on the `<source>` element of the XML configuration file
* [#5801](https://github.com/sebastianbergmann/phpunit/issues/5801): Support for targeting traits with `#[CoversClass]` and `#[UsesClass]` attributes
* [#5952](https://github.com/sebastianbergmann/phpunit/issues/5952): `includeUncoveredFiles` configuration option
* [#5978](https://github.com/sebastianbergmann/phpunit/issues/5978): Support for PHP 8.2
[12.0.0]: https://github.com/sebastianbergmann/phpunit/compare/11.5...main

View File

@@ -8,6 +8,66 @@ This functionality is currently [hard-deprecated](https://phpunit.de/backward-co
#### Assertions, Constraints, and Expectations
| Issue | Description | Since | Replacement |
|-------------------------------------------------------------------|----------------------------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [#5472](https://github.com/sebastianbergmann/phpunit/issues/5472) | `Assert::assertStringNotMatchesFormat()` | 10.4.0 | |
| [#5472](https://github.com/sebastianbergmann/phpunit/issues/5472) | `Assert::assertStringNotMatchesFormatFile()` | 10.4.0 | |
#### Test Double API
| Issue | Description | Since | Replacement |
|-------------------------------------------------------------------|--------------------------------------------------------------------------------|--------|-----------------------------------------------------------------------------------------|
| [#5240](https://github.com/sebastianbergmann/phpunit/issues/5240) | `TestCase::createTestProxy()` | 10.1.0 | |
| [#5241](https://github.com/sebastianbergmann/phpunit/issues/5241) | `TestCase::getMockForAbstractClass()` | 10.1.0 | |
| [#5242](https://github.com/sebastianbergmann/phpunit/issues/5242) | `TestCase::getMockFromWsdl()` | 10.1.0 | |
| [#5243](https://github.com/sebastianbergmann/phpunit/issues/5243) | `TestCase::getMockForTrait()` | 10.1.0 | |
| [#5244](https://github.com/sebastianbergmann/phpunit/issues/5244) | `TestCase::getObjectForTrait()` | 10.1.0 | |
| [#5305](https://github.com/sebastianbergmann/phpunit/issues/5305) | `MockBuilder::getMockForAbstractClass()` | 10.1.0 | |
| [#5306](https://github.com/sebastianbergmann/phpunit/issues/5306) | `MockBuilder::getMockForTrait()` | 10.1.0 | |
| [#5307](https://github.com/sebastianbergmann/phpunit/issues/5307) | `MockBuilder::disableProxyingToOriginalMethods()` | 10.1.0 | |
| [#5307](https://github.com/sebastianbergmann/phpunit/issues/5307) | `MockBuilder::enableProxyingToOriginalMethods()` | 10.1.0 | |
| [#5307](https://github.com/sebastianbergmann/phpunit/issues/5307) | `MockBuilder::setProxyTarget()` | 10.1.0 | |
| [#5308](https://github.com/sebastianbergmann/phpunit/issues/5308) | `MockBuilder::allowMockingUnknownTypes()` | 10.1.0 | |
| [#5308](https://github.com/sebastianbergmann/phpunit/issues/5308) | `MockBuilder::disallowMockingUnknownTypes()` | 10.1.0 | |
| [#5309](https://github.com/sebastianbergmann/phpunit/issues/5309) | `MockBuilder::disableAutoload()` | 10.1.0 | |
| [#5309](https://github.com/sebastianbergmann/phpunit/issues/5309) | `MockBuilder::enableAutoload()` | 10.1.0 | |
| [#5315](https://github.com/sebastianbergmann/phpunit/issues/5315) | `MockBuilder::disableArgumentCloning()` | 10.1.0 | |
| [#5315](https://github.com/sebastianbergmann/phpunit/issues/5315) | `MockBuilder::enableArgumentCloning()` | 10.1.0 | |
| [#5320](https://github.com/sebastianbergmann/phpunit/issues/5320) | `MockBuilder::addMethods()` | 10.1.0 | |
| [#5415](https://github.com/sebastianbergmann/phpunit/issues/5415) | Support for doubling interfaces (or classes) that have a method named `method` | 11.0.0 | |
| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::onConsecutiveCalls()` | 10.3.0 | Use `$double->willReturn()` instead of `$double->will($this->onConsecutiveCalls())` |
| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnArgument()` | 10.3.0 | Use `$double->willReturnArgument()` instead of `$double->will($this->returnArgument())` |
| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnCallback()` | 10.3.0 | Use `$double->willReturnCallback()` instead of `$double->will($this->returnCallback())` |
| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnSelf()` | 10.3.0 | Use `$double->willReturnSelf()` instead of `$double->will($this->returnSelf())` |
| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnValue()` | 10.3.0 | Use `$double->willReturn()` instead of `$double->will($this->returnValue())` |
| [#5423](https://github.com/sebastianbergmann/phpunit/issues/5423) | `TestCase::returnValueMap()` | 10.3.0 | Use `$double->willReturnMap()` instead of `$double->will($this->returnValueMap())` |
| [#5535](https://github.com/sebastianbergmann/phpunit/issues/5525) | Configuring expectations using `expects()` on test stubs | 11.0.0 | Create a mock object when you need to configure expectations on a test double |
### Running Tests
| Issue | Description | Since | Replacement |
|-------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|--------|----------------------------------------------------------------------------------------------------|
| [#5689](https://github.com/sebastianbergmann/phpunit/issues/5689) | `restrictDeprecations` attribute on the `<source>` element of the XML configuration file | 11.1.0 | Use `ignoreSelfDeprecations`, `ignoreDirectDeprecations`, and `ignoreIndirectDeprecations` instead |
| [#5709](https://github.com/sebastianbergmann/phpunit/issues/5709) | Support for using comma-separated values with the `--group`, `--exclude-group`, `--covers`, `--uses`, and `--test-suffix` CLI options | 11.1.0 | Use `--group foo --group bar` instead of `--group foo,bar`, for example |
#### Miscellaneous
| Issue | Description | Since | Replacement |
|-------------------------------------------------------------------|-------------------------------------------------------------|--------|-----------------------------------------------------------------------------------------|
| [#4505](https://github.com/sebastianbergmann/phpunit/issues/4505) | Metadata in doc-comments | 10.3.0 | Metadata in attributes |
| [#5214](https://github.com/sebastianbergmann/phpunit/issues/5214) | `TestCase::iniSet()` | 10.3.0 | |
| [#5216](https://github.com/sebastianbergmann/phpunit/issues/5216) | `TestCase::setLocale()` | 10.3.0 | |
| [#5800](https://github.com/sebastianbergmann/phpunit/issues/5800) | Targeting traits with `#[CoversClass]` and `#[UsesClass]` | 11.2.0 | `#[CoversClass]` and `#[UsesClass]` also target the traits used by the targeted classes |
| [#5951](https://github.com/sebastianbergmann/phpunit/issues/5951) | `includeUncoveredFiles` configuration option | 11.4.0 | |
## Soft Deprecations
This functionality is currently [soft-deprecated](https://phpunit.de/backward-compatibility.html#soft-deprecation):
### Writing Tests
#### Assertions, Constraints, and Expectations
| Issue | Description | Since | Replacement |
|-------------------------------------------------------------------|-----------------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [#6052](https://github.com/sebastianbergmann/phpunit/issues/6052) | `Assert::isType()` | 11.5.0 | Use `isArray()`, `isBool()`, `isCallable()`, `isFloat()`, `isInt()`, `isIterable()`, `isNull()`, `isNumeric()`, `isObject()`, `isResource()`, `isClosedResource()`, `isScalar()`, or `isString()` instead |

View File

@@ -1,20 +1,14 @@
[![PHPUnit](.github/img/phpunit.svg)](https://phpunit.de/?ref=github)
[![CI Status](https://github.com/sebastianbergmann/phpunit/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/phpunit/actions)
[![codecov](https://codecov.io/gh/sebastianbergmann/phpunit/branch/main/graph/badge.svg?token=0yzBUK8Wri)](https://codecov.io/gh/sebastianbergmann/phpunit)
[![Latest Stable Version](https://poser.pugx.org/phpunit/phpunit/v)](https://packagist.org/packages/phpunit/phpunit)
[![Total Downloads](https://poser.pugx.org/phpunit/phpunit/downloads)](https://packagist.org/packages/phpunit/phpunit/stats)
[![Monthly Downloads](https://poser.pugx.org/phpunit/phpunit/d/monthly)](https://packagist.org/packages/phpunit/phpunit/stats)
[![Daily Downloads](https://poser.pugx.org/phpunit/phpunit/d/daily)](https://packagist.org/packages/phpunit/phpunit/stats)
# PHPUnit
PHPUnit is a programmer-oriented testing framework for PHP.
It is an instance of the xUnit architecture for unit testing frameworks.
[![Latest Stable Version](https://poser.pugx.org/phpunit/phpunit/v)](https://packagist.org/packages/phpunit/phpunit)
[![CI Status](https://github.com/sebastianbergmann/phpunit/workflows/CI/badge.svg)](https://github.com/sebastianbergmann/phpunit/actions)
[![codecov](https://codecov.io/gh/sebastianbergmann/phpunit/branch/main/graph/badge.svg)](https://codecov.io/gh/sebastianbergmann/phpunit)
PHPUnit is a programmer-oriented testing framework for PHP. It is an instance of the xUnit architecture for unit testing frameworks.
## Installation
We distribute a [PHP Archive (PHAR)](https://php.net/phar) that has all required dependencies of PHPUnit bundled in a single file:
We distribute a [PHP Archive (PHAR)](https://php.net/phar) that has all required (as well as some optional) dependencies of PHPUnit bundled in a single file:
```bash
$ wget https://phar.phpunit.de/phpunit-X.Y.phar
@@ -24,86 +18,17 @@ $ php phpunit-X.Y.phar --version
Please replace `X.Y` with the version of PHPUnit you are interested in.
Alternatively, you may use [Composer](https://getcomposer.org/) to download and install PHPUnit as well as its dependencies.
Please refer to the [documentation](https://phpunit.de/documentation.html?ref=github) for details on how to install PHPUnit.
Alternatively, you may use [Composer](https://getcomposer.org/) to download and install PHPUnit as well as its dependencies. Please refer to the [documentation](https://phpunit.de/documentation.html) for details on how to install PHPUnit.
## Contribute
Please refer to [CONTRIBUTING.md](https://github.com/sebastianbergmann/phpunit/blob/main/.github/CONTRIBUTING.md) for information on how to contribute to PHPUnit and its related projects.
A big "Thank you!" to everyone who has contributed to PHPUnit!
You can find a detailed list of contributors on every PHPUnit related package on GitHub.
## List of Contributors
Here is a list of all components that are primarily developed and maintained by [Sebastian Bergmann](https://sebastian-bergmann.de/open-source.html?ref=github):
Thanks to everyone who has contributed to PHPUnit! You can find a detailed list of contributors on every PHPUnit related package on GitHub. This list shows only the major components:
* [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit)
* [phpunit/php-code-coverage](https://github.com/sebastianbergmann/php-code-coverage)
* [phpunit/php-file-iterator](https://github.com/sebastianbergmann/php-file-iterator)
* [phpunit/php-invoker](https://github.com/sebastianbergmann/php-invoker)
* [phpunit/php-text-template](https://github.com/sebastianbergmann/php-text-template)
* [phpunit/php-timer](https://github.com/sebastianbergmann/php-timer)
* [sebastian/cli-parser](https://github.com/sebastianbergmann/cli-parser)
* [sebastian/comparator](https://github.com/sebastianbergmann/comparator)
* [sebastian/complexity](https://github.com/sebastianbergmann/complexity)
* [sebastian/diff](https://github.com/sebastianbergmann/diff)
* [sebastian/environment](https://github.com/sebastianbergmann/environment)
* [sebastian/exporter](https://github.com/sebastianbergmann/exporter)
* [sebastian/global-state](https://github.com/sebastianbergmann/global-state)
* [sebastian/lines-of-code](https://github.com/sebastianbergmann/lines-of-code)
* [sebastian/object-enumerator](https://github.com/sebastianbergmann/object-enumerator)
* [sebastian/object-reflector](https://github.com/sebastianbergmann/object-reflector)
* [sebastian/recursion-context](https://github.com/sebastianbergmann/recursion-context)
* [sebastian/type](https://github.com/sebastianbergmann/type)
* [sebastian/version](https://github.com/sebastianbergmann/version)
* [PHPUnit](https://github.com/sebastianbergmann/phpunit/graphs/contributors)
* [php-code-coverage](https://github.com/sebastianbergmann/php-code-coverage/graphs/contributors)
A very special thanks to everyone who has contributed to the [PHPUnit Manual](https://github.com/sebastianbergmann/phpunit-documentation-english).
In addition to the components listed above, PHPUnit depends on the components listed below:
* [myclabs/deep-copy](https://github.com/myclabs/DeepCopy)
* [nikic/php-parser](https://github.com/nikic/php-parser)
* [phar-io/manifest](https://github.com/phar-io/manifest)
* [phar-io/version](https://github.com/phar-io/version)
* [staabm/side-effects-detector](https://github.com/staabm/side-effects-detector)
* [theseer/tokenizer](https://github.com/theseer/tokenizer)
These tools are used to develop PHPUnit:
* [Composer](https://getcomposer.org/)
* [Phive](https://phar.io/)
* [PHP Autoload Builder](https://github.com/theseer/Autoload/)
* [PHP-CS-Fixer](https://cs.symfony.com/)
* [PHP-Scoper](https://github.com/humbug/php-scoper)
* [PHPStan](https://phpstan.org/)
## Sponsors
It has taken [Sebastian Bergmann](https://sebastian-bergmann.de/open-source.html?ref=github) thousands of hours to develop, test, and support PHPUnit.
[**You can sponsor his Open Source work through GitHub Sponsors**](https://github.com/sponsors/sebastianbergmann), for example.
These businesses support Sebastian Bergmann's work on PHPUnit:
<table>
<tbody>
<tr>
<td style="width: 30%; vertical-align: middle;"><a href="https://www.bubbleshooter.net/"><img alt="Bubble Shooter" src=".github/img/bubble-shooter.png" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://www.in2it.be/phpunit-supporter/"><img alt="in2it vof" src=".github/img/in2it.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://roave.com/"><img alt="Roave" src=".github/img/roave.svg" style="width: 200px;"/></a></td>
</tr>
<tr>
<td style="width: 30%; vertical-align: middle;"><a href="https://route4me.com/"><img alt="Route4Me" src=".github/img/route4me.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://testmo.com/"><img alt="Testmo GmbH" src=".github/img/testmo.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://tideways.com/"><img alt="Tideways GmbH" src=".github/img/tideways.svg" style="width: 200px;"/></a></td>
</tr>
<tr>
<td style="width: 30%; vertical-align: middle;"><a href="https://typo3.com/"><img alt="TYPO3 GmbH" src=".github/img/typo3.svg" style="width: 200px;"/></a></td>
<td style="width: 30%; vertical-align: middle;"><a href="https://vema-eg.de/"><img alt="VEMA Versicherungsmakler Genossenschaft eG" src=".github/img/vema.svg" style="width: 200px;"/></a></td>
</tr>
</tbody>
</table>
Would you like to see your logo here as well as on the [PHPUnit website](https://phpunit.de/sponsors.html?ref=github)?
Contact Sebastian Bergmann at [sponsoring@phpunit.de](mailto:sponsoring@phpunit.de) to learn more about how you can support his work on PHPUnit.
Whether you are a CEO, CFO, CTO, or a developer: your company surely depends on Open Source software.
[It is time to pay your share](https://opensourcepledge.com/) and support maintainers like [Sebastian Bergmann](https://sebastian-bergmann.de/open-source.html?ref=github).
A very special thanks to everyone who has contributed to the [documentation](https://github.com/sebastianbergmann/phpunit-documentation-english/graphs/contributors).

View File

@@ -20,10 +20,9 @@
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy"
},
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.3",
"php": ">=8.2",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
@@ -33,29 +32,33 @@
"myclabs/deep-copy": "^1.12.1",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"phpunit/php-code-coverage": "^12.0-dev",
"phpunit/php-file-iterator": "^6.0-dev",
"phpunit/php-invoker": "^6.0-dev",
"phpunit/php-text-template": "^5.0-dev",
"phpunit/php-timer": "^8.0-dev",
"sebastian/cli-parser": "^4.0-dev",
"sebastian/comparator": "^7.0-dev",
"sebastian/diff": "^7.0-dev",
"sebastian/environment": "^8.0-dev",
"sebastian/exporter": "^7.0-dev",
"sebastian/global-state": "^8.0-dev",
"sebastian/object-enumerator": "^7.0-dev",
"sebastian/type": "^6.0-dev",
"sebastian/version": "^6.0-dev",
"phpunit/php-code-coverage": "^11.0.8",
"phpunit/php-file-iterator": "^5.1.0",
"phpunit/php-invoker": "^5.0.1",
"phpunit/php-text-template": "^4.0.1",
"phpunit/php-timer": "^7.0.1",
"sebastian/cli-parser": "^3.0.2",
"sebastian/code-unit": "^3.0.2",
"sebastian/comparator": "^6.3.0",
"sebastian/diff": "^6.0.2",
"sebastian/environment": "^7.2.0",
"sebastian/exporter": "^6.3.0",
"sebastian/global-state": "^7.0.2",
"sebastian/object-enumerator": "^6.0.1",
"sebastian/type": "^5.1.0",
"sebastian/version": "^5.0.2",
"staabm/side-effects-detector": "^1.0.5"
},
"config": {
"platform": {
"php": "8.3.0"
"php": "8.2.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"suggest": {
"ext-soap": "To be able to generate mocks based on WSDL files"
},
"bin": [
"phpunit"
],
@@ -75,6 +78,7 @@
"tests/_files/deprecation-trigger/trigger_deprecation.php",
"tests/unit/Event/AbstractEventTestCase.php",
"tests/unit/Framework/MockObject/TestDoubleTestCase.php",
"tests/unit/Metadata/Parser/AnnotationParserTestCase.php",
"tests/unit/Metadata/Parser/AttributeParserTestCase.php",
"tests/unit/Framework/Assert/assertContainsOnlyArrayTest.php",
"tests/unit/Framework/Assert/assertContainsOnlyBoolTest.php",
@@ -103,6 +107,7 @@
"tests/unit/Framework/Assert/assertNullTest.php",
"tests/unit/Framework/Assert/assertSameSizeTest.php",
"tests/unit/Framework/Assert/assertSameTest.php",
"tests/_files/CoverageNamespacedFunctionTest.php",
"tests/_files/CoveredFunction.php",
"tests/_files/Generator.php",
"tests/_files/NamespaceCoveredFunction.php"
@@ -110,7 +115,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "12.0-dev"
"dev-main": "11.5-dev"
}
}
}

511
vendor/phpunit/phpunit/composer.lock generated vendored

File diff suppressed because it is too large Load Diff

View File

@@ -24,11 +24,11 @@ if (!version_compare(PHP_VERSION, PHP_VERSION, '=')) {
die(1);
}
if (version_compare('8.3.0', PHP_VERSION, '>')) {
if (version_compare('8.2.0', PHP_VERSION, '>')) {
fwrite(
STDERR,
sprintf(
'This version of PHPUnit requires PHP >= 8.3.' . PHP_EOL .
'This version of PHPUnit requires PHP >= 8.2.' . PHP_EOL .
'You are using PHP %s (%s).' . PHP_EOL,
PHP_VERSION,
PHP_BINARY

View File

@@ -2,7 +2,7 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:annotation>
<xs:documentation source="https://phpunit.de/documentation.html">
This Schema file defines the rules by which the XML configuration file of PHPUnit 12.0 may be structured.
This Schema file defines the rules by which the XML configuration file of PHPUnit 11.5 may be structured.
</xs:documentation>
<xs:appinfo source="https://phpunit.de/documentation.html"/>
</xs:annotation>
@@ -218,7 +218,7 @@
<xs:attribute name="displayDetailsOnTestsThatTriggerErrors" type="xs:boolean" default="false"/>
<xs:attribute name="displayDetailsOnTestsThatTriggerNotices" type="xs:boolean" default="false"/>
<xs:attribute name="displayDetailsOnTestsThatTriggerWarnings" type="xs:boolean" default="false"/>
<xs:attribute name="shortenArraysForExportThreshold" type="xs:integer" default="10"/>
<xs:attribute name="shortenArraysForExportThreshold" type="xs:integer" default="0"/>
</xs:attributeGroup>
<xs:group name="configGroup">
<xs:all>

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