$codeLines
*/
- private function renderBranchLines(array $branch, array $codeLines, array $testData): string
+ private function renderBranchLines(ProcessedBranchCoverageData $branch, array $codeLines, array $testData): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');
$lines = '';
- $branchLines = range($branch['line_start'], $branch['line_end']);
+ $branchLines = range($branch->line_start, $branch->line_end);
sort($branchLines); // sometimes end_line < start_line
/** @var int $line */
@@ -818,7 +819,7 @@ private function renderBranchLines(array $branch, array $codeLines, array $testD
$popoverContent = '';
$popoverTitle = '';
- $numTests = count($branch['hit']);
+ $numTests = count($branch->hit);
if ($numTests === 0) {
$trClass = 'danger';
@@ -832,7 +833,7 @@ private function renderBranchLines(array $branch, array $codeLines, array $testD
$popoverTitle = '1 test covers this branch';
}
- foreach ($branch['hit'] as $test) {
+ foreach ($branch->hit as $test) {
if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') {
$lineCss = 'covered-by-medium-tests';
} elseif ($testData[$test]['size'] === 'small') {
@@ -877,21 +878,18 @@ private function renderPathStructure(FileNode $node): string
ksort($coverageData);
+ /** @var ProcessedFunctionCoverageData $methodData */
foreach ($coverageData as $methodName => $methodData) {
- if (!$methodData['paths']) {
- continue;
- }
-
$pathStructure = '';
- if (count($methodData['paths']) > 100) {
- $pathStructure .= '' . count($methodData['paths']) . ' is too many paths to sensibly render, consider refactoring your code to bring this number down.
';
+ if (count($methodData->paths) > 100) {
+ $pathStructure .= '' . count($methodData->paths) . ' is too many paths to sensibly render, consider refactoring your code to bring this number down.
';
continue;
}
- foreach ($methodData['paths'] as $path) {
- $pathStructure .= $this->renderPathLines($path, $methodData['branches'], $codeLines, $testData);
+ foreach ($methodData->paths as $path) {
+ $pathStructure .= $this->renderPathLines($path, $methodData->branches, $codeLines, $testData);
}
if ($pathStructure !== '') {
@@ -906,9 +904,10 @@ private function renderPathStructure(FileNode $node): string
}
/**
- * @param list $codeLines
+ * @param array $branches
+ * @param list $codeLines
*/
- private function renderPathLines(array $path, array $branches, array $codeLines, array $testData): string
+ private function renderPathLines(ProcessedPathCoverageData $path, array $branches, array $codeLines, array $testData): string
{
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');
@@ -916,14 +915,14 @@ private function renderPathLines(array $path, array $branches, array $codeLines,
$lines = '';
$first = true;
- foreach ($path['path'] as $branchId) {
+ foreach ($path->path as $branchId) {
if ($first) {
$first = false;
} else {
$lines .= ' ' . "\n";
}
- $branchLines = range($branches[$branchId]['line_start'], $branches[$branchId]['line_end']);
+ $branchLines = range($branches[$branchId]->line_start, $branches[$branchId]->line_end);
sort($branchLines); // sometimes end_line < start_line
/** @var int $line */
@@ -935,7 +934,7 @@ private function renderPathLines(array $path, array $branches, array $codeLines,
$popoverContent = '';
$popoverTitle = '';
- $numTests = count($path['hit']);
+ $numTests = count($path->hit);
if ($numTests === 0) {
$trClass = 'danger';
@@ -949,7 +948,7 @@ private function renderPathLines(array $path, array $branches, array $codeLines,
$popoverTitle = '1 test covers this path';
}
- foreach ($path['hit'] as $test) {
+ foreach ($path->hit as $test) {
if ($lineCss === 'covered-by-large-tests' && $testData[$test]['size'] === 'medium') {
$lineCss = 'covered-by-medium-tests';
} elseif ($testData[$test]['size'] === 'small') {
diff --git a/src/Report/Html/Renderer/Template/css/style.css b/src/Report/Html/Renderer/Template/css/style.css
index c0a045889..4303bf844 100644
--- a/src/Report/Html/Renderer/Template/css/style.css
+++ b/src/Report/Html/Renderer/Template/css/style.css
@@ -1,13 +1,86 @@
+
:root {
- --phpunit-breadcrumbs: var(--bs-gray-200);
- --phpunit-success-bar: #28a745;
- --phpunit-success-high: {{success-high}};
- --phpunit-success-medium: {{success-medium}};
- --phpunit-success-low: {{success-low}};
- --phpunit-warning: {{warning}};
- --phpunit-warning-bar: #ffc107;
- --phpunit-danger: {{danger}};
- --phpunit-danger-bar: #dc3545;
+ /* Implementing an auto-selection of dark/light theme via: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark */
+ color-scheme: light dark;
+
+ /* PHPUnit light/dark colors */
+ --phpunit-breadcrumbs: light-dark(var(--bs-gray-200), var(--bs-gray-800));
+ --phpunit-success-bar: light-dark(#28a745 ,#1f8135);
+ --phpunit-success-high: light-dark(#99cb84, #3d5c4e);
+ --phpunit-success-medium: light-dark(#c3e3b5,#3c6051);
+ --phpunit-success-low: light-dark(#dff0d8, #2d4431);
+ --phpunit-warning: light-dark(#fcf8e3, #3e3408);
+ --phpunit-warning-bar: light-dark(#ffc107 ,#c19406);
+ --phpunit-danger: light-dark(#f2dede, #42221e);
+ --phpunit-danger-bar: light-dark(#dc3545, #a62633);
+
+ /* Bootstrap v5.3 default colors (light, dark) */
+ --bs-body-bg-rgb: 255, 255, 255;
+ --bs-body-bg: light-dark(#fff, #212529);
+ --bs-body-color-rgb: light-dark(33, 37, 41, 222, 226, 230);
+ --bs-body-color: light-dark(#212529, #dee2e6);
+ --bs-border-color-translucent: light-dark(rgba(0, 0, 0, 0.175), rgba(255, 255, 255, 0.15));
+ --bs-border-color: light-dark(#dee2e6, #495057);
+ --bs-code-color: light-dark(#d63384, #e685b5);
+ --bs-danger-bg-subtle: light-dark(#f8d7da, #2c0b0e);
+ --bs-danger-border-subtle: light-dark(#f1aeb5, #842029);
+ --bs-danger-text-emphasis: light-dark(#58151c, #ea868f);
+ --bs-dark-bg-subtle: light-dark(#ced4da, #1a1d20);
+ --bs-dark-border-subtle: light-dark(#adb5bd, #343a40);
+ --bs-dark-text-emphasis: light-dark(#495057, #dee2e6);
+ --bs-emphasis-color-rgb: 0, 0, 0;
+ --bs-emphasis-color: light-dark(#000, #fff);
+ --bs-form-invalid-border-color: light-dark(#dc3545, #ea868f);
+ --bs-form-invalid-color: light-dark(#dc3545, #ea868f);
+ --bs-form-valid-border-color: light-dark(#198754, #75b798);
+ --bs-form-valid-color: light-dark(#198754, #75b798);
+ --bs-highlight-bg: light-dark(#fff3cd, #664d03);
+ --bs-highlight-color: light-dark(#212529, #dee2e6);
+ --bs-info-bg-subtle: light-dark(#cff4fc, #032830);
+ --bs-info-border-subtle: light-dark(#9eeaf9, #087990);
+ --bs-info-text-emphasis: light-dark(#055160, #6edff6);
+ --bs-light-bg-subtle: light-dark(#fcfcfd, #343a40);
+ --bs-light-border-subtle: light-dark(#e9ecef, #495057);
+ --bs-light-text-emphasis: light-dark(#495057, #f8f9fa);
+ --bs-link-color-rgb: 13, 110, 253;
+ --bs-link-color: light-dark(#0d6efd, #6ea8fe);
+ --bs-link-hover-color-rgb: 10, 88, 202;
+ --bs-link-hover-color: light-dark(#0a58ca, #8bb9fe);
+ --bs-primary-bg-subtle: light-dark(#cfe2ff, #031633);
+ --bs-primary-border-subtle: light-dark(#9ec5fe, #084298);
+ --bs-primary-text-emphasis: light-dark(#052c65, #6ea8fe);
+ --bs-secondary-bg-rgb: 233, 236, 239;
+ --bs-secondary-bg-subtle: light-dark(#e2e3e5, #161719);
+ --bs-secondary-bg: light-dark(#e9ecef, #343a40);
+ --bs-secondary-border-subtle: light-dark(#c4c8cb, #41464b);
+ --bs-secondary-color-rgb: 33, 37, 41;
+ --bs-secondary-color: light-dark(rgba(33, 37, 41, 0.75), rgba(222, 226, 230, 0.75));
+ --bs-secondary-text-emphasis: light-dark(#2b2f32, #a7acb1);
+ --bs-success-bg-subtle: light-dark(#d1e7dd, #051b11);
+ --bs-success-border-subtle: light-dark(#a3cfbb, #0f5132);
+ --bs-success-text-emphasis: light-dark(#0a3622, #75b798);
+ --bs-tertiary-bg-rgb: light-dark(248, 249, 250, 43, 48, 53);
+ --bs-tertiary-bg: light-dark(#f8f9fa, #2b3035);
+ --bs-tertiary-color-rgb: 33, 37, 41;
+ --bs-tertiary-color: light-dark(rgba(33, 37, 41, 0.5), rgba(222, 226, 230, 0.5));
+ --bs-warning-bg-subtle: light-dark(#fff3cd, #332701);
+ --bs-warning-border-subtle: light-dark(#ffe69c, #997404);
+ --bs-warning-text-emphasis: light-dark(#664d03, #ffda6a);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --bs-body-bg-rgb: 33, 37, 41;
+ --bs-emphasis-color-rgb: 255, 255, 255;
+ --bs-link-color-rgb: 110, 168, 254;
+ --bs-link-hover-color-rgb: 139, 185, 254;
+ --bs-secondary-bg-rgb: 52, 58, 64;
+ --bs-secondary-color-rgb: 222, 226, 230;
+ --bs-tertiary-color-rgb: 222, 226, 230;
+ }
+
+ /* Invert icon's colors on dark mode to improve readability */
+ img.octicon { filter: invert(1); }
}
body {
@@ -198,4 +271,4 @@ table#code td:first-of-type a {
.progress-bar.bg-danger {
background-color: var(--phpunit-danger-bar) !important;
-}
\ No newline at end of file
+}
diff --git a/src/Report/OpenClover.php b/src/Report/OpenClover.php
index 042132b33..3c9a7c3d1 100644
--- a/src/Report/OpenClover.php
+++ b/src/Report/OpenClover.php
@@ -12,13 +12,10 @@
use function assert;
use function basename;
use function count;
-use function dirname;
-use function file_put_contents;
use function is_string;
use function ksort;
use function max;
use function range;
-use function str_contains;
use function str_replace;
use function time;
use DOMDocument;
@@ -26,6 +23,7 @@
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
+use SebastianBergmann\CodeCoverage\Util\Xml;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\CodeCoverage\WriteOperationFailedException;
@@ -80,53 +78,53 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string
$classMethods = 0;
// Assumption: one namespace per file
- if ($class['namespace'] !== '') {
- $namespace = $class['namespace'];
+ if ($class->namespace !== '') {
+ $namespace = $class->namespace;
}
- foreach ($class['methods'] as $methodName => $method) {
+ foreach ($class->methods as $methodName => $method) {
/** @phpstan-ignore equal.notAllowed */
- if ($method['executableLines'] == 0) {
+ if ($method->executableLines == 0) {
continue;
}
$classMethods++;
- $classStatements += $method['executableLines'];
- $coveredClassStatements += $method['executedLines'];
+ $classStatements += $method->executableLines;
+ $coveredClassStatements += $method->executedLines;
/** @phpstan-ignore equal.notAllowed */
- if ($method['coverage'] == 100) {
+ if ($method->coverage == 100) {
$coveredMethods++;
}
$methodCount = 0;
- foreach (range($method['startLine'], $method['endLine']) as $line) {
+ foreach (range($method->startLine, $method->endLine) as $line) {
if (isset($coverageData[$line])) {
$methodCount = max($methodCount, count($coverageData[$line]));
}
}
- $lines[$method['startLine']] = [
- 'ccn' => $method['ccn'],
+ $lines[$method->startLine] = [
+ 'ccn' => $method->ccn,
'count' => $methodCount,
'type' => 'method',
- 'signature' => $method['signature'],
- 'visibility' => $method['visibility'],
+ 'signature' => $method->signature,
+ 'visibility' => $method->visibility,
];
}
$xmlClass = $xmlDocument->createElement('class');
- $xmlClass->setAttribute('name', str_replace($class['namespace'] . '\\', '', $className));
+ $xmlClass->setAttribute('name', str_replace($class->namespace . '\\', '', $className));
$xmlFile->appendChild($xmlClass);
$xmlMetrics = $xmlDocument->createElement('metrics');
- $xmlMetrics->setAttribute('complexity', (string) $class['ccn']);
- $xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class['executableBranches']));
- $xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class['executedBranches']));
- $xmlMetrics->setAttribute('conditionals', (string) $class['executableBranches']);
- $xmlMetrics->setAttribute('coveredconditionals', (string) $class['executedBranches']);
+ $xmlMetrics->setAttribute('complexity', (string) $class->ccn);
+ $xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class->executableBranches));
+ $xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class->executedBranches));
+ $xmlMetrics->setAttribute('conditionals', (string) $class->executableBranches);
+ $xmlMetrics->setAttribute('coveredconditionals', (string) $class->executedBranches);
$xmlMetrics->setAttribute('statements', (string) $classStatements);
$xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements);
$xmlMetrics->setAttribute('methods', (string) $classMethods);
@@ -240,16 +238,10 @@ public function process(CodeCoverage $coverage, ?string $target = null, ?string
$xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods());
$xmlProject->insertBefore($xmlMetrics, $xmlProject->firstChild);
- $buffer = $xmlDocument->saveXML();
+ $buffer = Xml::asString($xmlDocument);
if ($target !== null) {
- if (!str_contains($target, '://')) {
- Filesystem::createDirectory(dirname($target));
- }
-
- if (@file_put_contents($target, $buffer) === false) {
- throw new WriteOperationFailedException($target);
- }
+ Filesystem::write($target, $buffer);
}
return $buffer;
diff --git a/src/Report/PHP.php b/src/Report/PHP.php
index 051f9154e..aa941dc8a 100644
--- a/src/Report/PHP.php
+++ b/src/Report/PHP.php
@@ -10,16 +10,18 @@
namespace SebastianBergmann\CodeCoverage\Report;
use const PHP_EOL;
-use function dirname;
-use function file_put_contents;
use function serialize;
-use function str_contains;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
use SebastianBergmann\CodeCoverage\WriteOperationFailedException;
final class PHP
{
+ /**
+ * @param null|non-empty-string $target
+ *
+ * @throws WriteOperationFailedException
+ */
public function process(CodeCoverage $coverage, ?string $target = null): string
{
$coverage->clearCache();
@@ -28,13 +30,7 @@ public function process(CodeCoverage $coverage, ?string $target = null): string
return \unserialize(<<<'END_OF_COVERAGE_SERIALIZATION'" . PHP_EOL . serialize($coverage) . PHP_EOL . 'END_OF_COVERAGE_SERIALIZATION' . PHP_EOL . ');';
if ($target !== null) {
- if (!str_contains($target, '://')) {
- Filesystem::createDirectory(dirname($target));
- }
-
- if (@file_put_contents($target, $buffer) === false) {
- throw new WriteOperationFailedException($target);
- }
+ Filesystem::write($target, $buffer);
}
return $buffer;
diff --git a/src/Report/Text.php b/src/Report/Text.php
index f18820b70..4c8d70986 100644
--- a/src/Report/Text.php
+++ b/src/Report/Text.php
@@ -190,28 +190,28 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
$coveredMethods = 0;
$classMethods = 0;
- foreach ($class['methods'] as $method) {
+ foreach ($class->methods as $method) {
/** @phpstan-ignore equal.notAllowed */
- if ($method['executableLines'] == 0) {
+ if ($method->executableLines == 0) {
continue;
}
$classMethods++;
- $classExecutableLines += $method['executableLines'];
- $classExecutedLines += $method['executedLines'];
- $classExecutableBranches += $method['executableBranches'];
- $classExecutedBranches += $method['executedBranches'];
- $classExecutablePaths += $method['executablePaths'];
- $classExecutedPaths += $method['executedPaths'];
+ $classExecutableLines += $method->executableLines;
+ $classExecutedLines += $method->executedLines;
+ $classExecutableBranches += $method->executableBranches;
+ $classExecutedBranches += $method->executedBranches;
+ $classExecutablePaths += $method->executablePaths;
+ $classExecutedPaths += $method->executedPaths;
/** @phpstan-ignore equal.notAllowed */
- if ($method['coverage'] == 100) {
+ if ($method->coverage == 100) {
$coveredMethods++;
}
}
$classCoverage[$className] = [
- 'namespace' => $class['namespace'],
+ 'namespace' => $class->namespace,
'className' => $className,
'methodsCovered' => $coveredMethods,
'methodCount' => $classMethods,
diff --git a/src/Report/Xml/BuildInformation.php b/src/Report/Xml/BuildInformation.php
index dba230123..b11fc6ede 100644
--- a/src/Report/Xml/BuildInformation.php
+++ b/src/Report/Xml/BuildInformation.php
@@ -9,74 +9,40 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use function phpversion;
use DateTimeImmutable;
-use DOMElement;
use SebastianBergmann\Environment\Runtime;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class BuildInformation
{
- private DOMElement $contextNode;
-
- public function __construct(DOMElement $contextNode)
- {
- $this->contextNode = $contextNode;
- }
-
- public function setRuntimeInformation(Runtime $runtime): void
- {
- $runtimeNode = $this->nodeByName('runtime');
-
- $runtimeNode->setAttribute('name', $runtime->getName());
- $runtimeNode->setAttribute('version', $runtime->getVersion());
- $runtimeNode->setAttribute('url', $runtime->getVendorUrl());
-
- $driverNode = $this->nodeByName('driver');
-
- if ($runtime->hasXdebug()) {
- $driverNode->setAttribute('name', 'xdebug');
- $driverNode->setAttribute('version', phpversion('xdebug'));
- }
-
- if ($runtime->hasPCOV()) {
- $driverNode->setAttribute('name', 'pcov');
- $driverNode->setAttribute('version', phpversion('pcov'));
- }
- }
-
- public function setBuildTime(DateTimeImmutable $date): void
- {
- $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y'));
- }
-
- public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void
- {
- $this->contextNode->setAttribute('phpunit', $phpUnitVersion);
- $this->contextNode->setAttribute('coverage', $coverageVersion);
- }
-
- private function nodeByName(string $name): DOMElement
- {
- $node = $this->contextNode->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
- $name,
- )->item(0);
-
- if ($node === null) {
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- $name,
- ),
- );
- }
-
- assert($node instanceof DOMElement);
-
- return $node;
+ public function __construct(
+ XMLWriter $xmlWriter,
+ Runtime $runtime,
+ DateTimeImmutable $buildDate,
+ string $phpUnitVersion,
+ string $coverageVersion,
+ string $driverExtensionName,
+ string $driverExtensionVersion,
+ ) {
+ $xmlWriter->startElement('build');
+ $xmlWriter->writeAttribute('time', $buildDate->format('D M j G:i:s T Y'));
+ $xmlWriter->writeAttribute('phpunit', $phpUnitVersion);
+ $xmlWriter->writeAttribute('coverage', $coverageVersion);
+
+ $xmlWriter->startElement('runtime');
+ $xmlWriter->writeAttribute('name', $runtime->getName());
+ $xmlWriter->writeAttribute('version', $runtime->getVersion());
+ $xmlWriter->writeAttribute('url', $runtime->getVendorUrl());
+ $xmlWriter->endElement();
+
+ $xmlWriter->startElement('driver');
+ $xmlWriter->writeAttribute('name', $driverExtensionName);
+ $xmlWriter->writeAttribute('version', $driverExtensionVersion);
+ $xmlWriter->endElement();
+
+ $xmlWriter->endElement();
}
}
diff --git a/src/Report/Xml/Coverage.php b/src/Report/Xml/Coverage.php
index afb70a069..3038eb143 100644
--- a/src/Report/Xml/Coverage.php
+++ b/src/Report/Xml/Coverage.php
@@ -9,8 +9,6 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use DOMElement;
-use SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException;
use XMLWriter;
/**
@@ -18,48 +16,28 @@
*/
final class Coverage
{
- private readonly XMLWriter $writer;
- private readonly DOMElement $contextNode;
- private bool $finalized = false;
-
- public function __construct(DOMElement $context, string $line)
- {
- $this->contextNode = $context;
-
- $this->writer = new XMLWriter;
- $this->writer->openMemory();
- $this->writer->startElementNs(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0');
- $this->writer->writeAttribute('nr', $line);
+ private readonly XMLWriter $xmlWriter;
+ private readonly string $line;
+
+ public function __construct(
+ XMLWriter $xmlWriter,
+ string $line
+ ) {
+ $this->xmlWriter = $xmlWriter;
+ $this->line = $line;
}
- /**
- * @throws ReportAlreadyFinalizedException
- */
- public function addTest(string $test): void
+ public function finalize(array $tests): void
{
- if ($this->finalized) {
- // @codeCoverageIgnoreStart
- throw new ReportAlreadyFinalizedException;
- // @codeCoverageIgnoreEnd
+ $writer = $this->xmlWriter;
+ $writer->startElement('line');
+ $writer->writeAttribute('nr', $this->line);
+
+ foreach ($tests as $test) {
+ $writer->startElement('covered');
+ $writer->writeAttribute('by', $test);
+ $writer->endElement();
}
-
- $this->writer->startElement('covered');
- $this->writer->writeAttribute('by', $test);
- $this->writer->endElement();
- }
-
- public function finalize(): void
- {
- $this->writer->endElement();
-
- $fragment = $this->contextNode->ownerDocument->createDocumentFragment();
- $fragment->appendXML($this->writer->outputMemory());
-
- $this->contextNode->parentNode->replaceChild(
- $fragment,
- $this->contextNode,
- );
-
- $this->finalized = true;
+ $writer->endElement();
}
}
diff --git a/src/Report/Xml/Facade.php b/src/Report/Xml/Facade.php
index ee2e8aa01..05ab96a60 100644
--- a/src/Report/Xml/Facade.php
+++ b/src/Report/Xml/Facade.php
@@ -10,50 +10,48 @@
namespace SebastianBergmann\CodeCoverage\Report\Xml;
use const DIRECTORY_SEPARATOR;
-use const PHP_EOL;
use function count;
use function dirname;
use function file_get_contents;
-use function file_put_contents;
use function is_array;
use function is_dir;
use function is_file;
use function is_writable;
-use function libxml_clear_errors;
-use function libxml_get_errors;
-use function libxml_use_internal_errors;
+use function phpversion;
use function sprintf;
use function strlen;
use function substr;
use DateTimeImmutable;
-use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Data\ProcessedClassType;
+use SebastianBergmann\CodeCoverage\Data\ProcessedFunctionType;
+use SebastianBergmann\CodeCoverage\Data\ProcessedTraitType;
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\PathExistsButIsNotDirectoryException;
-use SebastianBergmann\CodeCoverage\Util\Filesystem as DirectoryUtil;
+use SebastianBergmann\CodeCoverage\Util\Filesystem;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\CodeCoverage\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\XmlException;
use SebastianBergmann\Environment\Runtime;
+use XMLWriter;
/**
- * @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
{
+ public const string XML_NAMESPACE = 'https://schema.phpunit.de/coverage/1.0';
private string $target;
private Project $project;
private readonly string $phpUnitVersion;
+ private readonly bool $includeSource;
- public function __construct(string $version)
+ public function __construct(string $version, bool $includeSource = true)
{
$this->phpUnitVersion = $version;
+ $this->includeSource = $includeSource;
}
/**
@@ -70,23 +68,46 @@ public function process(CodeCoverage $coverage, string $target): void
$report = $coverage->getReport();
+ $writer = new XMLWriter;
+ $writer->openUri($this->targetFilePath('index'));
+ $writer->setIndent(true);
+ $writer->setIndentString(' ');
$this->project = new Project(
+ $writer,
$coverage->getReport()->name(),
);
- $this->setBuildInformation();
+ $this->setBuildInformation($coverage);
+
+ $this->project->startProject();
$this->processTests($coverage->getTests());
$this->processDirectory($report, $this->project);
-
- $this->saveDocument($this->project->asDom(), 'index');
+ $this->project->finalize();
}
- private function setBuildInformation(): void
+ private function setBuildInformation(CodeCoverage $coverage): void
{
- $buildNode = $this->project->buildInformation();
- $buildNode->setRuntimeInformation(new Runtime);
- $buildNode->setBuildTime(new DateTimeImmutable);
- $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id());
+ if ($coverage->driverIsPcov()) {
+ $driverExtensionName = 'pcov';
+ $driverExtensionVersion = phpversion('pcov');
+ } elseif ($coverage->driverIsXdebug()) {
+ $driverExtensionName = 'xdebug';
+ $driverExtensionVersion = phpversion('xdebug');
+ } else {
+ // @codeCoverageIgnoreStart
+ $driverExtensionName = 'unknown';
+ $driverExtensionVersion = 'unknown';
+ // @codeCoverageIgnoreEnd
+ }
+
+ $this->project->buildInformation(
+ new Runtime,
+ new DateTimeImmutable,
+ $this->phpUnitVersion,
+ Version::id(),
+ $driverExtensionName,
+ $driverExtensionVersion,
+ );
}
/**
@@ -107,7 +128,7 @@ private function initTargetDirectory(string $directory): void
// @codeCoverageIgnoreEnd
}
- DirectoryUtil::createDirectory($directory);
+ Filesystem::createDirectory($directory);
}
/**
@@ -121,7 +142,10 @@ private function processDirectory(DirectoryNode $directory, Node $context): void
$directoryName = '/';
}
- $directoryObject = $context->addDirectory($directoryName);
+ $writer = $this->project->getWriter();
+ $writer->startElement('directory');
+ $writer->writeAttribute('name', $directoryName);
+ $directoryObject = $context->addDirectory();
$this->setTotals($directory, $directoryObject->totals());
@@ -132,6 +156,7 @@ private function processDirectory(DirectoryNode $directory, Node $context): void
foreach ($directory->files() as $node) {
$this->processFile($node, $directoryObject);
}
+ $writer->endElement();
}
/**
@@ -139,19 +164,27 @@ private function processDirectory(DirectoryNode $directory, Node $context): void
*/
private function processFile(FileNode $file, Directory $context): void
{
- $fileObject = $context->addFile(
- $file->name(),
- $file->id() . '.xml',
- );
+ $context->getWriter()->startElement('file');
+ $context->getWriter()->writeAttribute('name', $file->name());
+ $context->getWriter()->writeAttribute('href', $file->id() . '.xml');
+ $context->getWriter()->writeAttribute('hash', $file->sha1());
+
+ $fileObject = $context->addFile();
$this->setTotals($file, $fileObject->totals());
+ $context->getWriter()->endElement();
+
$path = substr(
$file->pathAsString(),
strlen($this->project->projectSourceDirectory()),
);
- $fileReport = new Report($path);
+ $writer = new XMLWriter;
+ $writer->openUri($this->targetFilePath($file->id()));
+ $writer->setIndent(true);
+ $writer->setIndentString(' ');
+ $fileReport = new Report($writer, $path, $file->sha1());
$this->setTotals($file, $fileReport->totals());
@@ -163,71 +196,89 @@ private function processFile(FileNode $file, Directory $context): void
$this->processFunction($function, $fileReport);
}
+ $fileReport->getWriter()->startElement('coverage');
+
foreach ($file->lineCoverageData() as $line => $tests) {
if (!is_array($tests) || count($tests) === 0) {
continue;
}
$coverage = $fileReport->lineCoverage((string) $line);
-
- foreach ($tests as $test) {
- $coverage->addTest($test);
- }
-
- $coverage->finalize();
+ $coverage->finalize($tests);
}
+ $fileReport->getWriter()->endElement();
- $fileReport->source()->setSourceCode(
- file_get_contents($file->pathAsString()),
- );
+ if ($this->includeSource) {
+ $fileReport->source()->setSourceCode(
+ file_get_contents($file->pathAsString()),
+ );
+ }
- $this->saveDocument($fileReport->asDom(), $file->id());
+ $fileReport->finalize();
}
- /**
- * @param ProcessedClassType|ProcessedTraitType $unit
- */
- private function processUnit(array $unit, Report $report): void
+ private function processUnit(ProcessedClassType|ProcessedTraitType $unit, Report $report): void
{
- if (isset($unit['className'])) {
- $unitObject = $report->classObject($unit['className']);
+ if ($unit instanceof ProcessedClassType) {
+ $report->getWriter()->startElement('class');
+
+ $unitObject = $report->classObject(
+ $unit->className,
+ $unit->namespace,
+ $unit->startLine,
+ $unit->executableLines,
+ $unit->executedLines,
+ (float) $unit->crap,
+ );
} else {
- $unitObject = $report->traitObject($unit['traitName']);
+ $report->getWriter()->startElement('trait');
+
+ $unitObject = $report->traitObject(
+ $unit->traitName,
+ $unit->namespace,
+ $unit->startLine,
+ $unit->executableLines,
+ $unit->executedLines,
+ (float) $unit->crap,
+ );
}
- $unitObject->setLines(
- $unit['startLine'],
- $unit['executableLines'],
- $unit['executedLines'],
- );
-
- $unitObject->setCrap((float) $unit['crap']);
- $unitObject->setNamespace($unit['namespace']);
-
- foreach ($unit['methods'] as $method) {
- $methodObject = $unitObject->addMethod($method['methodName']);
- $methodObject->setSignature($method['signature']);
- $methodObject->setLines((string) $method['startLine'], (string) $method['endLine']);
- $methodObject->setCrap($method['crap']);
- $methodObject->setTotals(
- (string) $method['executableLines'],
- (string) $method['executedLines'],
- (string) $method['coverage'],
+ foreach ($unit->methods as $method) {
+ $report->getWriter()->startElement('method');
+
+ $unitObject->addMethod(
+ $method->methodName,
+ $method->signature,
+ (string) $method->startLine,
+ (string) $method->endLine,
+ (string) $method->executableLines,
+ (string) $method->executedLines,
+ (string) $method->coverage,
+ $method->crap,
);
+
+ $report->getWriter()->endElement();
}
+
+ $report->getWriter()->endElement();
}
- /**
- * @param ProcessedFunctionType $function
- */
- private function processFunction(array $function, Report $report): void
+ private function processFunction(ProcessedFunctionType $function, Report $report): void
{
- $functionObject = $report->functionObject($function['functionName']);
+ $report->getWriter()->startElement('function');
+
+ $report->functionObject(
+ $function->functionName,
+ $function->signature,
+ (string) $function->startLine,
+ null,
+ (string) $function->executableLines,
+ (string) $function->executedLines,
+ (string) $function->coverage,
+ $function->crap,
+ );
- $functionObject->setSignature($function['signature']);
- $functionObject->setLines((string) $function['startLine']);
- $functionObject->setCrap($function['crap']);
- $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']);
+ $report->getWriter()->endElement();
}
/**
@@ -235,15 +286,21 @@ private function processFunction(array $function, Report $report): void
*/
private function processTests(array $tests): void
{
+ $this->project->getWriter()->startElement('tests');
+
$testsObject = $this->project->tests();
foreach ($tests as $test => $result) {
$testsObject->addTest($test, $result);
}
+
+ $this->project->getWriter()->endElement();
}
private function setTotals(AbstractNode $node, Totals $totals): void
{
+ $totals->getWriter()->startElement('totals');
+
$loc = $node->linesOfCode();
$totals->setNumLines(
@@ -254,6 +311,16 @@ private function setTotals(AbstractNode $node, Totals $totals): void
$node->numberOfExecutedLines(),
);
+ $totals->setNumMethods(
+ $node->numberOfMethods(),
+ $node->numberOfTestedMethods(),
+ );
+
+ $totals->setNumFunctions(
+ $node->numberOfFunctions(),
+ $node->numberOfTestedFunctions(),
+ );
+
$totals->setNumClasses(
$node->numberOfClasses(),
$node->numberOfTestedClasses(),
@@ -264,15 +331,7 @@ private function setTotals(AbstractNode $node, Totals $totals): void
$node->numberOfTestedTraits(),
);
- $totals->setNumMethods(
- $node->numberOfMethods(),
- $node->numberOfTestedMethods(),
- );
-
- $totals->setNumFunctions(
- $node->numberOfFunctions(),
- $node->numberOfTestedFunctions(),
- );
+ $totals->getWriter()->endElement();
}
private function targetDirectory(): string
@@ -280,45 +339,12 @@ private function targetDirectory(): string
return $this->target;
}
- /**
- * @throws XmlException
- */
- private function saveDocument(DOMDocument $document, string $name): void
+ private function targetFilePath(string $name): string
{
$filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name);
- $document->formatOutput = true;
- $document->preserveWhiteSpace = false;
$this->initTargetDirectory(dirname($filename));
- file_put_contents($filename, $this->documentAsString($document));
- }
-
- /**
- * @throws XmlException
- *
- * @see https://bugs.php.net/bug.php?id=79191
- */
- private function documentAsString(DOMDocument $document): string
- {
- $xmlErrorHandling = libxml_use_internal_errors(true);
- $xml = $document->saveXML();
-
- if ($xml === false) {
- // @codeCoverageIgnoreStart
- $message = 'Unable to generate the XML';
-
- foreach (libxml_get_errors() as $error) {
- $message .= PHP_EOL . $error->message;
- }
-
- throw new XmlException($message);
- // @codeCoverageIgnoreEnd
- }
-
- libxml_clear_errors();
- libxml_use_internal_errors($xmlErrorHandling);
-
- return $xml;
+ return $filename;
}
}
diff --git a/src/Report/Xml/File.php b/src/Report/Xml/File.php
index 4a3fea008..2d35582a8 100644
--- a/src/Report/Xml/File.php
+++ b/src/Report/Xml/File.php
@@ -9,77 +9,32 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMDocument;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
class File
{
- private readonly DOMDocument $dom;
- private readonly DOMElement $contextNode;
+ protected XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->dom = $context->ownerDocument;
- $this->contextNode = $context;
+ $this->xmlWriter = $xmlWriter;
}
- public function totals(): Totals
- {
- $totalsContainer = $this->contextNode->firstChild;
-
- if ($totalsContainer === null) {
- $totalsContainer = $this->contextNode->appendChild(
- $this->dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'totals',
- ),
- );
- }
-
- assert($totalsContainer instanceof DOMElement);
-
- return new Totals($totalsContainer);
- }
-
- public function lineCoverage(string $line): Coverage
+ public function getWriter(): XMLWriter
{
- $coverage = $this->contextNode->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'coverage',
- )->item(0);
-
- if ($coverage === null) {
- $coverage = $this->contextNode->appendChild(
- $this->dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'coverage',
- ),
- );
- }
-
- $lineNode = $coverage->appendChild(
- $this->dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'line',
- ),
- );
-
- assert($lineNode instanceof DOMElement);
-
- return new Coverage($lineNode, $line);
+ return $this->xmlWriter;
}
- protected function contextNode(): DOMElement
+ public function totals(): Totals
{
- return $this->contextNode;
+ return new Totals($this->xmlWriter);
}
- protected function dom(): DOMDocument
+ public function lineCoverage(string $line): Coverage
{
- return $this->dom;
+ return new Coverage($this->xmlWriter, $line);
}
}
diff --git a/src/Report/Xml/Method.php b/src/Report/Xml/Method.php
index 1994d0f79..965ad5259 100644
--- a/src/Report/Xml/Method.php
+++ b/src/Report/Xml/Method.php
@@ -9,50 +9,41 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Method
{
- private DOMElement $contextNode;
-
- public function __construct(DOMElement $context, string $name)
- {
- $this->contextNode = $context;
-
- $this->setName($name);
- }
-
- public function setSignature(string $signature): void
- {
- $this->contextNode->setAttribute('signature', $signature);
- }
-
- public function setLines(string $start, ?string $end = null): void
- {
- $this->contextNode->setAttribute('start', $start);
+ private XMLWriter $xmlWriter;
+
+ public function __construct(
+ XMLWriter $xmlWriter,
+ string $name,
+ string $signature,
+ string $start,
+ ?string $end,
+ string $executable,
+ string $executed,
+ string $coverage,
+ string $crap
+ ) {
+ $this->xmlWriter = $xmlWriter;
+
+ $this->xmlWriter->writeAttribute('name', $name);
+ $this->xmlWriter->writeAttribute('signature', $signature);
+
+ $this->xmlWriter->writeAttribute('start', $start);
if ($end !== null) {
- $this->contextNode->setAttribute('end', $end);
+ $this->xmlWriter->writeAttribute('end', $end);
}
- }
- public function setTotals(string $executable, string $executed, string $coverage): void
- {
- $this->contextNode->setAttribute('executable', $executable);
- $this->contextNode->setAttribute('executed', $executed);
- $this->contextNode->setAttribute('coverage', $coverage);
- }
-
- public function setCrap(string $crap): void
- {
- $this->contextNode->setAttribute('crap', $crap);
- }
+ $this->xmlWriter->writeAttribute('crap', $crap);
- private function setName(string $name): void
- {
- $this->contextNode->setAttribute('name', $name);
+ $this->xmlWriter->writeAttribute('executable', $executable);
+ $this->xmlWriter->writeAttribute('executed', $executed);
+ $this->xmlWriter->writeAttribute('coverage', $coverage);
}
}
diff --git a/src/Report/Xml/Node.php b/src/Report/Xml/Node.php
index e41197a08..36b75bcfe 100644
--- a/src/Report/Xml/Node.php
+++ b/src/Report/Xml/Node.php
@@ -9,81 +9,37 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMDocument;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
abstract class Node
{
- private DOMDocument $dom;
- private DOMElement $contextNode;
+ protected readonly XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->setContextNode($context);
- }
-
- public function dom(): DOMDocument
- {
- return $this->dom;
+ $this->xmlWriter = $xmlWriter;
}
public function totals(): Totals
{
- $totalsContainer = $this->contextNode()->firstChild;
-
- if ($totalsContainer === null) {
- $totalsContainer = $this->contextNode()->appendChild(
- $this->dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'totals',
- ),
- );
- }
-
- assert($totalsContainer instanceof DOMElement);
-
- return new Totals($totalsContainer);
+ return new Totals($this->xmlWriter);
}
- public function addDirectory(string $name): Directory
+ public function addDirectory(): Directory
{
- $dirNode = $this->dom()->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'directory',
- );
-
- $dirNode->setAttribute('name', $name);
- $this->contextNode()->appendChild($dirNode);
-
- return new Directory($dirNode);
- }
-
- public function addFile(string $name, string $href): File
- {
- $fileNode = $this->dom()->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'file',
- );
-
- $fileNode->setAttribute('name', $name);
- $fileNode->setAttribute('href', $href);
- $this->contextNode()->appendChild($fileNode);
-
- return new File($fileNode);
+ return new Directory($this->xmlWriter);
}
- protected function setContextNode(DOMElement $context): void
+ public function addFile(): File
{
- $this->dom = $context->ownerDocument;
- $this->contextNode = $context;
+ return new File($this->xmlWriter);
}
- protected function contextNode(): DOMElement
+ public function getWriter(): XMLWriter
{
- return $this->contextNode;
+ return $this->xmlWriter;
}
}
diff --git a/src/Report/Xml/Project.php b/src/Report/Xml/Project.php
index 21b5a2ce1..e21addb56 100644
--- a/src/Report/Xml/Project.php
+++ b/src/Report/Xml/Project.php
@@ -9,91 +9,73 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMDocument;
-use DOMElement;
+use DateTimeImmutable;
+use SebastianBergmann\Environment\Runtime;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Project extends Node
{
- /**
- * @phpstan-ignore constructor.missingParentCall
- */
- public function __construct(string $directory)
+ private readonly string $directory;
+
+ public function __construct(XMLWriter $xmlWriter, string $directory)
{
- $this->init();
- $this->setProjectSourceDirectory($directory);
+ $this->directory = $directory;
+
+ parent::__construct($xmlWriter);
+
+ $this->xmlWriter->startDocument();
+
+ $this->xmlWriter->startElement('phpunit');
+ $this->xmlWriter->writeAttribute('xmlns', Facade::XML_NAMESPACE);
}
public function projectSourceDirectory(): string
{
- return $this->contextNode()->getAttribute('source');
+ return $this->directory;
}
- public function buildInformation(): BuildInformation
- {
- $buildNode = $this->dom()->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'build',
- )->item(0);
-
- if ($buildNode === null) {
- $buildNode = $this->dom()->documentElement->appendChild(
- $this->dom()->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'build',
- ),
- );
- }
-
- assert($buildNode instanceof DOMElement);
-
- return new BuildInformation($buildNode);
+ public function buildInformation(
+ Runtime $runtime,
+ DateTimeImmutable $buildDate,
+ string $phpUnitVersion,
+ string $coverageVersion,
+ string $driverExtensionName,
+ string $driverExtensionVersion,
+ ): void {
+ new BuildInformation(
+ $this->xmlWriter,
+ $runtime,
+ $buildDate,
+ $phpUnitVersion,
+ $coverageVersion,
+ $driverExtensionName,
+ $driverExtensionVersion,
+ );
}
public function tests(): Tests
{
- $testsNode = $this->contextNode()->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'tests',
- )->item(0);
-
- if ($testsNode === null) {
- $testsNode = $this->contextNode()->appendChild(
- $this->dom()->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'tests',
- ),
- );
- }
-
- assert($testsNode instanceof DOMElement);
-
- return new Tests($testsNode);
+ return new Tests($this->xmlWriter);
}
- public function asDom(): DOMDocument
+ public function getWriter(): XMLWriter
{
- return $this->dom();
+ return $this->xmlWriter;
}
- private function init(): void
+ public function startProject(): void
{
- $dom = new DOMDocument;
- $dom->loadXML(' ');
-
- $this->setContextNode(
- $dom->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'project',
- )->item(0),
- );
+ $this->xmlWriter->startElement('project');
+ $this->xmlWriter->writeAttribute('source', $this->directory);
}
- private function setProjectSourceDirectory(string $name): void
+ public function finalize(): void
{
- $this->contextNode()->setAttribute('source', $name);
+ $this->xmlWriter->endElement();
+ $this->xmlWriter->endDocument();
+ $this->xmlWriter->flush();
}
}
diff --git a/src/Report/Xml/Report.php b/src/Report/Xml/Report.php
index f39ab860c..46d4dc0ed 100644
--- a/src/Report/Xml/Report.php
+++ b/src/Report/Xml/Report.php
@@ -9,99 +9,100 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
use function basename;
use function dirname;
use DOMDocument;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Report extends File
{
- public function __construct(string $name)
+ private readonly string $name;
+ private readonly string $sha1;
+
+ public function __construct(XMLWriter $xmlWriter, string $name, string $sha1)
{
+ /*
$dom = new DOMDocument;
$dom->loadXML(' ');
$contextNode = $dom->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
+ Facade::XML_NAMESPACE,
'file',
)->item(0);
-
- parent::__construct($contextNode);
-
- $this->setName($name);
+*/
+ parent::__construct($xmlWriter);
+
+ $this->name = $name;
+ $this->sha1 = $sha1;
+
+ $xmlWriter->startDocument();
+ $xmlWriter->startElement('phpunit');
+ $xmlWriter->writeAttribute('xmlns', Facade::XML_NAMESPACE);
+ $xmlWriter->startElement('file');
+ $xmlWriter->writeAttribute('name', basename($this->name));
+ $xmlWriter->writeAttribute('path', dirname($this->name));
+ $xmlWriter->writeAttribute('hash', $this->sha1);
}
- public function asDom(): DOMDocument
+ public function finalize(): void
{
- return $this->dom();
+ $this->xmlWriter->endElement();
+ $this->xmlWriter->endElement();
+
+ $this->xmlWriter->endDocument();
+ $this->xmlWriter->flush();
}
- public function functionObject(string $name): Method
- {
- $node = $this->contextNode()->appendChild(
- $this->dom()->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'function',
- ),
+ public function functionObject(
+ string $name,
+ string $signature,
+ string $start,
+ ?string $end,
+ string $executable,
+ string $executed,
+ string $coverage,
+ string $crap
+ ): void {
+ new Method(
+ $this->xmlWriter,
+ $name,
+ $signature,
+ $start,
+ $end,
+ $executable,
+ $executed,
+ $coverage,
+ $crap,
);
-
- assert($node instanceof DOMElement);
-
- return new Method($node, $name);
}
- public function classObject(string $name): Unit
- {
- return $this->unitObject('class', $name);
+ public function classObject(
+ string $name,
+ string $namespace,
+ int $start,
+ int $executable,
+ int $executed,
+ float $crap
+ ): Unit {
+ return new Unit($this->xmlWriter, $name, $namespace, $start, $executable, $executed, $crap);
}
- public function traitObject(string $name): Unit
- {
- return $this->unitObject('trait', $name);
+ public function traitObject(
+ string $name,
+ string $namespace,
+ int $start,
+ int $executable,
+ int $executed,
+ float $crap
+ ): Unit {
+ return new Unit($this->xmlWriter, $name, $namespace, $start, $executable, $executed, $crap);
}
public function source(): Source
{
- $source = $this->contextNode()->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'source',
- )->item(0);
-
- if ($source === null) {
- $source = $this->contextNode()->appendChild(
- $this->dom()->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'source',
- ),
- );
- }
-
- assert($source instanceof DOMElement);
-
- return new Source($source);
- }
-
- private function setName(string $name): void
- {
- $this->contextNode()->setAttribute('name', basename($name));
- $this->contextNode()->setAttribute('path', dirname($name));
- }
-
- private function unitObject(string $tagName, string $name): Unit
- {
- $node = $this->contextNode()->appendChild(
- $this->dom()->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- $tagName,
- ),
- );
-
- assert($node instanceof DOMElement);
-
- return new Unit($node, $name);
+ return new Source($this->xmlWriter);
}
}
diff --git a/src/Report/Xml/Source.php b/src/Report/Xml/Source.php
index 448fe72d6..e82b2c382 100644
--- a/src/Report/Xml/Source.php
+++ b/src/Report/Xml/Source.php
@@ -9,33 +9,26 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use DOMElement;
use TheSeer\Tokenizer\NamespaceUri;
use TheSeer\Tokenizer\Tokenizer;
use TheSeer\Tokenizer\XMLSerializer;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Source
{
- private DOMElement $context;
+ private XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->context = $context;
+ $this->xmlWriter = $xmlWriter;
}
public function setSourceCode(string $source): void
{
- $context = $this->context;
-
$tokens = (new Tokenizer)->parse($source);
- $srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens);
-
- $context->parentNode->replaceChild(
- $context->ownerDocument->importNode($srcDom->documentElement, true),
- $context,
- );
+ (new XMLSerializer(new NamespaceUri(Facade::XML_NAMESPACE)))->appendToWriter($this->xmlWriter, $tokens);
}
}
diff --git a/src/Report/Xml/Tests.php b/src/Report/Xml/Tests.php
index c9e9c48ef..fcb6bb7cc 100644
--- a/src/Report/Xml/Tests.php
+++ b/src/Report/Xml/Tests.php
@@ -9,9 +9,9 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMElement;
+use function sprintf;
use SebastianBergmann\CodeCoverage\CodeCoverage;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -20,11 +20,11 @@
*/
final readonly class Tests
{
- private DOMElement $contextNode;
+ private readonly XMLWriter $xmlWriter;
- public function __construct(DOMElement $context)
+ public function __construct(XMLWriter $xmlWriter)
{
- $this->contextNode = $context;
+ $this->xmlWriter = $xmlWriter;
}
/**
@@ -32,17 +32,13 @@ public function __construct(DOMElement $context)
*/
public function addTest(string $test, array $result): void
{
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'test',
- ),
- );
+ $this->xmlWriter->startElement('test');
- assert($node instanceof DOMElement);
+ $this->xmlWriter->writeAttribute('name', $test);
+ $this->xmlWriter->writeAttribute('size', $result['size']);
+ $this->xmlWriter->writeAttribute('status', $result['status']);
+ $this->xmlWriter->writeAttribute('time', sprintf('%F', $result['time']));
- $node->setAttribute('name', $test);
- $node->setAttribute('size', $result['size']);
- $node->setAttribute('status', $result['status']);
+ $this->xmlWriter->endElement();
}
}
diff --git a/src/Report/Xml/Totals.php b/src/Report/Xml/Totals.php
index 8e285a78e..b0c57ec30 100644
--- a/src/Report/Xml/Totals.php
+++ b/src/Report/Xml/Totals.php
@@ -10,106 +10,86 @@
namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function sprintf;
-use DOMElement;
use SebastianBergmann\CodeCoverage\Util\Percentage;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Totals
{
- private DOMElement $linesNode;
- private DOMElement $methodsNode;
- private DOMElement $functionsNode;
- private DOMElement $classesNode;
- private DOMElement $traitsNode;
+ private XMLWriter $xmlWriter;
- public function __construct(DOMElement $container)
+ public function __construct(XMLWriter $xmlWriter)
{
- $dom = $container->ownerDocument;
-
- $this->linesNode = $dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'lines',
- );
-
- $this->methodsNode = $dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'methods',
- );
-
- $this->functionsNode = $dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'functions',
- );
-
- $this->classesNode = $dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'classes',
- );
-
- $this->traitsNode = $dom->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'traits',
- );
-
- $container->appendChild($this->linesNode);
- $container->appendChild($this->methodsNode);
- $container->appendChild($this->functionsNode);
- $container->appendChild($this->classesNode);
- $container->appendChild($this->traitsNode);
+ $this->xmlWriter = $xmlWriter;
}
public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void
{
- $this->linesNode->setAttribute('total', (string) $loc);
- $this->linesNode->setAttribute('comments', (string) $cloc);
- $this->linesNode->setAttribute('code', (string) $ncloc);
- $this->linesNode->setAttribute('executable', (string) $executable);
- $this->linesNode->setAttribute('executed', (string) $executed);
- $this->linesNode->setAttribute(
+ $this->xmlWriter->startElement('lines');
+ $this->xmlWriter->writeAttribute('total', (string) $loc);
+ $this->xmlWriter->writeAttribute('comments', (string) $cloc);
+ $this->xmlWriter->writeAttribute('code', (string) $ncloc);
+ $this->xmlWriter->writeAttribute('executable', (string) $executable);
+ $this->xmlWriter->writeAttribute('executed', (string) $executed);
+ $this->xmlWriter->writeAttribute(
'percent',
$executable === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumClasses(int $count, int $tested): void
{
- $this->classesNode->setAttribute('count', (string) $count);
- $this->classesNode->setAttribute('tested', (string) $tested);
- $this->classesNode->setAttribute(
+ $this->xmlWriter->startElement('classes');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumTraits(int $count, int $tested): void
{
- $this->traitsNode->setAttribute('count', (string) $count);
- $this->traitsNode->setAttribute('tested', (string) $tested);
- $this->traitsNode->setAttribute(
+ $this->xmlWriter->startElement('traits');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumMethods(int $count, int $tested): void
{
- $this->methodsNode->setAttribute('count', (string) $count);
- $this->methodsNode->setAttribute('tested', (string) $tested);
- $this->methodsNode->setAttribute(
+ $this->xmlWriter->startElement('methods');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
}
public function setNumFunctions(int $count, int $tested): void
{
- $this->functionsNode->setAttribute('count', (string) $count);
- $this->functionsNode->setAttribute('tested', (string) $tested);
- $this->functionsNode->setAttribute(
+ $this->xmlWriter->startElement('functions');
+ $this->xmlWriter->writeAttribute('count', (string) $count);
+ $this->xmlWriter->writeAttribute('tested', (string) $tested);
+ $this->xmlWriter->writeAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
+ $this->xmlWriter->endElement();
+ }
+
+ public function getWriter(): XMLWriter
+ {
+ return $this->xmlWriter;
}
}
diff --git a/src/Report/Xml/Unit.php b/src/Report/Xml/Unit.php
index a00f85d39..bfc5029c4 100644
--- a/src/Report/Xml/Unit.php
+++ b/src/Report/Xml/Unit.php
@@ -9,72 +9,57 @@
*/
namespace SebastianBergmann\CodeCoverage\Report\Xml;
-use function assert;
-use DOMElement;
+use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final readonly class Unit
{
- private DOMElement $contextNode;
+ private XMLWriter $xmlWriter;
- public function __construct(DOMElement $context, string $name)
- {
- $this->contextNode = $context;
+ public function __construct(
+ XMLWriter $xmlWriter,
+ string $name,
+ string $namespace,
+ int $start,
+ int $executable,
+ int $executed,
+ float $crap
+ ) {
+ $this->xmlWriter = $xmlWriter;
- $this->setName($name);
- }
-
- public function setLines(int $start, int $executable, int $executed): void
- {
- $this->contextNode->setAttribute('start', (string) $start);
- $this->contextNode->setAttribute('executable', (string) $executable);
- $this->contextNode->setAttribute('executed', (string) $executed);
- }
-
- public function setCrap(float $crap): void
- {
- $this->contextNode->setAttribute('crap', (string) $crap);
- }
-
- public function setNamespace(string $namespace): void
- {
- $node = $this->contextNode->getElementsByTagNameNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'namespace',
- )->item(0);
+ $this->xmlWriter->writeAttribute('name', $name);
+ $this->xmlWriter->writeAttribute('start', (string) $start);
+ $this->xmlWriter->writeAttribute('executable', (string) $executable);
+ $this->xmlWriter->writeAttribute('executed', (string) $executed);
+ $this->xmlWriter->writeAttribute('crap', (string) $crap);
- if ($node === null) {
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'namespace',
- ),
- );
- }
-
- assert($node instanceof DOMElement);
-
- $node->setAttribute('name', $namespace);
+ $this->xmlWriter->startElement('namespace');
+ $this->xmlWriter->writeAttribute('name', $namespace);
+ $this->xmlWriter->endElement();
}
- public function addMethod(string $name): Method
- {
- $node = $this->contextNode->appendChild(
- $this->contextNode->ownerDocument->createElementNS(
- 'https://schema.phpunit.de/coverage/1.0',
- 'method',
- ),
+ public function addMethod(
+ string $name,
+ string $signature,
+ string $start,
+ ?string $end,
+ string $executable,
+ string $executed,
+ string $coverage,
+ string $crap
+ ): void {
+ new Method(
+ $this->xmlWriter,
+ $name,
+ $signature,
+ $start,
+ $end,
+ $executable,
+ $executed,
+ $coverage,
+ $crap,
);
-
- assert($node instanceof DOMElement);
-
- return new Method($node, $name);
- }
-
- private function setName(string $name): void
- {
- $this->contextNode->setAttribute('name', $name);
}
}
diff --git a/src/Target/MapBuilder.php b/src/Target/MapBuilder.php
index be859e2eb..8e8d93fbb 100644
--- a/src/Target/MapBuilder.php
+++ b/src/Target/MapBuilder.php
@@ -11,7 +11,6 @@
use function array_keys;
use function array_merge;
-use function array_merge_recursive;
use function array_slice;
use function array_unique;
use function count;
@@ -72,20 +71,7 @@ public function build(Filter $filter, FileAnalyser $analyser): array
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],
- ),
- );
+ $this->mergeLines($trait->namespacedName(), $traits[$traitName], $traits);
}
}
}
@@ -109,20 +95,7 @@ public function build(Filter $filter, FileAnalyser $analyser): array
continue;
}
- foreach ($traits[$traitName] as $traitFile => $lines) {
- if (!isset($classes[$class->namespacedName()][$traitFile])) {
- $classes[$class->namespacedName()][$traitFile] = $lines;
-
- continue;
- }
-
- $classes[$class->namespacedName()][$traitFile] = array_unique(
- array_merge(
- $classes[$class->namespacedName()][$traitFile],
- $lines,
- ),
- );
- }
+ $this->mergeLines($class->namespacedName(), $traits[$traitName], $classes);
}
$this->processMethods($class, $file, $methods, $reverseLookup);
@@ -144,8 +117,8 @@ public function build(Filter $filter, FileAnalyser $analyser): array
}
}
- foreach (array_keys($namespaces) as $namespace) {
- foreach (array_keys($namespaces[$namespace]) as $file) {
+ foreach ($namespaces as $namespace => $files) {
+ foreach (array_keys($files) as $file) {
$namespaces[$namespace][$file] = array_unique($namespaces[$namespace][$file]);
}
}
@@ -160,10 +133,7 @@ public function build(Filter $filter, FileAnalyser $analyser): array
}
foreach ($this->parentClasses($classDetails, $class) as $parentClass) {
- $classes[$class->namespacedName()] = array_merge_recursive(
- $classes[$class->namespacedName()],
- $classes[$parentClass->namespacedName()],
- );
+ $this->mergeLines($class->namespacedName(), $classes[$parentClass->namespacedName()], $classes);
if (isset($classesThatExtendClass[$parentClass->namespacedName()])) {
$this->process($classesThatExtendClass, $parentClass->namespacedName(), $class->file(), $class->startLine(), $class->endLine());
@@ -187,15 +157,6 @@ public function build(Filter $filter, FileAnalyser $analyser): array
unset($classesThatExtendClass[$className]);
}
- /**
- * @todo Avoid duplication and remove this loop
- */
- foreach (array_keys($classes) as $className) {
- foreach (array_keys($classes[$className]) as $file) {
- $classes[$className][$file] = array_unique($classes[$className][$file]);
- }
- }
-
return [
'namespaces' => $namespaces,
'traits' => $traits,
@@ -208,6 +169,32 @@ public function build(Filter $filter, FileAnalyser $analyser): array
];
}
+ private function mergeLines(string $targetClass, array $sourceData, array &$data): void
+ {
+ /**
+ * In large inheritance trees we might handle a lot of data.
+ * This loop needs to prevent unnecessary work whenever possible.
+ */
+ foreach ($sourceData as $file => $lines) {
+ if (!isset($data[$targetClass][$file])) {
+ $data[$targetClass][$file] = $lines;
+
+ continue;
+ }
+
+ if ($data[$targetClass][$file] === $lines) {
+ continue;
+ }
+
+ $data[$targetClass][$file] = array_unique(
+ array_merge(
+ $data[$targetClass][$file],
+ $lines,
+ ),
+ );
+ }
+ }
+
private function processMethods(Class_|Trait_ $classOrTrait, string $file, array &$methods, array &$reverseLookup): void
{
foreach ($classOrTrait->methods() as $method) {
diff --git a/src/Util/Filesystem.php b/src/Util/Filesystem.php
index 0e99b1593..f73388ae2 100644
--- a/src/Util/Filesystem.php
+++ b/src/Util/Filesystem.php
@@ -9,9 +9,13 @@
*/
namespace SebastianBergmann\CodeCoverage\Util;
+use function dirname;
+use function file_put_contents;
use function is_dir;
use function mkdir;
use function sprintf;
+use function str_contains;
+use SebastianBergmann\CodeCoverage\WriteOperationFailedException;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -34,4 +38,20 @@ public static function createDirectory(string $directory): void
);
}
}
+
+ /**
+ * @param non-empty-string $target
+ *
+ * @throws WriteOperationFailedException
+ */
+ public static function write(string $target, string $buffer): void
+ {
+ if (!str_contains($target, '://')) {
+ self::createDirectory(dirname($target));
+ }
+
+ if (@file_put_contents($target, $buffer) === false) {
+ throw new WriteOperationFailedException($target);
+ }
+ }
}
diff --git a/src/Util/Xml.php b/src/Util/Xml.php
new file mode 100644
index 000000000..de958a4b2
--- /dev/null
+++ b/src/Util/Xml.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\Util;
+
+use const PHP_EOL;
+use function libxml_clear_errors;
+use function libxml_get_errors;
+use function libxml_use_internal_errors;
+use DOMDocument;
+use SebastianBergmann\CodeCoverage\XmlException;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final readonly class Xml
+{
+ /**
+ * @throws XmlException
+ *
+ * @see https://bugs.php.net/bug.php?id=79191
+ */
+ public static function asString(DOMDocument $document): string
+ {
+ $xmlErrorHandling = libxml_use_internal_errors(true);
+
+ $document->formatOutput = true;
+ $document->preserveWhiteSpace = false;
+
+ $buffer = $document->saveXML();
+
+ if ($buffer === false) {
+ $message = 'Unable to generate the XML';
+
+ foreach (libxml_get_errors() as $error) {
+ $message .= PHP_EOL . $error->message;
+ }
+
+ throw new XmlException($message);
+ }
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($xmlErrorHandling);
+
+ return $buffer;
+ }
+}
diff --git a/src/Version.php b/src/Version.php
index fe7c20d6c..e8dd97314 100644
--- a/src/Version.php
+++ b/src/Version.php
@@ -19,7 +19,7 @@ final class Version
public static function id(): string
{
if (self::$version === '') {
- self::$version = (new VersionId('12.3.2', dirname(__DIR__)))->asString();
+ self::$version = (new VersionId('12.5.1', dirname(__DIR__)))->asString();
}
return self::$version;
diff --git a/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml
index b49cdf8ed..a5b9ede6e 100644
--- a/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml
+++ b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml
@@ -1,6 +1,6 @@
-
+
@@ -35,7 +35,7 @@
-
+
<?php
diff --git a/tests/_files/Report/XML/CoverageForBankAccount/index.xml b/tests/_files/Report/XML/CoverageForBankAccount/index.xml
index 6e551a135..6c70139ff 100644
--- a/tests/_files/Report/XML/CoverageForBankAccount/index.xml
+++ b/tests/_files/Report/XML/CoverageForBankAccount/index.xml
@@ -6,10 +6,10 @@
-
-
-
-
+
+
+
+
@@ -19,7 +19,7 @@
-
+
diff --git a/tests/_files/Report/XML/CoverageForBankAccountWithoutSource/BankAccount.php.xml b/tests/_files/Report/XML/CoverageForBankAccountWithoutSource/BankAccount.php.xml
new file mode 100644
index 000000000..b7f0473d5
--- /dev/null
+++ b/tests/_files/Report/XML/CoverageForBankAccountWithoutSource/BankAccount.php.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/_files/Report/XML/CoverageForBankAccountWithoutSource/index.xml b/tests/_files/Report/XML/CoverageForBankAccountWithoutSource/index.xml
new file mode 100644
index 000000000..6c70139ff
--- /dev/null
+++ b/tests/_files/Report/XML/CoverageForBankAccountWithoutSource/index.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/index.xml b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/index.xml
index 3bfba4dda..cd6610ead 100644
--- a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/index.xml
+++ b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/index.xml
@@ -6,7 +6,7 @@
-
+
@@ -16,7 +16,7 @@
-
+
diff --git a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml
index 95d121dc8..88523d37c 100644
--- a/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml
+++ b/tests/_files/Report/XML/CoverageForClassWithAnonymousFunction/source_with_class_and_anonymous_function.php.xml
@@ -1,6 +1,6 @@
-
+
@@ -38,7 +38,7 @@
-
+
<?php
diff --git a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/index.xml b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/index.xml
index a53b12c53..ff32be810 100644
--- a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/index.xml
+++ b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/index.xml
@@ -6,7 +6,7 @@
-
+
@@ -16,7 +16,7 @@
-
+
diff --git a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml
index b862f3d1e..f5559b09e 100644
--- a/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml
+++ b/tests/_files/Report/XML/CoverageForFileWithIgnoredLines/source_with_ignore.php.xml
@@ -1,6 +1,6 @@
-
+
@@ -22,7 +22,7 @@
-
+
<?php
diff --git a/tests/src/TestCase.php b/tests/src/TestCase.php
index aff94901c..c086f62af 100644
--- a/tests/src/TestCase.php
+++ b/tests/src/TestCase.php
@@ -14,6 +14,9 @@
use BankAccount;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
+use SebastianBergmann\CodeCoverage\Data\ProcessedBranchCoverageData;
+use SebastianBergmann\CodeCoverage\Data\ProcessedFunctionCoverageData;
+use SebastianBergmann\CodeCoverage\Data\ProcessedPathCoverageData;
use SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\Test\Target\Target;
@@ -1006,7 +1009,8 @@ protected function getLineCoverageForBankAccount(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -1028,6 +1032,7 @@ protected function getLineCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'getBalance'),
],
),
+ time: 0.1,
);
$coverage->start(
@@ -1042,6 +1047,7 @@ protected function getLineCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 0.2,
);
$coverage->start(
@@ -1056,6 +1062,7 @@ protected function getLineCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'depositMoney'),
],
),
+ time: 0.3,
);
$coverage->start(
@@ -1072,6 +1079,7 @@ protected function getLineCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 0.4,
);
return $coverage;
@@ -1083,9 +1091,8 @@ protected function getPathCoverageForBankAccount(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('collectsBranchAndPathCoverage')->willReturn(true);
-
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -1093,6 +1100,8 @@ protected function getPathCoverageForBankAccount(): CodeCoverage
$coverage = new CodeCoverage($stub, $filter);
+ $coverage->enableBranchAndPathCoverage();
+
$coverage->start(
'BankAccountTest::testBalanceIsInitiallyZero',
null,
@@ -1107,6 +1116,7 @@ protected function getPathCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'getBalance'),
],
),
+ time: 0.5,
);
$coverage->start(
@@ -1121,6 +1131,7 @@ protected function getPathCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 0.6,
);
$coverage->start(
@@ -1135,6 +1146,7 @@ protected function getPathCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'depositMoney'),
],
),
+ time: 0.7,
);
$coverage->start(
@@ -1151,6 +1163,7 @@ protected function getPathCoverageForBankAccount(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 0.8,
);
return $coverage;
@@ -1162,9 +1175,8 @@ protected function getPathCoverageForSourceWithoutNamespace(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('collectsBranchAndPathCoverage')->willReturn(true);
-
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -1172,6 +1184,8 @@ protected function getPathCoverageForSourceWithoutNamespace(): CodeCoverage
$coverage = new CodeCoverage($stub, $filter);
+ $coverage->enableBranchAndPathCoverage();
+
$coverage->start(
'faketest',
null,
@@ -1241,7 +1255,8 @@ protected function getLineCoverageForNamespacedBankAccount(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -1273,6 +1288,7 @@ protected function getLineCoverageForNamespacedBankAccount(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccountTrait::class, 'withdrawMoney'),
]),
+ time: 0.9,
);
$coverage->start(
@@ -1285,6 +1301,7 @@ protected function getLineCoverageForNamespacedBankAccount(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccountTrait::class, 'depositMoney'),
]),
+ time: 1.0,
);
$coverage->start(
@@ -1299,6 +1316,7 @@ protected function getLineCoverageForNamespacedBankAccount(): CodeCoverage
Target::forMethod(BankAccountTrait::class, 'depositMoney'),
Target::forMethod(BankAccountTrait::class, 'withdrawMoney'),
]),
+ time: 1.1,
);
return $coverage;
@@ -1310,7 +1328,8 @@ protected function getLineCoverageForBankAccountForFirstTwoTests(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -1332,6 +1351,7 @@ protected function getLineCoverageForBankAccountForFirstTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'getBalance'),
],
),
+ time: 1.2,
);
$coverage->start(
@@ -1346,6 +1366,7 @@ protected function getLineCoverageForBankAccountForFirstTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 1.3,
);
return $coverage;
@@ -1357,7 +1378,8 @@ protected function getLineCoverageForBankAccountForLastTwoTests(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn($data[2], $data[3]);
$filter = new Filter;
@@ -1377,6 +1399,7 @@ protected function getLineCoverageForBankAccountForLastTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'depositMoney'),
],
),
+ time: 1.4,
);
$coverage->start(
@@ -1393,6 +1416,7 @@ protected function getLineCoverageForBankAccountForLastTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 1.5,
);
return $coverage;
@@ -1464,7 +1488,8 @@ protected function getPathCoverageForBankAccountForFirstTwoTests(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -1472,6 +1497,8 @@ protected function getPathCoverageForBankAccountForFirstTwoTests(): CodeCoverage
$coverage = new CodeCoverage($stub, $filter);
+ $coverage->enableBranchAndPathCoverage();
+
$coverage->start(
'BankAccountTest::testBalanceIsInitiallyZero',
null,
@@ -1486,6 +1513,7 @@ protected function getPathCoverageForBankAccountForFirstTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'getBalance'),
],
),
+ time: 1.6,
);
$coverage->start(
@@ -1500,6 +1528,7 @@ protected function getPathCoverageForBankAccountForFirstTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 1.7,
);
return $coverage;
@@ -1511,7 +1540,8 @@ protected function getPathCoverageForBankAccountForLastTwoTests(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn($data[2], $data[3]);
$filter = new Filter;
@@ -1519,6 +1549,8 @@ protected function getPathCoverageForBankAccountForLastTwoTests(): CodeCoverage
$coverage = new CodeCoverage($stub, $filter);
+ $coverage->enableBranchAndPathCoverage();
+
$coverage->start(
'BankAccountTest::testBalanceCannotBecomeNegative2',
);
@@ -1531,6 +1563,7 @@ protected function getPathCoverageForBankAccountForLastTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'depositMoney'),
],
),
+ time: 1.8,
);
$coverage->start(
@@ -1547,6 +1580,7 @@ protected function getPathCoverageForBankAccountForLastTwoTests(): CodeCoverage
Target::forMethod(BankAccount::class, 'withdrawMoney'),
],
),
+ time: 1.9,
);
return $coverage;
@@ -1556,201 +1590,199 @@ protected function getExpectedPathCoverageDataArrayForBankAccount(): array
{
return [
TEST_FILES_PATH . 'BankAccount.php' => [
- 'BankAccount->depositMoney' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 14,
- 'line_start' => 20,
- 'line_end' => 25,
- 'hit' => [
+ 'BankAccount->depositMoney' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 14,
+ 20,
+ 25,
+ [
0 => 'BankAccountTest::testBalanceCannotBecomeNegative2',
1 => 'BankAccountTest::testDepositWithdrawMoney',
],
- 'out' => [
- ],
- 'out_hit' => [
- ],
- ],
+ [],
+ [],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
],
- 'hit' => [
+ [
0 => 'BankAccountTest::testBalanceCannotBecomeNegative2',
1 => 'BankAccountTest::testDepositWithdrawMoney',
],
- ],
+ ),
],
- ],
- 'BankAccount->getBalance' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 5,
- 'line_start' => 6,
- 'line_end' => 9,
- 'hit' => [
+ ),
+ 'BankAccount->getBalance' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 5,
+ 6,
+ 9,
+ [
0 => 'BankAccountTest::testBalanceIsInitiallyZero',
1 => 'BankAccountTest::testDepositWithdrawMoney',
],
- 'out' => [
+ [
],
- 'out_hit' => [
+ [
],
- ],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
],
- 'hit' => [
+ [
0 => 'BankAccountTest::testBalanceIsInitiallyZero',
1 => 'BankAccountTest::testDepositWithdrawMoney',
],
- ],
+ ),
],
- ],
- 'BankAccount->withdrawMoney' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 14,
- 'line_start' => 27,
- 'line_end' => 32,
- 'hit' => [
+ ),
+ 'BankAccount->withdrawMoney' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 14,
+ 27,
+ 32,
+ [
0 => 'BankAccountTest::testBalanceCannotBecomeNegative',
1 => 'BankAccountTest::testDepositWithdrawMoney',
],
- 'out' => [
+ [
],
- 'out_hit' => [
+ [
],
- ],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
],
- 'hit' => [
+ [
0 => 'BankAccountTest::testBalanceCannotBecomeNegative',
1 => 'BankAccountTest::testDepositWithdrawMoney',
],
- ],
+ ),
],
- ],
- '{main}' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 1,
- 'line_start' => 34,
- 'line_end' => 34,
- 'hit' => [
- ],
- 'out' => [
+ ),
+ '{main}' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 1,
+ 34,
+ 34,
+ [
+ ],
+ [
0 => 2147483645,
],
- 'out_hit' => [
+ [
0 => 0,
],
- ],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
],
- 'hit' => [
+ [
],
- ],
+ ),
],
- ],
- 'BankAccount->setBalance' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 4,
- 'line_start' => 11,
- 'line_end' => 13,
- 'hit' => [
- ],
- 'out' => [
+ ),
+ 'BankAccount->setBalance' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 4,
+ 11,
+ 13,
+ [
+ ],
+ [
0 => 5,
1 => 9,
],
- 'out_hit' => [
+ [
0 => 0,
1 => 0,
],
- ],
- 5 => [
- 'op_start' => 5,
- 'op_end' => 8,
- 'line_start' => 14,
- 'line_end' => 14,
- 'hit' => [
- ],
- 'out' => [
+ ),
+ 5 => new ProcessedBranchCoverageData(
+ 5,
+ 8,
+ 14,
+ 14,
+ [
+ ],
+ [
0 => 13,
],
- 'out_hit' => [
+ [
0 => 0,
],
- ],
- 9 => [
- 'op_start' => 9,
- 'op_end' => 12,
- 'line_start' => 16,
- 'line_end' => 16,
- 'hit' => [
- ],
- 'out' => [
+ ),
+ 9 => new ProcessedBranchCoverageData(
+ 9,
+ 12,
+ 16,
+ 16,
+ [
+ ],
+ [
0 => 2147483645,
],
- 'out_hit' => [
+ [
0 => 0,
],
- ],
- 13 => [
- 'op_start' => 13,
- 'op_end' => 14,
- 'line_start' => 18,
- 'line_end' => 18,
- 'hit' => [
- ],
- 'out' => [
+ ),
+ 13 => new ProcessedBranchCoverageData(
+ 13,
+ 14,
+ 18,
+ 18,
+ [
+ ],
+ [
0 => 2147483645,
],
- 'out_hit' => [
+ [
0 => 0,
],
- ],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
1 => 5,
2 => 13,
],
- 'hit' => [
+ [
],
- ],
- 1 => [
- 'path' => [
+ ),
+ 1 => new ProcessedPathCoverageData(
+ [
0 => 0,
1 => 9,
],
- 'hit' => [
+ [
],
- ],
+ ),
],
- ],
+ ),
],
];
}
@@ -1775,7 +1807,8 @@ protected function setUpXdebugStubForFileWithIgnoredLines(): Driver
{
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage(
[
TEST_FILES_PATH . 'source_with_ignore.php' => [
@@ -1809,7 +1842,8 @@ protected function setUpXdebugStubForFileWithEval(): Driver
{
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage(
[
TEST_FILES_PATH . 'source_with_eval.php' => [
@@ -1845,7 +1879,8 @@ protected function setUpXdebugStubForClassWithAnonymousFunction(): Driver
{
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage(
[
TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php' => [
@@ -1885,7 +1920,8 @@ protected function setUpXdebugStubForClassWithOutsideFunction(): Driver
{
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(RawCodeCoverageData::fromXdebugWithoutPathCoverage(
[
TEST_FILES_PATH . 'source_with_class_and_outside_function.php' => [
@@ -1920,7 +1956,8 @@ protected function getCoverageForFilesWithUncoveredIncluded(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -1942,6 +1979,7 @@ protected function getCoverageForFilesWithUncoveredIncluded(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccount::class, 'getBalance'),
]),
+ time: 2.0,
);
$coverage->start(
@@ -1954,6 +1992,7 @@ protected function getCoverageForFilesWithUncoveredIncluded(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccount::class, 'withdrawMoney'),
]),
+ time: 2.1,
);
$coverage->start(
@@ -1966,6 +2005,7 @@ protected function getCoverageForFilesWithUncoveredIncluded(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccount::class, 'depositMoney'),
]),
+ time: 2.2,
);
$coverage->start(
@@ -1980,6 +2020,7 @@ protected function getCoverageForFilesWithUncoveredIncluded(): CodeCoverage
Target::forMethod(BankAccount::class, 'depositMoney'),
Target::forMethod(BankAccount::class, 'withdrawMoney'),
]),
+ time: 2.3,
);
return $coverage;
@@ -1991,7 +2032,8 @@ protected function getCoverageForFilesWithUncoveredExcluded(): CodeCoverage
$stub = $this->createStub(Driver::class);
- $stub->method('stop')
+ $stub
+ ->method('stop')
->willReturn(...$data);
$filter = new Filter;
@@ -2013,6 +2055,7 @@ protected function getCoverageForFilesWithUncoveredExcluded(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccount::class, 'getBalance'),
]),
+ time: 2.4,
);
$coverage->start(
@@ -2025,6 +2068,7 @@ protected function getCoverageForFilesWithUncoveredExcluded(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccount::class, 'withdrawMoney'),
]),
+ time: 2.5,
);
$coverage->start(
@@ -2037,6 +2081,7 @@ protected function getCoverageForFilesWithUncoveredExcluded(): CodeCoverage
TargetCollection::fromArray([
Target::forMethod(BankAccount::class, 'depositMoney'),
]),
+ time: 2.6,
);
$coverage->start(
@@ -2051,6 +2096,7 @@ protected function getCoverageForFilesWithUncoveredExcluded(): CodeCoverage
Target::forMethod(BankAccount::class, 'depositMoney'),
Target::forMethod(BankAccount::class, 'withdrawMoney'),
]),
+ time: 2.7,
);
return $coverage;
diff --git a/tests/tests/CodeCoverageTest.php b/tests/tests/CodeCoverageTest.php
index 32f2f1de0..895be4588 100644
--- a/tests/tests/CodeCoverageTest.php
+++ b/tests/tests/CodeCoverageTest.php
@@ -48,10 +48,10 @@ public function testCollect(): void
$this->assertEquals(
[
- 'BankAccountTest::testBalanceIsInitiallyZero' => ['size' => 'unknown', 'status' => 'unknown'],
- 'BankAccountTest::testBalanceCannotBecomeNegative' => ['size' => 'unknown', 'status' => 'unknown'],
- 'BankAccountTest::testBalanceCannotBecomeNegative2' => ['size' => 'unknown', 'status' => 'unknown'],
- 'BankAccountTest::testDepositWithdrawMoney' => ['size' => 'unknown', 'status' => 'unknown'],
+ 'BankAccountTest::testBalanceIsInitiallyZero' => ['size' => 'unknown', 'status' => 'unknown', 'time' => 0.1],
+ 'BankAccountTest::testBalanceCannotBecomeNegative' => ['size' => 'unknown', 'status' => 'unknown', 'time' => 0.2],
+ 'BankAccountTest::testBalanceCannotBecomeNegative2' => ['size' => 'unknown', 'status' => 'unknown', 'time' => 0.3],
+ 'BankAccountTest::testDepositWithdrawMoney' => ['size' => 'unknown', 'status' => 'unknown', 'time' => 0.4],
],
$coverage->getTests(),
);
diff --git a/tests/tests/Data/ProcessedCodeCoverageDataTest.php b/tests/tests/Data/ProcessedCodeCoverageDataTest.php
index 46e144eec..e903636b8 100644
--- a/tests/tests/Data/ProcessedCodeCoverageDataTest.php
+++ b/tests/tests/Data/ProcessedCodeCoverageDataTest.php
@@ -75,29 +75,27 @@ public function testMergeDoesNotCrashWhenFileContentsHaveChanged(): void
$coverage->setFunctionCoverage(
[
'/some/path/SomeClass.php' => [
- 'SomeClass->firstFunction' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 14,
- 'line_start' => 20,
- 'line_end' => 25,
- 'hit' => [],
- 'out' => [
- ],
- 'out_hit' => [
- ],
- ],
+ 'SomeClass->firstFunction' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 14,
+ 20,
+ 25,
+ [],
+ [],
+ [],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
],
- 'hit' => [],
- ],
+ [],
+ ),
],
- ],
+ ),
],
],
);
@@ -106,75 +104,70 @@ public function testMergeDoesNotCrashWhenFileContentsHaveChanged(): void
$newCoverage->setFunctionCoverage(
[
'/some/path/SomeClass.php' => [
- 'SomeClass->firstFunction' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 14,
- 'line_start' => 20,
- 'line_end' => 25,
- 'hit' => [],
- 'out' => [
- ],
- 'out_hit' => [
- ],
- ],
- 1 => [
- 'op_start' => 15,
- 'op_end' => 16,
- 'line_start' => 26,
- 'line_end' => 27,
- 'hit' => [],
- 'out' => [
- ],
- 'out_hit' => [
- ],
- ],
+ 'SomeClass->firstFunction' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 14,
+ 20,
+ 25,
+ [],
+ [],
+ [],
+ ),
+ 1 => new ProcessedBranchCoverageData(
+ 15,
+ 16,
+ 26,
+ 27,
+ [],
+ [],
+ [],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
],
- 'hit' => [],
- ],
- 1 => [
- 'path' => [
+ [],
+ ),
+ 1 => new ProcessedPathCoverageData(
+ [
0 => 1,
],
- 'hit' => [],
- ],
+ [],
+ ),
],
- ],
- 'SomeClass->secondFunction' => [
- 'branches' => [
- 0 => [
- 'op_start' => 0,
- 'op_end' => 24,
- 'line_start' => 30,
- 'line_end' => 35,
- 'hit' => [],
- 'out' => [
- ],
- 'out_hit' => [
- ],
- ],
+ ),
+ 'SomeClass->secondFunction' => new ProcessedFunctionCoverageData(
+ [
+ 0 => new ProcessedBranchCoverageData(
+ 0,
+ 24,
+ 30,
+ 35,
+ [],
+ [],
+ [],
+ ),
],
- 'paths' => [
- 0 => [
- 'path' => [
+ [
+ 0 => new ProcessedPathCoverageData(
+ [
0 => 0,
],
- 'hit' => [],
- ],
+ [],
+ ),
],
- ],
+ ),
],
],
);
$coverage->merge($newCoverage);
+ $this->assertIsArray($newCoverage->functionCoverage()['/some/path/SomeClass.php']);
$this->assertArrayHasKey('SomeClass->secondFunction', $newCoverage->functionCoverage()['/some/path/SomeClass.php']);
}
}
diff --git a/tests/tests/Data/RawCodeCoverageDataTest.php b/tests/tests/Data/RawCodeCoverageDataTest.php
index 98c4f45cb..7ec012acc 100644
--- a/tests/tests/Data/RawCodeCoverageDataTest.php
+++ b/tests/tests/Data/RawCodeCoverageDataTest.php
@@ -327,6 +327,8 @@ public function testCoverageForFileWithInlineAnnotations(): void
],
);
+ $coverage->skipEmptyLines();
+
$this->assertEquals(
[
13 => -1,
diff --git a/tests/tests/Exception/UnintentionallyCoveredCodeExceptionTest.php b/tests/tests/Exception/UnintentionallyCoveredCodeExceptionTest.php
index 5cafd013a..2853ac4ff 100644
--- a/tests/tests/Exception/UnintentionallyCoveredCodeExceptionTest.php
+++ b/tests/tests/Exception/UnintentionallyCoveredCodeExceptionTest.php
@@ -43,7 +43,6 @@ public function testCanConstructWithNonEmptyArray(): void
- foo
- bar
- baz
-
TXT;
$this->assertSame($expected, $exception->getMessage());
diff --git a/tests/tests/Report/PhpTest.php b/tests/tests/Report/PhpTest.php
index 358e588a7..ad67df576 100644
--- a/tests/tests/Report/PhpTest.php
+++ b/tests/tests/Report/PhpTest.php
@@ -10,14 +10,13 @@
namespace SebastianBergmann\CodeCoverage\Report;
use ReflectionProperty;
+use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\TestCase;
final class PhpTest extends TestCase
{
protected function tearDown(): void
{
- parent::tearDown();
-
$this->removeTemporaryFiles();
}
@@ -30,7 +29,9 @@ public function testPHPSerialisationProducesValidCode(): void
$unserialized = require TEST_FILES_PATH . 'tmp/serialized.php';
- $this->assertEquals($coverage, $unserialized);
+ $this->assertInstanceOf(CodeCoverage::class, $unserialized);
+ $this->assertEquals($coverage->getData(), $unserialized->getData());
+ $this->assertEquals($coverage->getTests(), $unserialized->getTests());
}
public function testPHPSerialisationProducesValidCodeWhenOutputIncludesSingleQuote(): void
@@ -42,7 +43,9 @@ public function testPHPSerialisationProducesValidCodeWhenOutputIncludesSingleQuo
$unserialized = require TEST_FILES_PATH . 'tmp/serialized.php';
- $this->assertEquals($coverage, $unserialized);
+ $this->assertInstanceOf(CodeCoverage::class, $unserialized);
+ $this->assertEquals($coverage->getData(), $unserialized->getData());
+ $this->assertEquals($coverage->getTests(), $unserialized->getTests());
}
public function testCacheDataNeverGetSaved(): void
diff --git a/tests/tests/Report/XmlTest.php b/tests/tests/Report/XmlTest.php
index 53deb784e..5fe031f09 100644
--- a/tests/tests/Report/XmlTest.php
+++ b/tests/tests/Report/XmlTest.php
@@ -47,6 +47,16 @@ public function testForBankAccountTest(): void
$this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp');
}
+ public function testForBankAccountTestWithoutSource(): void
+ {
+ $expectedFilesPath = self::$TEST_REPORT_PATH_SOURCE . DIRECTORY_SEPARATOR . 'CoverageForBankAccountWithoutSource';
+
+ $xml = new Facade('1.0.0', false);
+ $xml->process($this->getLineCoverageForBankAccount(), TEST_FILES_PATH . 'tmp');
+
+ $this->assertFilesEquals($expectedFilesPath, TEST_FILES_PATH . 'tmp');
+ }
+
public function testForFileWithIgnoredLines(): void
{
$expectedFilesPath = self::$TEST_REPORT_PATH_SOURCE . DIRECTORY_SEPARATOR . 'CoverageForFileWithIgnoredLines';
diff --git a/tests/tests/Target/MapBuilderTest.php b/tests/tests/Target/MapBuilderTest.php
index 0c03682b4..49c1c0ed6 100644
--- a/tests/tests/Target/MapBuilderTest.php
+++ b/tests/tests/Target/MapBuilderTest.php
@@ -418,6 +418,10 @@ public static function provider(): array
];
}
+ /**
+ * @param TargetMap $expected,
+ * @param non-empty-list $files
+ */
#[DataProvider('provider')]
public function testBuildsMap(array $expected, array $files): void
{
diff --git a/tools/.phpstan/composer.json b/tools/.phpstan/composer.json
index 37e97680a..8e13d1359 100644
--- a/tools/.phpstan/composer.json
+++ b/tools/.phpstan/composer.json
@@ -1,10 +1,10 @@
{
"require-dev": {
- "phpstan/phpstan": "^2.1.21",
+ "phpstan/phpstan": "^2.1.33",
"phpstan/extension-installer": "^1.4.3",
- "phpstan/phpstan-strict-rules": "^2.0.6",
- "tomasvotruba/type-coverage": "^2.0.2",
- "ergebnis/phpstan-rules": "^2.10.5"
+ "phpstan/phpstan-strict-rules": "^2.0.7",
+ "tomasvotruba/type-coverage": "^2.1.0",
+ "ergebnis/phpstan-rules": "^2.12.0"
},
"config": {
"allow-plugins": {
diff --git a/tools/.phpstan/composer.lock b/tools/.phpstan/composer.lock
index bac49621d..c99f4b27d 100644
--- a/tools/.phpstan/composer.lock
+++ b/tools/.phpstan/composer.lock
@@ -4,26 +4,26 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "21a2ca88ab1be049930ddca65ab02f99",
+ "content-hash": "4409cdc49212f7e878b14b7ae9285bb2",
"packages": [],
"packages-dev": [
{
"name": "ergebnis/phpstan-rules",
- "version": "2.10.5",
+ "version": "2.12.0",
"source": {
"type": "git",
"url": "https://github.com/ergebnis/phpstan-rules.git",
- "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78"
+ "reference": "c4e0121a937b3b551f800a86e7d78794da2783ea"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/719a53e793e2417da46d497f21fb8d2f007ecb78",
- "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78",
+ "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/c4e0121a937b3b551f800a86e7d78794da2783ea",
+ "reference": "c4e0121a937b3b551f800a86e7d78794da2783ea",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
- "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
+ "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"phpstan/phpstan": "^2.1.8"
},
"require-dev": {
@@ -31,14 +31,13 @@
"doctrine/orm": "^2.20.0 || ^3.3.0",
"ergebnis/composer-normalize": "^2.47.0",
"ergebnis/license": "^2.6.0",
- "ergebnis/php-cs-fixer-config": "^6.46.0",
- "ergebnis/phpunit-slow-test-detector": "^2.19.1",
+ "ergebnis/php-cs-fixer-config": "^6.54.0",
+ "ergebnis/phpunit-slow-test-detector": "^2.20.0",
"fakerphp/faker": "^1.24.1",
- "nette/di": "^3.1.10",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan-deprecation-rules": "^2.0.3",
- "phpstan/phpstan-phpunit": "^2.0.6",
- "phpstan/phpstan-strict-rules": "^2.0.4",
+ "phpstan/phpstan-phpunit": "^2.0.7",
+ "phpstan/phpstan-strict-rules": "^2.0.6",
"phpunit/phpunit": "^9.6.21",
"psr/container": "^2.0.2",
"symfony/finder": "^5.4.45",
@@ -79,33 +78,33 @@
"security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md",
"source": "https://github.com/ergebnis/phpstan-rules"
},
- "time": "2025-06-23T17:35:58+00:00"
+ "time": "2025-09-07T13:31:33+00:00"
},
{
"name": "nette/utils",
- "version": "v4.0.7",
+ "version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
- "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2"
+ "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2",
- "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2",
+ "url": "https://api.github.com/repos/nette/utils/zipball/fa1f0b8261ed150447979eb22e373b7b7ad5a8e0",
+ "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0",
"shasum": ""
},
"require": {
- "php": "8.0 - 8.4"
+ "php": "8.2 - 8.5"
},
"conflict": {
"nette/finder": "<3",
"nette/schema": "<1.2.2"
},
"require-dev": {
- "jetbrains/phpstorm-attributes": "dev-master",
+ "jetbrains/phpstorm-attributes": "^1.2",
"nette/tester": "^2.5",
- "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-nette": "^2.0@stable",
"tracy/tracy": "^2.9"
},
"suggest": {
@@ -119,10 +118,13 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.0-dev"
+ "dev-master": "4.1-dev"
}
},
"autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
"classmap": [
"src/"
]
@@ -163,9 +165,9 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
- "source": "https://github.com/nette/utils/tree/v4.0.7"
+ "source": "https://github.com/nette/utils/tree/v4.1.0"
},
- "time": "2025-06-03T04:55:08+00:00"
+ "time": "2025-12-01T17:49:23+00:00"
},
{
"name": "phpstan/extension-installer",
@@ -217,16 +219,11 @@
},
{
"name": "phpstan/phpstan",
- "version": "2.1.21",
- "source": {
- "type": "git",
- "url": "https://github.com/phpstan/phpstan.git",
- "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6"
- },
+ "version": "2.1.33",
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ccf445757458c06a04eb3f803603cb118fe5fa6",
- "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
+ "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
"shasum": ""
},
"require": {
@@ -271,25 +268,25 @@
"type": "github"
}
],
- "time": "2025-07-28T19:35:08+00:00"
+ "time": "2025-12-05T10:24:31+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
- "version": "2.0.6",
+ "version": "2.0.7",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
- "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094"
+ "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094",
- "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/d6211c46213d4181054b3d77b10a5c5cb0d59538",
+ "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
- "phpstan/phpstan": "^2.0.4"
+ "phpstan/phpstan": "^2.1.29"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -317,22 +314,22 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
- "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6"
+ "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.7"
},
- "time": "2025-07-21T12:19:29+00:00"
+ "time": "2025-09-26T11:19:08+00:00"
},
{
"name": "tomasvotruba/type-coverage",
- "version": "2.0.2",
+ "version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/TomasVotruba/type-coverage.git",
- "reference": "d033429580f2c18bda538fa44f2939236a990e0c"
+ "reference": "468354b3964120806a69890cbeb3fcf005876391"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c",
- "reference": "d033429580f2c18bda538fa44f2939236a990e0c",
+ "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/468354b3964120806a69890cbeb3fcf005876391",
+ "reference": "468354b3964120806a69890cbeb3fcf005876391",
"shasum": ""
},
"require": {
@@ -364,7 +361,7 @@
],
"support": {
"issues": "https://github.com/TomasVotruba/type-coverage/issues",
- "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2"
+ "source": "https://github.com/TomasVotruba/type-coverage/tree/2.1.0"
},
"funding": [
{
@@ -376,7 +373,7 @@
"type": "github"
}
],
- "time": "2025-01-07T00:10:26+00:00"
+ "time": "2025-12-05T16:38:02+00:00"
}
],
"aliases": [],
@@ -386,5 +383,5 @@
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}
diff --git a/tools/.phpstan/vendor/composer/autoload_psr4.php b/tools/.phpstan/vendor/composer/autoload_psr4.php
index 1dc5d9e4c..6b557fe59 100644
--- a/tools/.phpstan/vendor/composer/autoload_psr4.php
+++ b/tools/.phpstan/vendor/composer/autoload_psr4.php
@@ -9,5 +9,6 @@
'TomasVotruba\\TypeCoverage\\' => array($vendorDir . '/tomasvotruba/type-coverage/src'),
'PHPStan\\ExtensionInstaller\\' => array($vendorDir . '/phpstan/extension-installer/src'),
'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-strict-rules/src'),
+ 'Nette\\' => array($vendorDir . '/nette/utils/src'),
'Ergebnis\\PHPStan\\Rules\\' => array($vendorDir . '/ergebnis/phpstan-rules/src'),
);
diff --git a/tools/.phpstan/vendor/composer/autoload_static.php b/tools/.phpstan/vendor/composer/autoload_static.php
index 17adb2b10..b66b4992e 100644
--- a/tools/.phpstan/vendor/composer/autoload_static.php
+++ b/tools/.phpstan/vendor/composer/autoload_static.php
@@ -11,35 +11,43 @@ class ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7
);
public static $prefixLengthsPsr4 = array (
- 'T' =>
+ 'T' =>
array (
'TomasVotruba\\TypeCoverage\\' => 26,
),
- 'P' =>
+ 'P' =>
array (
'PHPStan\\ExtensionInstaller\\' => 27,
'PHPStan\\' => 8,
),
- 'E' =>
+ 'N' =>
+ array (
+ 'Nette\\' => 6,
+ ),
+ 'E' =>
array (
'Ergebnis\\PHPStan\\Rules\\' => 23,
),
);
public static $prefixDirsPsr4 = array (
- 'TomasVotruba\\TypeCoverage\\' =>
+ 'TomasVotruba\\TypeCoverage\\' =>
array (
0 => __DIR__ . '/..' . '/tomasvotruba/type-coverage/src',
),
- 'PHPStan\\ExtensionInstaller\\' =>
+ 'PHPStan\\ExtensionInstaller\\' =>
array (
0 => __DIR__ . '/..' . '/phpstan/extension-installer/src',
),
- 'PHPStan\\' =>
+ 'PHPStan\\' =>
array (
0 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src',
),
- 'Ergebnis\\PHPStan\\Rules\\' =>
+ 'Nette\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/nette/utils/src',
+ ),
+ 'Ergebnis\\PHPStan\\Rules\\' =>
array (
0 => __DIR__ . '/..' . '/ergebnis/phpstan-rules/src',
),
diff --git a/tools/.phpstan/vendor/composer/installed.json b/tools/.phpstan/vendor/composer/installed.json
index c13206e30..bb0437ecf 100644
--- a/tools/.phpstan/vendor/composer/installed.json
+++ b/tools/.phpstan/vendor/composer/installed.json
@@ -2,22 +2,22 @@
"packages": [
{
"name": "ergebnis/phpstan-rules",
- "version": "2.10.5",
- "version_normalized": "2.10.5.0",
+ "version": "2.12.0",
+ "version_normalized": "2.12.0.0",
"source": {
"type": "git",
"url": "https://github.com/ergebnis/phpstan-rules.git",
- "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78"
+ "reference": "c4e0121a937b3b551f800a86e7d78794da2783ea"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/719a53e793e2417da46d497f21fb8d2f007ecb78",
- "reference": "719a53e793e2417da46d497f21fb8d2f007ecb78",
+ "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/c4e0121a937b3b551f800a86e7d78794da2783ea",
+ "reference": "c4e0121a937b3b551f800a86e7d78794da2783ea",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
- "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
+ "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"phpstan/phpstan": "^2.1.8"
},
"require-dev": {
@@ -25,20 +25,19 @@
"doctrine/orm": "^2.20.0 || ^3.3.0",
"ergebnis/composer-normalize": "^2.47.0",
"ergebnis/license": "^2.6.0",
- "ergebnis/php-cs-fixer-config": "^6.46.0",
- "ergebnis/phpunit-slow-test-detector": "^2.19.1",
+ "ergebnis/php-cs-fixer-config": "^6.54.0",
+ "ergebnis/phpunit-slow-test-detector": "^2.20.0",
"fakerphp/faker": "^1.24.1",
- "nette/di": "^3.1.10",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan-deprecation-rules": "^2.0.3",
- "phpstan/phpstan-phpunit": "^2.0.6",
- "phpstan/phpstan-strict-rules": "^2.0.4",
+ "phpstan/phpstan-phpunit": "^2.0.7",
+ "phpstan/phpstan-strict-rules": "^2.0.6",
"phpunit/phpunit": "^9.6.21",
"psr/container": "^2.0.2",
"symfony/finder": "^5.4.45",
"symfony/process": "^5.4.47"
},
- "time": "2025-06-23T17:35:58+00:00",
+ "time": "2025-09-07T13:31:33+00:00",
"type": "phpstan-extension",
"extra": {
"phpstan": {
@@ -79,30 +78,30 @@
},
{
"name": "nette/utils",
- "version": "v4.0.7",
- "version_normalized": "4.0.7.0",
+ "version": "v4.1.0",
+ "version_normalized": "4.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
- "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2"
+ "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2",
- "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2",
+ "url": "https://api.github.com/repos/nette/utils/zipball/fa1f0b8261ed150447979eb22e373b7b7ad5a8e0",
+ "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0",
"shasum": ""
},
"require": {
- "php": "8.0 - 8.4"
+ "php": "8.2 - 8.5"
},
"conflict": {
"nette/finder": "<3",
"nette/schema": "<1.2.2"
},
"require-dev": {
- "jetbrains/phpstorm-attributes": "dev-master",
+ "jetbrains/phpstorm-attributes": "^1.2",
"nette/tester": "^2.5",
- "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-nette": "^2.0@stable",
"tracy/tracy": "^2.9"
},
"suggest": {
@@ -113,15 +112,18 @@
"ext-mbstring": "to use Strings::lower() etc...",
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
},
- "time": "2025-06-03T04:55:08+00:00",
+ "time": "2025-12-01T17:49:23+00:00",
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.0-dev"
+ "dev-master": "4.1-dev"
}
},
"installation-source": "dist",
"autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
"classmap": [
"src/"
]
@@ -162,7 +164,7 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
- "source": "https://github.com/nette/utils/tree/v4.0.7"
+ "source": "https://github.com/nette/utils/tree/v4.1.0"
},
"install-path": "../nette/utils"
},
@@ -219,17 +221,12 @@
},
{
"name": "phpstan/phpstan",
- "version": "2.1.21",
- "version_normalized": "2.1.21.0",
- "source": {
- "type": "git",
- "url": "https://github.com/phpstan/phpstan.git",
- "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6"
- },
+ "version": "2.1.33",
+ "version_normalized": "2.1.33.0",
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ccf445757458c06a04eb3f803603cb118fe5fa6",
- "reference": "1ccf445757458c06a04eb3f803603cb118fe5fa6",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
+ "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
"shasum": ""
},
"require": {
@@ -238,7 +235,7 @@
"conflict": {
"phpstan/phpstan-shim": "*"
},
- "time": "2025-07-28T19:35:08+00:00",
+ "time": "2025-12-05T10:24:31+00:00",
"bin": [
"phpstan",
"phpstan.phar"
@@ -280,22 +277,22 @@
},
{
"name": "phpstan/phpstan-strict-rules",
- "version": "2.0.6",
- "version_normalized": "2.0.6.0",
+ "version": "2.0.7",
+ "version_normalized": "2.0.7.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
- "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094"
+ "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094",
- "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/d6211c46213d4181054b3d77b10a5c5cb0d59538",
+ "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
- "phpstan/phpstan": "^2.0.4"
+ "phpstan/phpstan": "^2.1.29"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -303,7 +300,7 @@
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^9.6"
},
- "time": "2025-07-21T12:19:29+00:00",
+ "time": "2025-09-26T11:19:08+00:00",
"type": "phpstan-extension",
"extra": {
"phpstan": {
@@ -325,23 +322,23 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
- "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6"
+ "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.7"
},
"install-path": "../phpstan/phpstan-strict-rules"
},
{
"name": "tomasvotruba/type-coverage",
- "version": "2.0.2",
- "version_normalized": "2.0.2.0",
+ "version": "2.1.0",
+ "version_normalized": "2.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/TomasVotruba/type-coverage.git",
- "reference": "d033429580f2c18bda538fa44f2939236a990e0c"
+ "reference": "468354b3964120806a69890cbeb3fcf005876391"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c",
- "reference": "d033429580f2c18bda538fa44f2939236a990e0c",
+ "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/468354b3964120806a69890cbeb3fcf005876391",
+ "reference": "468354b3964120806a69890cbeb3fcf005876391",
"shasum": ""
},
"require": {
@@ -349,7 +346,7 @@
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.0"
},
- "time": "2025-01-07T00:10:26+00:00",
+ "time": "2025-12-05T16:38:02+00:00",
"type": "phpstan-extension",
"extra": {
"phpstan": {
@@ -375,7 +372,7 @@
],
"support": {
"issues": "https://github.com/TomasVotruba/type-coverage/issues",
- "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2"
+ "source": "https://github.com/TomasVotruba/type-coverage/tree/2.1.0"
},
"funding": [
{
diff --git a/tools/.phpstan/vendor/composer/installed.php b/tools/.phpstan/vendor/composer/installed.php
index 25f75aa88..759f7f1b7 100644
--- a/tools/.phpstan/vendor/composer/installed.php
+++ b/tools/.phpstan/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
- 'reference' => '530bb5df4bf62ecfdc3ad304fa6b9e8afe4acd66',
+ 'reference' => '5b74f6242eb0baf4414dd15033afd51abc0bb45d',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,25 +13,25 @@
'__root__' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
- 'reference' => '530bb5df4bf62ecfdc3ad304fa6b9e8afe4acd66',
+ 'reference' => '5b74f6242eb0baf4414dd15033afd51abc0bb45d',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'ergebnis/phpstan-rules' => array(
- 'pretty_version' => '2.10.5',
- 'version' => '2.10.5.0',
- 'reference' => '719a53e793e2417da46d497f21fb8d2f007ecb78',
+ 'pretty_version' => '2.12.0',
+ 'version' => '2.12.0.0',
+ 'reference' => 'c4e0121a937b3b551f800a86e7d78794da2783ea',
'type' => 'phpstan-extension',
'install_path' => __DIR__ . '/../ergebnis/phpstan-rules',
'aliases' => array(),
'dev_requirement' => true,
),
'nette/utils' => array(
- 'pretty_version' => 'v4.0.7',
- 'version' => '4.0.7.0',
- 'reference' => 'e67c4061eb40b9c113b218214e42cb5a0dda28f2',
+ 'pretty_version' => 'v4.1.0',
+ 'version' => '4.1.0.0',
+ 'reference' => 'fa1f0b8261ed150447979eb22e373b7b7ad5a8e0',
'type' => 'library',
'install_path' => __DIR__ . '/../nette/utils',
'aliases' => array(),
@@ -47,27 +47,27 @@
'dev_requirement' => true,
),
'phpstan/phpstan' => array(
- 'pretty_version' => '2.1.21',
- 'version' => '2.1.21.0',
- 'reference' => '1ccf445757458c06a04eb3f803603cb118fe5fa6',
+ 'pretty_version' => '2.1.33',
+ 'version' => '2.1.33.0',
+ 'reference' => '9e800e6bee7d5bd02784d4c6069b48032d16224f',
'type' => 'library',
'install_path' => __DIR__ . '/../phpstan/phpstan',
'aliases' => array(),
'dev_requirement' => true,
),
'phpstan/phpstan-strict-rules' => array(
- 'pretty_version' => '2.0.6',
- 'version' => '2.0.6.0',
- 'reference' => 'f9f77efa9de31992a832ff77ea52eb42d675b094',
+ 'pretty_version' => '2.0.7',
+ 'version' => '2.0.7.0',
+ 'reference' => 'd6211c46213d4181054b3d77b10a5c5cb0d59538',
'type' => 'phpstan-extension',
'install_path' => __DIR__ . '/../phpstan/phpstan-strict-rules',
'aliases' => array(),
'dev_requirement' => true,
),
'tomasvotruba/type-coverage' => array(
- 'pretty_version' => '2.0.2',
- 'version' => '2.0.2.0',
- 'reference' => 'd033429580f2c18bda538fa44f2939236a990e0c',
+ 'pretty_version' => '2.1.0',
+ 'version' => '2.1.0.0',
+ 'reference' => '468354b3964120806a69890cbeb3fcf005876391',
'type' => 'phpstan-extension',
'install_path' => __DIR__ . '/../tomasvotruba/type-coverage',
'aliases' => array(),
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md
index 38ee8788b..7729147af 100644
--- a/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md
@@ -6,7 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## Unreleased
-For a full diff see [`2.10.5...main`][2.10.5...main].
+For a full diff see [`2.12.0...main`][2.12.0...main].
+
+## [`2.12.0`][2.12.0]
+
+For a full diff see [`2.11.0...2.12.0`][2.11.0...2.12.0].
+
+### Added
+
+- Added support for PHP 8.5 ([#977]), by [@localheinz]
+
+## [`2.11.0`][2.11.0]
+
+For a full diff see [`2.10.5...2.11.0`][2.10.5...2.11.0].
+
+### Changed
+
+- Allowed installation on PHP 8.5 ([#972]), by [@localheinz]
## [`2.10.5`][2.10.5]
@@ -570,6 +586,8 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
[2.10.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.3
[2.10.4]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.4
[2.10.5]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.5
+[2.11.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.11.0
+[2.12.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.12.0
[362c7ea...0.1.0]: https://github.com/ergebnis/phpstan-rules/compare/362c7ea...0.1.0
[0.1.0...0.2.0]: https://github.com/ergebnis/phpstan-rules/compare/0.1.0...0.2.0
@@ -618,7 +636,9 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
[2.10.2...2.10.3]: https://github.com/ergebnis/phpstan-rules/compare/2.10.2...2.10.3
[2.10.3...2.10.4]: https://github.com/ergebnis/phpstan-rules/compare/2.10.3...2.10.4
[2.10.4...2.10.5]: https://github.com/ergebnis/phpstan-rules/compare/2.10.4...2.10.5
-[2.10.5...main]: https://github.com/ergebnis/phpstan-rules/compare/2.10.5...main
+[2.10.5...2.11.0]: https://github.com/ergebnis/phpstan-rules/compare/2.10.5...2.11.0
+[2.11.0...2.12.0]: https://github.com/ergebnis/phpstan-rules/compare/2.11.0...2.12.0
+[2.12.0...main]: https://github.com/ergebnis/phpstan-rules/compare/2.12.0...main
[#1]: https://github.com/ergebnis/phpstan-rules/pull/1
[#4]: https://github.com/ergebnis/phpstan-rules/pull/4
@@ -714,6 +734,8 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
[#951]: https://github.com/ergebnis/phpstan-rules/pull/951
[#957]: https://github.com/ergebnis/phpstan-rules/pull/957
[#958]: https://github.com/ergebnis/phpstan-rules/pull/958
+[#972]: https://github.com/ergebnis/phpstan-rules/pull/972
+[#977]: https://github.com/ergebnis/phpstan-rules/pull/977
[@cosmastech]: https://github.com/cosmastech
[@enumag]: https://github.com/enumag
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md
index 7f80534b7..690d4ad04 100644
--- a/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md
@@ -735,7 +735,7 @@ The maintainers of this project ask contributors to follow the [code of conduct]
The maintainers of this project provide limited support.
-You can support the maintenance of this project by [sponsoring @localheinz](https://github.com/sponsors/localheinz) or [requesting an invoice for services related to this project](mailto:am@localheinz.com?subject=ergebnis/phpstan-rules:%20Requesting%20invoice%20for%20services).
+You can support the maintenance of this project by [sponsoring @ergebnis](https://github.com/sponsors/ergebnis).
## PHP Version Support Policy
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json
index 10429599c..5060dde2d 100644
--- a/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json
@@ -21,7 +21,7 @@
"security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md"
},
"require": {
- "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
+ "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"ext-mbstring": "*",
"phpstan/phpstan": "^2.1.8"
},
@@ -30,14 +30,13 @@
"doctrine/orm": "^2.20.0 || ^3.3.0",
"ergebnis/composer-normalize": "^2.47.0",
"ergebnis/license": "^2.6.0",
- "ergebnis/php-cs-fixer-config": "^6.46.0",
- "ergebnis/phpunit-slow-test-detector": "^2.19.1",
+ "ergebnis/php-cs-fixer-config": "^6.54.0",
+ "ergebnis/phpunit-slow-test-detector": "^2.20.0",
"fakerphp/faker": "^1.24.1",
- "nette/di": "^3.1.10",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan-deprecation-rules": "^2.0.3",
- "phpstan/phpstan-phpunit": "^2.0.6",
- "phpstan/phpstan-strict-rules": "^2.0.4",
+ "phpstan/phpstan-phpunit": "^2.0.7",
+ "phpstan/phpstan-strict-rules": "^2.0.6",
"phpunit/phpunit": "^9.6.21",
"psr/container": "^2.0.2",
"symfony/finder": "^5.4.45",
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php
index 937bf0ec2..05a9f3ca6 100644
--- a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php
@@ -93,11 +93,11 @@ public function processNode(
return [];
}
- if ($this->hasWhitelistedAnnotation($node)) {
+ if (self::hasWhitelistedAnnotation($node)) {
return [];
}
- if ($this->hasWhitelistedAttribute($node)) {
+ if (self::hasWhitelistedAttribute($node)) {
return [];
}
@@ -124,7 +124,7 @@ public function processNode(
* @see https://github.com/SpacePossum
* @see https://github.com/Slamdunk
*/
- private function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool
+ private static function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool
{
$docComment = $node->getDocComment();
@@ -147,7 +147,7 @@ private function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool
return false;
}
- private function hasWhitelistedAttribute(Node\Stmt\Class_ $node): bool
+ private static function hasWhitelistedAttribute(Node\Stmt\Class_ $node): bool
{
foreach ($node->attrGroups as $attributeGroup) {
foreach ($attributeGroup->attrs as $attribute) {
diff --git a/tools/.phpstan/vendor/nette/utils/composer.json b/tools/.phpstan/vendor/nette/utils/composer.json
index 74bd6183c..6f6db4a45 100644
--- a/tools/.phpstan/vendor/nette/utils/composer.json
+++ b/tools/.phpstan/vendor/nette/utils/composer.json
@@ -15,13 +15,13 @@
}
],
"require": {
- "php": "8.0 - 8.4"
+ "php": "8.2 - 8.5"
},
"require-dev": {
"nette/tester": "^2.5",
"tracy/tracy": "^2.9",
- "phpstan/phpstan": "^1.0",
- "jetbrains/phpstorm-attributes": "dev-master"
+ "phpstan/phpstan-nette": "^2.0@stable",
+ "jetbrains/phpstorm-attributes": "^1.2"
},
"conflict": {
"nette/finder": "<3",
@@ -36,7 +36,10 @@
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
},
"autoload": {
- "classmap": ["src/"]
+ "classmap": ["src/"],
+ "psr-4": {
+ "Nette\\": "src"
+ }
},
"minimum-stability": "dev",
"scripts": {
@@ -45,7 +48,7 @@
},
"extra": {
"branch-alias": {
- "dev-master": "4.0-dev"
+ "dev-master": "4.1-dev"
}
}
}
diff --git a/tools/.phpstan/vendor/nette/utils/readme.md b/tools/.phpstan/vendor/nette/utils/readme.md
index 0038f534f..8c717be42 100644
--- a/tools/.phpstan/vendor/nette/utils/readme.md
+++ b/tools/.phpstan/vendor/nette/utils/readme.md
@@ -41,7 +41,7 @@ The recommended way to install is via Composer:
composer require nette/utils
```
-Nette Utils 4.0 is compatible with PHP 8.0 to 8.4.
+Nette Utils 4.1 is compatible with PHP 8.2 to 8.5.
diff --git a/tools/.phpstan/vendor/nette/utils/src/Iterators/CachingIterator.php b/tools/.phpstan/vendor/nette/utils/src/Iterators/CachingIterator.php
index 02bd74070..bf6ef85ce 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Iterators/CachingIterator.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Iterators/CachingIterator.php
@@ -34,7 +34,7 @@ class CachingIterator extends \CachingIterator implements \Countable
public function __construct(iterable|\stdClass $iterable)
{
$iterable = $iterable instanceof \stdClass
- ? new \ArrayIterator($iterable)
+ ? new \ArrayIterator((array) $iterable)
: Nette\Utils\Iterables::toIterator($iterable);
parent::__construct($iterable, 0);
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/StaticClass.php b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php
index b1d84862e..46b278669 100644
--- a/tools/.phpstan/vendor/nette/utils/src/StaticClass.php
+++ b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php
@@ -21,14 +21,4 @@ trait StaticClass
private function __construct()
{
}
-
-
- /**
- * Call to undefined static method.
- * @throws MemberAccessException
- */
- public static function __callStatic(string $name, array $args): mixed
- {
- Utils\ObjectHelpers::strictStaticCall(static::class, $name);
- }
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayHash.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayHash.php
index 349ffe0ef..6e6516bd6 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayHash.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayHash.php
@@ -10,6 +10,7 @@
namespace Nette\Utils;
use Nette;
+use function count, is_array, is_scalar, sprintf;
/**
@@ -59,7 +60,7 @@ public function count(): int
/**
- * Replaces or appends a item.
+ * Replaces or appends an item.
* @param array-key $key
* @param T $value
*/
@@ -74,7 +75,7 @@ public function offsetSet($key, $value): void
/**
- * Returns a item.
+ * Returns an item.
* @param array-key $key
* @return T
*/
@@ -86,7 +87,7 @@ public function offsetGet($key)
/**
- * Determines whether a item exists.
+ * Determines whether an item exists.
* @param array-key $key
*/
public function offsetExists($key): bool
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php
index a402f9bf6..98a508219 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php
@@ -10,6 +10,7 @@
namespace Nette\Utils;
use Nette;
+use function array_slice, array_splice, count, is_int;
/**
@@ -20,8 +21,6 @@
*/
class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
{
- use Nette\SmartObject;
-
private array $list = [];
@@ -63,7 +62,7 @@ public function count(): int
/**
- * Replaces or appends a item.
+ * Replaces or appends an item.
* @param int|null $index
* @param T $value
* @throws Nette\OutOfRangeException
@@ -83,7 +82,7 @@ public function offsetSet($index, $value): void
/**
- * Returns a item.
+ * Returns an item.
* @param int $index
* @return T
* @throws Nette\OutOfRangeException
@@ -99,7 +98,7 @@ public function offsetGet($index): mixed
/**
- * Determines whether a item exists.
+ * Determines whether an item exists.
* @param int $index
*/
public function offsetExists($index): bool
@@ -124,7 +123,7 @@ public function offsetUnset($index): void
/**
- * Prepends a item.
+ * Prepends an item.
* @param T $value
*/
public function prepend(mixed $value): void
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php
index 02522ca21..986118c89 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php
@@ -11,7 +11,8 @@
use JetBrains\PhpStorm\Language;
use Nette;
-use function is_array, is_int, is_object, count;
+use function array_combine, array_intersect_key, array_is_list, array_key_exists, array_key_first, array_key_last, array_keys, array_reverse, array_search, array_slice, array_walk_recursive, count, func_num_args, in_array, is_array, is_int, is_object, key, preg_split;
+use const PREG_GREP_INVERT, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY;
/**
@@ -277,11 +278,7 @@ public static function flatten(array $array, bool $preserveKeys = false): array
*/
public static function isList(mixed $value): bool
{
- return is_array($value) && (
- PHP_VERSION_ID < 80100
- ? !$value || array_keys($value) === range(0, count($value) - 1)
- : array_is_list($value)
- );
+ return is_array($value) && array_is_list($value);
}
@@ -532,7 +529,7 @@ public static function toObject(iterable $array, object $object): object
*/
public static function toKey(mixed $value): int|string
{
- return key([$value => null]);
+ return key(@[$value => null]);
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php
index 1777428fd..7d384f25e 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php
@@ -10,7 +10,7 @@
namespace Nette\Utils;
use Nette;
-use function is_array, is_object, is_string;
+use function explode, func_get_args, ini_get, is_array, is_callable, is_object, is_string, preg_replace, restore_error_handler, set_error_handler, sprintf, str_contains, str_ends_with;
/**
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php
index 811f68dbe..6191223f5 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php
@@ -9,7 +9,7 @@
namespace Nette\Utils;
-use Nette;
+use function array_merge, checkdate, implode, is_numeric, is_string, preg_replace_callback, sprintf, time, trim;
/**
@@ -17,8 +17,6 @@
*/
class DateTime extends \DateTime implements \JsonSerializable
{
- use Nette\SmartObject;
-
/** minute in seconds */
public const MINUTE = 60;
@@ -45,7 +43,7 @@ class DateTime extends \DateTime implements \JsonSerializable
public static function from(string|int|\DateTimeInterface|null $time): static
{
if ($time instanceof \DateTimeInterface) {
- return new static($time->format('Y-m-d H:i:s.u'), $time->getTimezone());
+ return static::createFromInterface($time);
} elseif (is_numeric($time)) {
if ($time <= self::YEAR) {
@@ -62,7 +60,7 @@ public static function from(string|int|\DateTimeInterface|null $time): static
/**
* Creates DateTime object.
- * @throws Nette\InvalidArgumentException if the date and time are not valid.
+ * @throws \Exception if the date and time are not valid.
*/
public static function fromParts(
int $year,
@@ -73,17 +71,10 @@ public static function fromParts(
float $second = 0.0,
): static
{
- $s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second);
- if (
- !checkdate($month, $day, $year)
- || $hour < 0 || $hour > 23
- || $minute < 0 || $minute > 59
- || $second < 0 || $second >= 60
- ) {
- throw new Nette\InvalidArgumentException("Invalid date '$s'");
- }
-
- return new static($s);
+ $sec = (int) floor($second);
+ return (new static(''))
+ ->setDate($year, $month, $day)
+ ->setTime($hour, $minute, $sec, (int) round(($second - $sec) * 1e6));
}
@@ -96,10 +87,7 @@ public static function createFromFormat(
string|\DateTimeZone|null $timezone = null,
): static|false
{
- if ($timezone === null) {
- $timezone = new \DateTimeZone(date_default_timezone_get());
-
- } elseif (is_string($timezone)) {
+ if (is_string($timezone)) {
$timezone = new \DateTimeZone($timezone);
}
@@ -124,7 +112,7 @@ public function modify(string $modifier): static
public function setDate(int $year, int $month, int $day): static
{
if (!checkdate($month, $day, $year)) {
- trigger_error(sprintf(self::class . ': The date %04d-%02d-%02d is not valid.', $year, $month, $day), E_USER_WARNING);
+ throw new \Exception(sprintf('The date %04d-%02d-%02d is not valid.', $year, $month, $day));
}
return parent::setDate($year, $month, $day);
}
@@ -138,7 +126,7 @@ public function setTime(int $hour, int $minute, int $second = 0, int $microsecon
|| $second < 0 || $second >= 60
|| $microsecond < 0 || $microsecond >= 1_000_000
) {
- trigger_error(sprintf(self::class . ': The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000), E_USER_WARNING);
+ throw new \Exception(sprintf('The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000));
}
return parent::setTime($hour, $minute, $second, $microsecond);
}
@@ -149,7 +137,7 @@ public function setTime(int $hour, int $minute, int $second = 0, int $microsecon
*/
public static function relativeToSeconds(string $relativeTime): int
{
- return (new \DateTimeImmutable('1970-01-01 ' . $relativeTime, new \DateTimeZone('UTC')))
+ return (new self('@0 ' . $relativeTime))
->getTimestamp();
}
@@ -158,7 +146,7 @@ private function apply(string $datetime, $timezone = null, bool $ctr = false): v
{
$relPart = '';
$absPart = preg_replace_callback(
- '/[+-]?\s*\d+\s+((microsecond|millisecond|[mµu]sec)s?|[mµ]s|sec(ond)?s?|min(ute)?s?|hours?)\b/iu',
+ '/[+-]?\s*\d+\s+((microsecond|millisecond|[mµu]sec)s?|[mµ]s|sec(ond)?s?|min(ute)?s?|hours?)(\s+ago)?\b/iu',
function ($m) use (&$relPart) {
$relPart .= $m[0] . ' ';
return '';
@@ -215,7 +203,7 @@ private function handleErrors(string $value): void
$errors = self::getLastErrors();
$errors = array_merge($errors['errors'] ?? [], $errors['warnings'] ?? []);
if ($errors) {
- trigger_error(self::class . ': ' . implode(', ', $errors) . " '$value'", E_USER_WARNING);
+ throw new \Exception(implode(', ', $errors) . " '$value'");
}
}
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php
index fb92d1191..59dd72271 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php
@@ -10,6 +10,7 @@
namespace Nette\Utils;
use Nette;
+use const DIRECTORY_SEPARATOR;
/**
@@ -18,7 +19,7 @@
*/
final class FileInfo extends \SplFileInfo
{
- private string $relativePath;
+ private readonly string $relativePath;
public function __construct(string $file, string $relativePath = '')
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php
index 6328fb856..8adb21e66 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php
@@ -10,6 +10,8 @@
namespace Nette\Utils;
use Nette;
+use function array_pop, chmod, decoct, dirname, end, fclose, file_exists, file_get_contents, file_put_contents, fopen, implode, is_dir, is_file, is_link, mkdir, preg_match, preg_split, realpath, rename, rmdir, rtrim, sprintf, str_replace, stream_copy_to_stream, stream_is_local, strtr;
+use const DIRECTORY_SEPARATOR;
/**
@@ -21,7 +23,7 @@ final class FileSystem
* Creates a directory if it does not exist, including parent directories.
* @throws Nette\IOException on error occurred
*/
- public static function createDir(string $dir, int $mode = 0777): void
+ public static function createDir(string $dir, int $mode = 0o777): void
{
if (!is_dir($dir) && !@mkdir($dir, $mode, recursive: true) && !is_dir($dir)) { // @ - dir may already exist
throw new Nette\IOException(sprintf(
@@ -209,7 +211,7 @@ public static function readLines(string $file, bool $stripNewLines = true): \Gen
* Writes the string to a file.
* @throws Nette\IOException on error occurred
*/
- public static function write(string $file, string $content, ?int $mode = 0666): void
+ public static function write(string $file, string $content, ?int $mode = 0o666): void
{
static::createDir(dirname($file));
if (@file_put_contents($file, $content) === false) { // @ is escalated to exception
@@ -236,7 +238,7 @@ public static function write(string $file, string $content, ?int $mode = 0666):
* Recursively traverses and sets permissions on the entire contents of the directory as well.
* @throws Nette\IOException on error occurred
*/
- public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void
+ public static function makeWritable(string $path, int $dirMode = 0o777, int $fileMode = 0o666): void
{
if (is_file($path)) {
if (!@chmod($path, $fileMode)) { // @ is escalated to exception
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php
index 587b35972..2f5f7d16b 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php
@@ -10,6 +10,8 @@
namespace Nette\Utils;
use Nette;
+use function array_merge, count, func_get_args, func_num_args, glob, implode, is_array, is_dir, iterator_to_array, preg_match, preg_quote, preg_replace, preg_split, rtrim, spl_object_id, sprintf, str_ends_with, str_starts_with, strnatcmp, strpbrk, strrpos, strtolower, strtr, substr, usort;
+use const GLOB_NOESCAPE, GLOB_NOSORT, GLOB_ONLYDIR;
/**
@@ -24,8 +26,6 @@
*/
class Finder implements \IteratorAggregate
{
- use Nette\SmartObject;
-
/** @var array */
private array $find = [];
@@ -385,7 +385,7 @@ private function traverseDir(string $dir, array $searches, array $subdirs = []):
$relativePathname = FileSystem::unixSlashes($file->getRelativePathname());
foreach ($searches as $search) {
if (
- $file->{'is' . $search->mode}()
+ "is_$search->mode"(Helpers::IsWindows && $file->isLink() ? $file->getLinkTarget() : $file->getPathname())
&& preg_match($search->pattern, $relativePathname)
&& $this->proveFilters($this->filters, $file, $cache)
) {
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php
index cc2781d71..ed78a5503 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Floats.php
@@ -10,6 +10,7 @@
namespace Nette\Utils;
use Nette;
+use function abs, is_finite, is_nan, max, round;
/**
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php
index 21efb2ac7..c7d78943d 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Helpers.php
@@ -10,6 +10,8 @@
namespace Nette\Utils;
use Nette;
+use function array_unique, ini_get, levenshtein, max, min, ob_end_clean, ob_get_clean, ob_start, preg_replace, strlen;
+use const PHP_OS_FAMILY;
class Helpers
@@ -22,7 +24,7 @@ class Helpers
*/
public static function capture(callable $func): string
{
- ob_start(function () {});
+ ob_start(fn() => '');
try {
$func();
return ob_get_clean();
@@ -104,4 +106,16 @@ public static function compare(mixed $left, string $operator, mixed $right): boo
default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"),
};
}
+
+
+ /**
+ * Splits a class name into namespace and short class name.
+ * @return array{string, string}
+ */
+ public static function splitClassName(string $name): array
+ {
+ return ($pos = strrpos($name, '\\')) === false
+ ? ['', $name]
+ : [substr($name, 0, $pos), substr($name, $pos + 1)];
+ }
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php
index fc0e3ef2a..3a57de5ed 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Html.php
@@ -9,9 +9,9 @@
namespace Nette\Utils;
-use Nette;
use Nette\HtmlStringable;
-use function is_array, is_float, is_object, is_string;
+use function array_merge, array_splice, count, explode, func_num_args, html_entity_decode, htmlspecialchars, http_build_query, implode, is_array, is_bool, is_float, is_object, is_string, json_encode, max, number_format, rtrim, str_contains, str_repeat, str_replace, strip_tags, strncmp, strpbrk, substr;
+use const ENT_HTML5, ENT_NOQUOTES, ENT_QUOTES;
/**
@@ -233,20 +233,18 @@
*/
class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringable
{
- use Nette\SmartObject;
-
/** @var array element's attributes */
- public $attrs = [];
+ public array $attrs = [];
/** void elements */
- public static $emptyElements = [
+ public static array $emptyElements = [
'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1,
'source' => 1, 'base' => 1, 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1,
'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1,
];
/** @var array nodes */
- protected $children = [];
+ protected array $children = [];
/** element's name */
private string $name = '';
@@ -575,7 +573,7 @@ final public function getText(): string
/**
* Adds new element's child.
*/
- final public function addHtml(mixed $child): static
+ final public function addHtml(HtmlStringable|string $child): static
{
return $this->insert(null, $child);
}
@@ -584,7 +582,7 @@ final public function addHtml(mixed $child): static
/**
* Appends plain-text string to element content.
*/
- public function addText(mixed $text): static
+ public function addText(\Stringable|string $text): static
{
if (!$text instanceof HtmlStringable) {
$text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php
index 19ce7b4f4..a6bb4782a 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Image.php
@@ -10,6 +10,8 @@
namespace Nette\Utils;
use Nette;
+use function is_array, is_int, is_string;
+use const IMG_BMP, IMG_FLIP_BOTH, IMG_FLIP_HORIZONTAL, IMG_FLIP_VERTICAL, IMG_GIF, IMG_JPG, IMG_PNG, IMG_WEBP, PATHINFO_EXTENSION;
/**
@@ -237,8 +239,8 @@ public static function fromBlank(int $width, int $height, ImageColor|array|null
*/
public static function detectTypeFromFile(string $file, &$width = null, &$height = null): ?int
{
- [$width, $height, $type] = @getimagesize($file); // @ - files smaller than 12 bytes causes read error
- return isset(self::Formats[$type]) ? $type : null;
+ [$width, $height, $type] = Helpers::falseToNull(@getimagesize($file)); // @ - files smaller than 12 bytes causes read error
+ return $type && isset(self::Formats[$type]) ? $type : null;
}
@@ -248,8 +250,8 @@ public static function detectTypeFromFile(string $file, &$width = null, &$height
*/
public static function detectTypeFromString(string $s, &$width = null, &$height = null): ?int
{
- [$width, $height, $type] = @getimagesizefromstring($s); // @ - strings smaller than 12 bytes causes read error
- return isset(self::Formats[$type]) ? $type : null;
+ [$width, $height, $type] = Helpers::falseToNull(@getimagesizefromstring($s)); // @ - strings smaller than 12 bytes causes read error
+ return $type && isset(self::Formats[$type]) ? $type : null;
}
@@ -771,7 +773,7 @@ public function __call(string $name, array $args): mixed
public function __clone()
{
- ob_start(function () {});
+ ob_start(fn() => '');
imagepng($this->image, null, 0);
$this->setImageResource(imagecreatefromstring(ob_get_clean()));
}
@@ -794,7 +796,7 @@ private static function isPercent(int|string &$num): bool
/**
* Prevents serialization.
*/
- public function __sleep(): array
+ public function __serialize(): array
{
throw new Nette\NotSupportedException('You cannot serialize or unserialize ' . self::class . ' instances.');
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php
index 013adbd6e..66966620d 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageColor.php
@@ -10,6 +10,7 @@
namespace Nette\Utils;
use Nette;
+use function hexdec, ltrim, max, min, round, strlen;
/**
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php
index 3092c8f02..080ca8a09 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ImageType.php
@@ -9,6 +9,8 @@
namespace Nette\Utils;
+use const IMAGETYPE_BMP, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP;
+
/**
* Type of image file.
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Iterables.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Iterables.php
index c7a7b8755..28c9269cf 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Iterables.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Iterables.php
@@ -10,6 +10,7 @@
namespace Nette\Utils;
use Nette;
+use function is_array;
/**
@@ -177,6 +178,31 @@ public static function mapWithKeys(iterable $iterable, callable $transformer): \
}
+ /**
+ * Creates a repeatable iterator from a factory function.
+ * The factory is called every time the iterator is iterated.
+ * @template K
+ * @template V
+ * @param callable(): iterable $factory
+ * @return \IteratorAggregate
+ */
+ public static function repeatable(callable $factory): \IteratorAggregate
+ {
+ return new class ($factory) implements \IteratorAggregate {
+ public function __construct(
+ private $factory,
+ ) {
+ }
+
+
+ public function getIterator(): \Iterator
+ {
+ return Iterables::toIterator(($this->factory)());
+ }
+ };
+ }
+
+
/**
* Wraps around iterator and caches its keys and values during iteration.
* This allows the data to be re-iterated multiple times.
@@ -185,7 +211,7 @@ public static function mapWithKeys(iterable $iterable, callable $transformer): \
* @param iterable $iterable
* @return \IteratorAggregate
*/
- public static function memoize(iterable $iterable): iterable
+ public static function memoize(iterable $iterable): \IteratorAggregate
{
return new class (self::toIterator($iterable)) implements \IteratorAggregate {
public function __construct(
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php
index b87917b2a..4e4cf238e 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Json.php
@@ -10,6 +10,8 @@
namespace Nette\Utils;
use Nette;
+use function defined, is_int, json_decode, json_encode, json_last_error, json_last_error_msg;
+use const JSON_BIGINT_AS_STRING, JSON_FORCE_OBJECT, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_OBJECT_AS_ARRAY, JSON_PRESERVE_ZERO_FRACTION, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE;
/**
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ObjectHelpers.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ObjectHelpers.php
index f4bd55f2b..5d7d9e291 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/ObjectHelpers.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ObjectHelpers.php
@@ -11,6 +11,8 @@
use Nette;
use Nette\MemberAccessException;
+use function array_filter, array_merge, array_pop, array_unique, get_class_methods, get_parent_class, implode, is_a, levenshtein, method_exists, preg_match_all, preg_replace, strlen, ucfirst;
+use const PREG_SET_ORDER, SORT_REGULAR;
/**
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php
index b14fbd55e..e636dd072 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Random.php
@@ -11,6 +11,8 @@
use Nette;
use Random\Randomizer;
+use function strlen;
+use const PHP_VERSION_ID;
/**
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php
index 215c56860..022098422 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Reflection.php
@@ -10,6 +10,8 @@
namespace Nette\Utils;
use Nette;
+use function constant, current, defined, end, explode, file_get_contents, implode, ltrim, next, ord, strrchr, strtolower, substr;
+use const T_AS, T_CLASS, T_COMMENT, T_CURLY_OPEN, T_DOC_COMMENT, T_DOLLAR_OPEN_CURLY_BRACES, T_ENUM, T_INTERFACE, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAMESPACE, T_NS_SEPARATOR, T_STRING, T_TRAIT, T_USE, T_WHITESPACE, TOKEN_PARSE;
/**
@@ -26,14 +28,13 @@ public static function isBuiltinType(string $type): bool
}
- /** @deprecated use Nette\Utils\Validators::isClassKeyword() */
+ #[\Deprecated('use Nette\Utils\Validators::isClassKeyword()')]
public static function isClassKeyword(string $name): bool
{
return Validators::isClassKeyword($name);
}
- /** @deprecated use native ReflectionParameter::getDefaultValue() */
public static function getParameterDefaultValue(\ReflectionParameter $param): mixed
{
if ($param->isDefaultValueConstant()) {
@@ -136,9 +137,7 @@ public static function toString(\Reflector $ref): string
} elseif ($ref instanceof \ReflectionMethod) {
return $ref->getDeclaringClass()->name . '::' . $ref->name . '()';
} elseif ($ref instanceof \ReflectionFunction) {
- return PHP_VERSION_ID >= 80200 && $ref->isAnonymous()
- ? '{closure}()'
- : $ref->name . '()';
+ return $ref->isAnonymous() ? '{closure}()' : $ref->name . '()';
} elseif ($ref instanceof \ReflectionProperty) {
return self::getPropertyDeclaringClass($ref)->name . '::$' . $ref->name;
} elseif ($ref instanceof \ReflectionParameter) {
@@ -240,7 +239,7 @@ private static function parseUseStatements(string $code, ?string $forClass = nul
case T_CLASS:
case T_INTERFACE:
case T_TRAIT:
- case PHP_VERSION_ID < 80100 ? T_CLASS : T_ENUM:
+ case T_ENUM:
if ($name = self::fetch($tokens, T_STRING)) {
$class = $namespace . $name;
$classLevel = $level + 1;
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php
index b003fcbd1..2a8a55c62 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ReflectionMethod.php
@@ -9,6 +9,8 @@
namespace Nette\Utils;
+use function explode, is_string, str_contains;
+
/**
* ReflectionMethod preserving the original class name.
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php
index 79fa46bb0..eb44b481d 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Strings.php
@@ -11,7 +11,8 @@
use JetBrains\PhpStorm\Language;
use Nette;
-use function is_array, is_object, strlen;
+use function array_keys, array_map, array_shift, array_values, bin2hex, class_exists, defined, extension_loaded, function_exists, htmlspecialchars, htmlspecialchars_decode, iconv, iconv_strlen, iconv_substr, implode, in_array, is_array, is_callable, is_int, is_object, is_string, key, max, mb_convert_case, mb_strlen, mb_strtolower, mb_strtoupper, mb_substr, pack, preg_last_error, preg_last_error_msg, preg_quote, preg_replace, str_contains, str_ends_with, str_repeat, str_replace, str_starts_with, strlen, strpos, strrev, strrpos, strtolower, strtoupper, strtr, substr, trim, unpack, utf8_decode;
+use const ENT_IGNORE, ENT_NOQUOTES, ICONV_IMPL, MB_CASE_TITLE, PHP_EOL, PREG_OFFSET_CAPTURE, PREG_PATTERN_ORDER, PREG_SET_ORDER, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY, PREG_SPLIT_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL;
/**
@@ -21,9 +22,9 @@ class Strings
{
use Nette\StaticClass;
- public const TrimCharacters = " \t\n\r\0\x0B\u{A0}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{200B}";
+ public const TrimCharacters = " \t\n\r\0\x0B\u{A0}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{200B}\u{2028}\u{3000}";
- /** @deprecated use Strings::TrimCharacters */
+ #[\Deprecated('use Strings::TrimCharacters')]
public const TRIM_CHARACTERS = self::TrimCharacters;
@@ -185,17 +186,12 @@ public static function platformNewLines(string $s): string
*/
public static function toAscii(string $s): string
{
- $iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
- static $transliterator = null;
- if ($transliterator === null) {
- if (class_exists('Transliterator', false)) {
- $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
- } else {
- trigger_error(__METHOD__ . "(): it is recommended to enable PHP extensions 'intl'.", E_USER_NOTICE);
- $transliterator = false;
- }
+ if (!extension_loaded('intl')) {
+ throw new Nette\NotSupportedException(__METHOD__ . '() requires INTL extension that is not loaded.');
}
+ $iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
+
// remove control characters and check UTF-8 validity
$s = self::pcre('preg_replace', ['#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s]);
@@ -205,39 +201,15 @@ public static function toAscii(string $s): string
$s = strtr($s, ["\u{AE}" => '(R)', "\u{A9}" => '(c)', "\u{2026}" => '...', "\u{AB}" => '<<', "\u{BB}" => '>>', "\u{A3}" => 'lb', "\u{A5}" => 'yen', "\u{B2}" => '^2', "\u{B3}" => '^3', "\u{B5}" => 'u', "\u{B9}" => '^1', "\u{BA}" => 'o', "\u{BF}" => '?', "\u{2CA}" => "'", "\u{2CD}" => '_', "\u{2DD}" => '"', "\u{1FEF}" => '', "\u{20AC}" => 'EUR', "\u{2122}" => 'TM', "\u{212E}" => 'e', "\u{2190}" => '<-', "\u{2191}" => '^', "\u{2192}" => '->', "\u{2193}" => 'V', "\u{2194}" => '<->']); // ® © … « » £ ¥ ² ³ µ ¹ º ¿ ˊ ˍ ˝ ` € ™ ℮ ← ↑ → ↓ ↔
}
- if ($transliterator) {
- $s = $transliterator->transliterate($s);
- // use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
- if ($iconv === 'glibc') {
- $s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
- $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
- $s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
- } elseif ($iconv === 'libiconv') {
- $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
- } else { // null or 'unknown' (#216)
- $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
- }
- } elseif ($iconv === 'glibc' || $iconv === 'libiconv') {
- // temporarily hide these characters to distinguish them from the garbage that iconv creates
- $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
- if ($iconv === 'glibc') {
- // glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved
- $s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s);
- $s = strtr(
- $s,
- "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
- 'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.',
- );
- $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]);
- } else {
- $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
- }
-
- // remove garbage that iconv creates during transliteration (eg Ý -> Y')
- $s = str_replace(['`', "'", '"', '^', '~', '?'], '', $s);
- // restore temporarily hidden characters
- $s = strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
- } else {
+ $s = \Transliterator::create('Any-Latin; Latin-ASCII')->transliterate($s);
+ // use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
+ if ($iconv === 'glibc') {
+ $s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
+ $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
+ $s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
+ } elseif ($iconv === 'libiconv') {
+ $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
+ } else { // null or 'unknown' (#216)
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php
index 3444a8f17..92aac80de 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Type.php
@@ -10,12 +10,13 @@
namespace Nette\Utils;
use Nette;
+use function array_map, array_search, array_splice, count, explode, implode, is_a, is_resource, is_string, strcasecmp, strtolower, substr, trim;
/**
* PHP type reflection.
*/
-final class Type
+final readonly class Type
{
/** @var array */
private array $types;
@@ -32,7 +33,7 @@ public static function fromReflection(
): ?self
{
$type = $reflection instanceof \ReflectionFunctionAbstract
- ? $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null)
+ ? $reflection->getReturnType() ?? ($reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null)
: $reflection->getType();
return $type ? self::fromReflectionType($type, $reflection, asObject: true) : null;
@@ -84,6 +85,23 @@ public static function fromString(string $type): self
}
+ /**
+ * Creates a Type object based on the actual type of value.
+ */
+ public static function fromValue(mixed $value): self
+ {
+ $type = get_debug_type($value);
+ if (is_resource($value)) {
+ $type = 'mixed';
+ } elseif (str_ends_with($type, '@anonymous')) {
+ $parent = substr($type, 0, -10);
+ $type = $parent === 'class' ? 'object' : $parent;
+ }
+
+ return new self([$type]);
+ }
+
+
/**
* Resolves 'self', 'static' and 'parent' to the actual class name.
*/
@@ -136,6 +154,23 @@ public function __toString(): string
}
+ /**
+ * Returns a type that accepts both the current type and the given type.
+ */
+ public function with(string|self $type): self
+ {
+ $type = is_string($type) ? self::fromString($type) : $type;
+ return match (true) {
+ $this->allows($type) => $this,
+ $type->allows($this) => $type,
+ default => new self(array_unique(
+ array_merge($this->isIntersection() ? [$this] : $this->types, $type->isIntersection() ? [$type] : $type->types),
+ SORT_REGULAR,
+ ), '|'),
+ };
+ }
+
+
/**
* Returns the array of subtypes that make up the compound type as strings.
* @return array
@@ -194,7 +229,7 @@ public function isSimple(): bool
}
- /** @deprecated use isSimple() */
+ #[\Deprecated('use isSimple()')]
public function isSingle(): bool
{
return $this->simple;
@@ -231,36 +266,36 @@ public function isClassKeyword(): bool
/**
* Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter.
*/
- public function allows(string $subtype): bool
+ public function allows(string|self $type): bool
{
if ($this->types === ['mixed']) {
return true;
}
- $subtype = self::fromString($subtype);
- return $subtype->isUnion()
- ? Arrays::every($subtype->types, fn($t) => $this->allows2($t instanceof self ? $t->types : [$t]))
- : $this->allows2($subtype->types);
+ $type = is_string($type) ? self::fromString($type) : $type;
+ return $type->isUnion()
+ ? Arrays::every($type->types, fn($t) => $this->allowsAny($t instanceof self ? $t->types : [$t]))
+ : $this->allowsAny($type->types);
}
- private function allows2(array $subtypes): bool
+ private function allowsAny(array $givenTypes): bool
{
return $this->isUnion()
- ? Arrays::some($this->types, fn($t) => $this->allows3($t instanceof self ? $t->types : [$t], $subtypes))
- : $this->allows3($this->types, $subtypes);
+ ? Arrays::some($this->types, fn($t) => $this->allowsAll($t instanceof self ? $t->types : [$t], $givenTypes))
+ : $this->allowsAll($this->types, $givenTypes);
}
- private function allows3(array $types, array $subtypes): bool
+ private function allowsAll(array $ourTypes, array $givenTypes): bool
{
return Arrays::every(
- $types,
- fn($type) => Arrays::some(
- $subtypes,
- fn($subtype) => Validators::isBuiltinType($type)
- ? strcasecmp($type, $subtype) === 0
- : is_a($subtype, $type, allow_string: true),
+ $ourTypes,
+ fn($ourType) => Arrays::some(
+ $givenTypes,
+ fn($givenType) => Validators::isBuiltinType($ourType)
+ ? strcasecmp($ourType, $givenType) === 0
+ : is_a($givenType, $ourType, allow_string: true),
),
);
}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php
index 61ccf091a..940c3eb43 100644
--- a/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Validators.php
@@ -10,6 +10,7 @@
namespace Nette\Utils;
use Nette;
+use function array_key_exists, class_exists, explode, gettype, interface_exists, is_callable, is_float, is_int, is_iterable, is_numeric, is_object, is_string, preg_match, str_ends_with, str_replace, str_starts_with, strlen, strtolower, substr, trait_exists, var_export;
/**
diff --git a/tools/.phpstan/vendor/phpstan/extension-installer/src/GeneratedConfig.php b/tools/.phpstan/vendor/phpstan/extension-installer/src/GeneratedConfig.php
index c48636d65..1d81e9e0e 100644
--- a/tools/.phpstan/vendor/phpstan/extension-installer/src/GeneratedConfig.php
+++ b/tools/.phpstan/vendor/phpstan/extension-installer/src/GeneratedConfig.php
@@ -21,7 +21,7 @@ final class GeneratedConfig
0 => 'rules.neon',
),
),
- 'version' => '2.10.5',
+ 'version' => '2.12.0',
'phpstanVersionConstraint' => '>=2.1.8.0-dev, <3.0.0.0-dev',
),
'phpstan/phpstan-strict-rules' =>
@@ -35,8 +35,8 @@ final class GeneratedConfig
0 => 'rules.neon',
),
),
- 'version' => '2.0.6',
- 'phpstanVersionConstraint' => '>=2.0.4.0-dev, <3.0.0.0-dev',
+ 'version' => '2.0.7',
+ 'phpstanVersionConstraint' => '>=2.1.29.0-dev, <3.0.0.0-dev',
),
'tomasvotruba/type-coverage' =>
array (
@@ -49,7 +49,7 @@ final class GeneratedConfig
0 => 'config/extension.neon',
),
),
- 'version' => '2.0.2',
+ 'version' => '2.1.0',
'phpstanVersionConstraint' => '>=2.0.0.0-dev, <3.0.0.0-dev',
),
);
@@ -58,7 +58,7 @@ final class GeneratedConfig
);
/** @var string|null */
- public const PHPSTAN_VERSION_CONSTRAINT = '>=2.1.8.0-dev, <3.0.0.0-dev';
+ public const PHPSTAN_VERSION_CONSTRAINT = '>=2.1.29.0-dev, <3.0.0.0-dev';
private function __construct()
{
diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md
index e56aa34d7..3a60ec8e1 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md
+++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/README.md
@@ -13,14 +13,12 @@
| `numericOperandsInArithmeticOperators` | Require numeric operand in `+$var`, `-$var`, `$var++`, `$var--`, `++$var` and `--$var`. |
| `numericOperandsInArithmeticOperators` | Require numeric operand in `$var++`, `$var--`, `++$var`and `--$var`. |
| `strictFunctionCalls` | These functions contain a `$strict` parameter for better type safety, it must be set to `true`: * `in_array` (3rd parameter) * `array_search` (3rd parameter) * `array_keys` (3rd parameter; only if the 2nd parameter `$search_value` is provided) * `base64_decode` (2nd parameter). |
-| `overwriteVariablesWithLoop` | Variables assigned in `while` loop condition and `for` loop initial assignment cannot be used after the loop. |
-| `overwriteVariablesWithLoop` | Variables set in foreach that's always looped thanks to non-empty arrays cannot be used after the loop. |
+| `overwriteVariablesWithLoop` | * Disallow overwriting variables with `foreach` key and value variables. * Disallow overwriting variables with `for` loop initial assignment. |
| `switchConditionsMatchingType` | Types in `switch` condition and `case` value must match. PHP compares them loosely by default and that can lead to unexpected results. |
| `dynamicCallOnStaticMethod` | Check that statically declared methods are called statically. |
| `disallowedEmpty` | Disallow `empty()` - it's a very loose comparison (see [manual](https://php.net/empty)), it's recommended to use more strict one. |
| `disallowedShortTernary` | Disallow short ternary operator (`?:`) - implies weak comparison, it's recommended to use null coalesce operator (`??`) or ternary operator with strict condition. |
| `noVariableVariables` | Disallow variable variables (`$$foo`, `$this->$method()` etc.). |
-| `overwriteVariablesWithLoop` | Disallow overwriting variables with foreach key and value variables. |
| `checkAlwaysTrueInstanceof`, `checkAlwaysTrueCheckTypeFunctionCall`, `checkAlwaysTrueStrictComparison` | Always true `instanceof`, type-checking `is_*` functions and strict comparisons `===`/`!==`. These checks can be turned off by setting `checkAlwaysTrueInstanceof`, `checkAlwaysTrueCheckTypeFunctionCall` and `checkAlwaysTrueStrictComparison` to false. |
| | Correct case for referenced and called function names. |
| `matchingInheritedMethodNames` | Correct case for inherited and implemented method names. |
diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json
index 2bbc44d69..bc72c5811 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json
+++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/composer.json
@@ -7,7 +7,7 @@
],
"require": {
"php": "^7.4 || ^8.0",
- "phpstan/phpstan": "^2.0.4"
+ "phpstan/phpstan": "^2.1.29"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon
index 7a63c4ec3..0def6d878 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon
+++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/rules.neon
@@ -11,6 +11,7 @@ parameters:
reportStaticMethodSignatures: true
reportMaybesInPropertyPhpDocTypes: true
reportWrongPhpDocTypeInVarTag: true
+ checkStrictPrintfPlaceholderTypes: true
strictRules:
allRules: true
disallowedLooseComparison: %strictRules.allRules%
diff --git a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php
index 77595810a..38c5e0339 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php
+++ b/tools/.phpstan/vendor/phpstan/phpstan-strict-rules/src/Rules/Classes/RequireParentConstructCallRule.php
@@ -104,26 +104,27 @@ private function callsParentConstruct(Node $parserNode): bool
*/
private function getParentConstructorClass($classReflection)
{
- while ($classReflection->getParentClass() !== false) {
- $constructor = $classReflection->getParentClass()->hasMethod('__construct') ? $classReflection->getParentClass()->getMethod('__construct') : null;
- $constructorWithClassName = $classReflection->getParentClass()->hasMethod($classReflection->getParentClass()->getName()) ? $classReflection->getParentClass()->getMethod($classReflection->getParentClass()->getName()) : null;
+ $parentClass = $classReflection->getParentClass();
+ while ($parentClass !== false) {
+ $constructor = $parentClass->hasMethod('__construct') ? $parentClass->getMethod('__construct') : null;
+ $constructorWithClassName = $parentClass->hasMethod($parentClass->getName()) ? $parentClass->getMethod($parentClass->getName()) : null;
if (
(
$constructor !== null
- && $constructor->getDeclaringClass()->getName() === $classReflection->getParentClass()->getName()
+ && $constructor->getDeclaringClass()->getName() === $parentClass->getName()
&& !$constructor->isAbstract()
&& !$constructor->isPrivate()
&& !$constructor->isDeprecated()
) || (
$constructorWithClassName !== null
- && $constructorWithClassName->getDeclaringClass()->getName() === $classReflection->getParentClass()->getName()
+ && $constructorWithClassName->getDeclaringClass()->getName() === $parentClass->getName()
&& !$constructorWithClassName->isAbstract()
)
) {
- return $classReflection->getParentClass();
+ return $parentClass;
}
- $classReflection = $classReflection->getParentClass();
+ $parentClass = $parentClass->getParentClass();
}
return false;
diff --git a/tools/.phpstan/vendor/phpstan/phpstan/README.md b/tools/.phpstan/vendor/phpstan/phpstan/README.md
index f4af4753a..689f9841c 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan/README.md
+++ b/tools/.phpstan/vendor/phpstan/phpstan/README.md
@@ -29,7 +29,10 @@ Want your logo here? [Learn more »](https://phpstan.org/sponsor)
### Gold Sponsors
+
+
+
@@ -60,7 +63,7 @@ Want your logo here? [Learn more »](https://phpstan.org/sponsor)
-
+
@@ -78,8 +81,6 @@ Want your logo here? [Learn more »](https://phpstan.org/sponsor)
-
-
diff --git a/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php b/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php
index a5d341bfd..889755e60 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php
+++ b/tools/.phpstan/vendor/phpstan/phpstan/bootstrap.php
@@ -92,6 +92,36 @@ final public static function loadClass(string $class): void {
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/Php81.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/bootstrap.php';
}
+
+ if (
+ PHP_VERSION_ID < 80300
+ && empty ($GLOBALS['__composer_autoload_files']['662a729f963d39afe703c9d9b7ab4a8c'])
+ && !class_exists(\Symfony\Polyfill\Php83\Php83::class, false)
+ ) {
+ $GLOBALS['__composer_autoload_files']['662a729f963d39afe703c9d9b7ab4a8c'] = true;
+ require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php83/Php83.php';
+ require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php83/bootstrap.php';
+ }
+
+ if (
+ PHP_VERSION_ID < 80400
+ && empty ($GLOBALS['__composer_autoload_files']['9d2b9fc6db0f153a0a149fefb182415e'])
+ && !class_exists(\Symfony\Polyfill\Php83\Php84::class, false)
+ ) {
+ $GLOBALS['__composer_autoload_files']['9d2b9fc6db0f153a0a149fefb182415e'] = true;
+ require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php84/Php84.php';
+ require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php84/bootstrap.php';
+ }
+
+ if (
+ PHP_VERSION_ID < 80500
+ && empty ($GLOBALS['__composer_autoload_files']['606a39d89246991a373564698c2d8383'])
+ && !class_exists(\Symfony\Polyfill\Php83\Php85::class, false)
+ ) {
+ $GLOBALS['__composer_autoload_files']['606a39d89246991a373564698c2d8383'] = true;
+ require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php85/Php85.php';
+ require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php85/bootstrap.php';
+ }
}
$filename = str_replace('\\', DIRECTORY_SEPARATOR, $class);
diff --git a/tools/.phpstan/vendor/phpstan/phpstan/composer.json b/tools/.phpstan/vendor/phpstan/phpstan/composer.json
index dc62c19ce..8b51d4b71 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan/composer.json
+++ b/tools/.phpstan/vendor/phpstan/phpstan/composer.json
@@ -16,6 +16,11 @@
"autoload": {
"files": ["bootstrap.php"]
},
+ "source": {
+ "type": "",
+ "url": "",
+ "reference": ""
+ },
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"forum": "https://github.com/phpstan/phpstan/discussions",
diff --git a/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar b/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar
index 81475a5c8..bc32b8047 100755
Binary files a/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar and b/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar differ
diff --git a/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar.asc b/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar.asc
index 76eb1c8f9..fec010bf3 100644
--- a/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar.asc
+++ b/tools/.phpstan/vendor/phpstan/phpstan/phpstan.phar.asc
@@ -1,16 +1,16 @@
-----BEGIN PGP SIGNATURE-----
-iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmiH0MMACgkQUcZzBf/C
-5cB4GRAAladOYQWUE2XHwwyhl/baoJEkPyPVbRmwZakva0Hg9kksH62fDwM944cE
-NZCFWnuKn/ujVFLnd4vCVctibsKjM9EPTdugAFrmEna52JGfFFrb+0oj5VblRDc9
-CaSK6WJwcIDZI8xkmeO5m1fKCWosq99FzrATJkpXnB8vn/ODTT9OLVvi/3mU8h6B
-TY5/RX3KKlc8iKE6tyQ+OljX4iMicyxC3CuISRuzP04hK7Mx+U4xAtaJa5Uv7aDH
-LKdWpkiRMnDK8GklrSyrpdAl80J3KFt5aKX9m/mwb77tSujDTOkoOU5GfaamSNKX
-CvyfuvoDn8tO5a8YeqbBsaje5GBWI9nuMrHG2zNEjNepYCk0N0dDclah1tu9sjs5
-mQNnEuI2Gj3b1O+rNbPRgwgOgUk7OjyRX/rvUotw013PQKclSWhEhY2mcpyla5bw
-vq1Xfjf5eXbu610cxQnSnFG13DlIleMTbqXFXD4ICpiKYeOOCLSQf8+X7+WVlUjW
-90/TrhV+VpsWILdjVBvceemgqcR5SyWRZe4Xx+FqsSFnJmmnRkK6d/sILnXrYug+
-FZVJutuUJN2HKa/vlm23BNOca8evEngUfqua/ykbanugJwBxy0HynfyzVRXa33Ud
-WQ2MXepBMBC4ogwjCnyAw5wfc2uAnWSXf4yHTcWfvAlzMmpDHfQ=
-=nV2o
+iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmkysr4ACgkQUcZzBf/C
+5cDE+RAAlbDueQxsbyo+wqm1sURcXT4fPbwdFAv1RXSYfZl7XraoagxYHssIHt9n
+TcKuBSOPOmlKgZ4FmVW05TRr0yoLZtckpRDCWuJoPzI9ilW2tiVi6AbEVhV0EqwL
+xVToGIzZXmti+X5Mm2OwQMCdpAbqpAy0mwmv/TudFiKdgZBzl5zHWDUSNCwP8s2m
+pLGBupQis6sA45+u28vLpyR7vn3mIq3F3hJgb5nrZlWt33jVJ7MDFnpGpeJ6+jBn
+/1BmefK0sXwN4EqaJbrRkHdRePISt2+jQXa/V/L3oJ0a3hVFufcGze41ARecSQ/0
+203aB0XcBtIFlshE5OIpXW+ibmgBabd+ggZEkRoS3OXtV5cJizLh49B+16A97QOr
+H0NWgLfY/W1u2t0Iu+WDT37Na5LPsuucW9vv9PwS6WY9eCr4Jbbmq1PtF0T17aqx
+STyPWNjs5A8NNjVUQ+Ufj/AAdH3hmNj4B5H6psxHD5UsFHoOLVFtwih4GdHrKXIZ
+N2lznVo8HDCcvKo6C4F8ejG1t4f5qBHNpYeEC9aVQVvXrBockQQz4lUclibW7MIe
+g33fXgO/GIm0dn2u0NCzjYof+TAVOfvtbK4GY0Z9BOlJcQnzu7V1LxqFamG5ef+u
+A2ABQa0HVEeKTomB3jzNQQVKw9QzWyfI21xSskfpFs4FXW3A5xc=
+=edJd
-----END PGP SIGNATURE-----
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/.editorconfig b/tools/.phpstan/vendor/tomasvotruba/type-coverage/.editorconfig
deleted file mode 100644
index bec95c449..000000000
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/.editorconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-root = true
-
-[*]
-charset = utf-8
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-indent_style = space
-indent_size = 4
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/.gitignore b/tools/.phpstan/vendor/tomasvotruba/type-coverage/.gitignore
deleted file mode 100644
index a6edf7a9b..000000000
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/vendor
-composer.lock
-
-.idea
-/.phpunit.cache
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md b/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md
index 5d673fdf0..cb7e71f7c 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md
@@ -2,25 +2,34 @@
-
-
-
+A PHPStan extension, to check and require minimal type coverage of PHP code.
+The type coverage rate = total count of **defined** type declarations / total count of **possible** type declarations.
+
+E.g. we have 10 methods, but only 7 have defined return type = 70 % return type coverage.
+
+---
+
PHPStan uses type declarations to determine the type of variables, properties and other expression. Sometimes it's hard to see what PHPStan errors are the important ones among thousands of others.
Instead of fixing all PHPStan errors at once, we can start with minimal require type coverage.
-What is the type coverage you ask? We have 4 type possible declarations in total here:
+
+## How to increase type coverage?
+
+Here we have 3 possible type declarations:
+
+* property,
+* param
+* and return type
```php
final class ConferenceFactory
{
- const SPEAKER_TAG = 'speaker';
-
private $talkFactory;
public function createConference(array $data)
@@ -32,20 +41,17 @@ final class ConferenceFactory
}
```
-*Note: Class constant types require PHP 8.3 to run.*
+The param type is defined as `array`.
-The param type is defined. But the property, return and constant types are missing.
+1 defined / 3 possible = **33.3 % type coverage**
-* 1 out of 4 = 25 % coverage
+
-Our code quality is only at one-quarter of its potential. Let's get to 100 %!
+Our code quality is only at one-third of its potential. Let's get to 100 %!
```diff
final class ConferenceFactory
{
-- public const SPEAKER_TAG = 'speaker';
-+ public const string SPEAKER_TAG = 'speaker';
-
- private $talkFactory;
+ private TalkFactory $talkFactory;
@@ -69,7 +75,7 @@ This technique is very simple to start even on legacy project. Also, you're now
composer require tomasvotruba/type-coverage --dev
```
-The package is available on PHP 7.2+ version in tagged releases.
+The package is available on PHP 7.2+.
@@ -86,6 +92,8 @@ parameters:
return: 50
param: 35.5
property: 70
+
+ # since PHP 8.3
constant: 85
```
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg b/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg
deleted file mode 100644
index cd219abe8..000000000
Binary files a/tools/.phpstan/vendor/tomasvotruba/type-coverage/docs/required_type_level.jpg and /dev/null differ
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php
deleted file mode 100644
index 224fbf73e..000000000
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/rector.php
+++ /dev/null
@@ -1,18 +0,0 @@
-withPaths([
- __DIR__ . '/src',
- __DIR__ . '/tests',
- ])
- ->withPhpSets()
- ->withPreparedSets(deadCode: true, codeQuality: true, codingStyle: true, typeDeclarations: true, privatization: true, naming: true)
- ->withImportNames(removeUnusedImports: true)
- ->withSkip([
- '*/Fixture/*',
- '*/Source/*',
- ]);
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php
index 4296b6135..b0babdd80 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/ConstantTypeDeclarationCollector.php
@@ -26,7 +26,7 @@ public function getNodeType(): string
/**
* @param ClassConstantsNode $node
- * @return mixed[]
+ * @return array)>>
*/
public function processNode(Node $node, Scope $scope): array
{
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php
index 893a60ba4..86c09ccde 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Collectors/PropertyTypeDeclarationCollector.php
@@ -27,7 +27,7 @@ public function getNodeType(): string
/**
* @param InClassNode $node
- * @return mixed[]
+ * @return array)>>
*/
public function processNode(Node $node, Scope $scope): array
{
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php
index f2fd6f2b9..311d103fa 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration.php
@@ -30,6 +30,7 @@ public function getRequiredPropertyTypeLevel()
public function isConstantTypeCoverageEnabled(): bool
{
+ // constant types are available only on PHP 8.3+
if (PHP_VERSION_ID < 80300) {
return false;
}
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php
index 7b4cec24f..4929c86fe 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Configuration/ScopeConfigurationResolver.php
@@ -51,7 +51,9 @@ public static function areFullPathsAnalysed(Scope $scope): bool
private static function getPrivateProperty(object $object, string $propertyName): object
{
$reflectionProperty = new ReflectionProperty($object, $propertyName);
- $reflectionProperty->setAccessible(true);
+ if (PHP_VERSION_ID < 80100) {
+ $reflectionProperty->setAccessible(true);
+ }
return $reflectionProperty->getValue($object);
}
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php
index b53da098c..7fe74a9e0 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ConstantTypeCoverageRule.php
@@ -48,8 +48,11 @@ final class ConstantTypeCoverageRule implements Rule
*/
private CollectorDataNormalizer $collectorDataNormalizer;
- public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
- {
+ public function __construct(
+ TypeCoverageFormatter $typeCoverageFormatter,
+ Configuration $configuration,
+ CollectorDataNormalizer $collectorDataNormalizer
+ ) {
$this->typeCoverageFormatter = $typeCoverageFormatter;
$this->configuration = $configuration;
$this->collectorDataNormalizer = $collectorDataNormalizer;
@@ -69,6 +72,11 @@ public function getNodeType(): string
*/
public function processNode(Node $node, Scope $scope): array
{
+ // enable only on PHP 8.3+
+ if (PHP_VERSION_ID < 80300) {
+ return [];
+ }
+
// if only subpaths are analysed, skip as data will be false positive
if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) {
return [];
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php
index ed300eea6..81df4581f 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ParamTypeCoverageRule.php
@@ -48,8 +48,11 @@ final class ParamTypeCoverageRule implements Rule
*/
private CollectorDataNormalizer $collectorDataNormalizer;
- public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
- {
+ public function __construct(
+ TypeCoverageFormatter $typeCoverageFormatter,
+ Configuration $configuration,
+ CollectorDataNormalizer $collectorDataNormalizer
+ ) {
$this->typeCoverageFormatter = $typeCoverageFormatter;
$this->configuration = $configuration;
$this->collectorDataNormalizer = $collectorDataNormalizer;
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php
index 27dcec2cf..74edaa063 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/PropertyTypeCoverageRule.php
@@ -48,8 +48,11 @@ final class PropertyTypeCoverageRule implements Rule
*/
private CollectorDataNormalizer $collectorDataNormalizer;
- public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
- {
+ public function __construct(
+ TypeCoverageFormatter $typeCoverageFormatter,
+ Configuration $configuration,
+ CollectorDataNormalizer $collectorDataNormalizer
+ ) {
$this->typeCoverageFormatter = $typeCoverageFormatter;
$this->configuration = $configuration;
$this->collectorDataNormalizer = $collectorDataNormalizer;
diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php
index c7e7fbaa6..cee1d65a3 100644
--- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php
+++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/src/Rules/ReturnTypeCoverageRule.php
@@ -48,8 +48,11 @@ final class ReturnTypeCoverageRule implements Rule
*/
private CollectorDataNormalizer $collectorDataNormalizer;
- public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
- {
+ public function __construct(
+ TypeCoverageFormatter $typeCoverageFormatter,
+ Configuration $configuration,
+ CollectorDataNormalizer $collectorDataNormalizer
+ ) {
$this->typeCoverageFormatter = $typeCoverageFormatter;
$this->configuration = $configuration;
$this->collectorDataNormalizer = $collectorDataNormalizer;
diff --git a/tools/composer b/tools/composer
index 4f53d5aa0..02740c582 100755
Binary files a/tools/composer and b/tools/composer differ
diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer
index c1ebe8cd5..04a544723 100755
Binary files a/tools/php-cs-fixer and b/tools/php-cs-fixer differ
diff --git a/tools/phpstan~11.0 b/tools/phpstan~11.0
new file mode 100755
index 000000000..c542df205
Binary files /dev/null and b/tools/phpstan~11.0 differ