summaryrefslogtreecommitdiff
path: root/vendor/phpunit/php-code-coverage/src/Node
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/phpunit/php-code-coverage/src/Node')
-rw-r--r--vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php253
-rw-r--r--vendor/phpunit/php-code-coverage/src/Node/Builder.php264
-rw-r--r--vendor/phpunit/php-code-coverage/src/Node/CrapIndex.php50
-rw-r--r--vendor/phpunit/php-code-coverage/src/Node/Directory.php440
-rw-r--r--vendor/phpunit/php-code-coverage/src/Node/File.php651
-rw-r--r--vendor/phpunit/php-code-coverage/src/Node/Iterator.php92
6 files changed, 1750 insertions, 0 deletions
diff --git a/vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php b/vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php
new file mode 100644
index 000000000..60d6391d3
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php
@@ -0,0 +1,253 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use const DIRECTORY_SEPARATOR;
+use function array_merge;
+use function str_replace;
+use function substr;
+use Countable;
+use SebastianBergmann\CodeCoverage\Util\Percentage;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+abstract class AbstractNode implements Countable
+{
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var string
+ */
+ private $pathAsString;
+
+ /**
+ * @var array
+ */
+ private $pathAsArray;
+
+ /**
+ * @var AbstractNode
+ */
+ private $parent;
+
+ /**
+ * @var string
+ */
+ private $id;
+
+ public function __construct(string $name, self $parent = null)
+ {
+ if (substr($name, -1) === DIRECTORY_SEPARATOR) {
+ $name = substr($name, 0, -1);
+ }
+
+ $this->name = $name;
+ $this->parent = $parent;
+ }
+
+ public function name(): string
+ {
+ return $this->name;
+ }
+
+ public function id(): string
+ {
+ if ($this->id === null) {
+ $parent = $this->parent();
+
+ if ($parent === null) {
+ $this->id = 'index';
+ } else {
+ $parentId = $parent->id();
+
+ if ($parentId === 'index') {
+ $this->id = str_replace(':', '_', $this->name);
+ } else {
+ $this->id = $parentId . '/' . $this->name;
+ }
+ }
+ }
+
+ return $this->id;
+ }
+
+ public function pathAsString(): string
+ {
+ if ($this->pathAsString === null) {
+ if ($this->parent === null) {
+ $this->pathAsString = $this->name;
+ } else {
+ $this->pathAsString = $this->parent->pathAsString() . DIRECTORY_SEPARATOR . $this->name;
+ }
+ }
+
+ return $this->pathAsString;
+ }
+
+ public function pathAsArray(): array
+ {
+ if ($this->pathAsArray === null) {
+ if ($this->parent === null) {
+ $this->pathAsArray = [];
+ } else {
+ $this->pathAsArray = $this->parent->pathAsArray();
+ }
+
+ $this->pathAsArray[] = $this;
+ }
+
+ return $this->pathAsArray;
+ }
+
+ public function parent(): ?self
+ {
+ return $this->parent;
+ }
+
+ public function percentageOfTestedClasses(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfTestedClasses(),
+ $this->numberOfClasses(),
+ );
+ }
+
+ public function percentageOfTestedTraits(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfTestedTraits(),
+ $this->numberOfTraits(),
+ );
+ }
+
+ public function percentageOfTestedClassesAndTraits(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfTestedClassesAndTraits(),
+ $this->numberOfClassesAndTraits(),
+ );
+ }
+
+ public function percentageOfTestedFunctions(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfTestedFunctions(),
+ $this->numberOfFunctions(),
+ );
+ }
+
+ public function percentageOfTestedMethods(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfTestedMethods(),
+ $this->numberOfMethods(),
+ );
+ }
+
+ public function percentageOfTestedFunctionsAndMethods(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfTestedFunctionsAndMethods(),
+ $this->numberOfFunctionsAndMethods(),
+ );
+ }
+
+ public function percentageOfExecutedLines(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfExecutedLines(),
+ $this->numberOfExecutableLines(),
+ );
+ }
+
+ public function percentageOfExecutedBranches(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfExecutedBranches(),
+ $this->numberOfExecutableBranches()
+ );
+ }
+
+ public function percentageOfExecutedPaths(): Percentage
+ {
+ return Percentage::fromFractionAndTotal(
+ $this->numberOfExecutedPaths(),
+ $this->numberOfExecutablePaths()
+ );
+ }
+
+ public function numberOfClassesAndTraits(): int
+ {
+ return $this->numberOfClasses() + $this->numberOfTraits();
+ }
+
+ public function numberOfTestedClassesAndTraits(): int
+ {
+ return $this->numberOfTestedClasses() + $this->numberOfTestedTraits();
+ }
+
+ public function classesAndTraits(): array
+ {
+ return array_merge($this->classes(), $this->traits());
+ }
+
+ public function numberOfFunctionsAndMethods(): int
+ {
+ return $this->numberOfFunctions() + $this->numberOfMethods();
+ }
+
+ public function numberOfTestedFunctionsAndMethods(): int
+ {
+ return $this->numberOfTestedFunctions() + $this->numberOfTestedMethods();
+ }
+
+ abstract public function classes(): array;
+
+ abstract public function traits(): array;
+
+ abstract public function functions(): array;
+
+ /**
+ * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
+ */
+ abstract public function linesOfCode(): array;
+
+ abstract public function numberOfExecutableLines(): int;
+
+ abstract public function numberOfExecutedLines(): int;
+
+ abstract public function numberOfExecutableBranches(): int;
+
+ abstract public function numberOfExecutedBranches(): int;
+
+ abstract public function numberOfExecutablePaths(): int;
+
+ abstract public function numberOfExecutedPaths(): int;
+
+ abstract public function numberOfClasses(): int;
+
+ abstract public function numberOfTestedClasses(): int;
+
+ abstract public function numberOfTraits(): int;
+
+ abstract public function numberOfTestedTraits(): int;
+
+ abstract public function numberOfMethods(): int;
+
+ abstract public function numberOfTestedMethods(): int;
+
+ abstract public function numberOfFunctions(): int;
+
+ abstract public function numberOfTestedFunctions(): int;
+}
diff --git a/vendor/phpunit/php-code-coverage/src/Node/Builder.php b/vendor/phpunit/php-code-coverage/src/Node/Builder.php
new file mode 100644
index 000000000..6d11c7798
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/Node/Builder.php
@@ -0,0 +1,264 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use const DIRECTORY_SEPARATOR;
+use function array_shift;
+use function basename;
+use function count;
+use function dirname;
+use function explode;
+use function implode;
+use function is_file;
+use function str_replace;
+use function strpos;
+use function substr;
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData;
+use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final class Builder
+{
+ /**
+ * @var FileAnalyser
+ */
+ private $analyser;
+
+ public function __construct(FileAnalyser $analyser)
+ {
+ $this->analyser = $analyser;
+ }
+
+ public function build(CodeCoverage $coverage): Directory
+ {
+ $data = clone $coverage->getData(); // clone because path munging is destructive to the original data
+ $commonPath = $this->reducePaths($data);
+ $root = new Directory(
+ $commonPath,
+ null
+ );
+
+ $this->addItems(
+ $root,
+ $this->buildDirectoryStructure($data),
+ $coverage->getTests()
+ );
+
+ return $root;
+ }
+
+ private function addItems(Directory $root, array $items, array $tests): void
+ {
+ foreach ($items as $key => $value) {
+ $key = (string) $key;
+
+ if (substr($key, -2) === '/f') {
+ $key = substr($key, 0, -2);
+ $filename = $root->pathAsString() . DIRECTORY_SEPARATOR . $key;
+
+ if (is_file($filename)) {
+ $root->addFile(
+ new File(
+ $key,
+ $root,
+ $value['lineCoverage'],
+ $value['functionCoverage'],
+ $tests,
+ $this->analyser->classesIn($filename),
+ $this->analyser->traitsIn($filename),
+ $this->analyser->functionsIn($filename),
+ $this->analyser->linesOfCodeFor($filename)
+ )
+ );
+ }
+ } else {
+ $child = $root->addDirectory($key);
+
+ $this->addItems($child, $value, $tests);
+ }
+ }
+ }
+
+ /**
+ * Builds an array representation of the directory structure.
+ *
+ * For instance,
+ *
+ * <code>
+ * Array
+ * (
+ * [Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * </code>
+ *
+ * is transformed into
+ *
+ * <code>
+ * Array
+ * (
+ * [.] => Array
+ * (
+ * [Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * )
+ * </code>
+ */
+ private function buildDirectoryStructure(ProcessedCodeCoverageData $data): array
+ {
+ $result = [];
+
+ foreach ($data->coveredFiles() as $originalPath) {
+ $path = explode(DIRECTORY_SEPARATOR, $originalPath);
+ $pointer = &$result;
+ $max = count($path);
+
+ for ($i = 0; $i < $max; $i++) {
+ $type = '';
+
+ if ($i === ($max - 1)) {
+ $type = '/f';
+ }
+
+ $pointer = &$pointer[$path[$i] . $type];
+ }
+
+ $pointer = [
+ 'lineCoverage' => $data->lineCoverage()[$originalPath] ?? [],
+ 'functionCoverage' => $data->functionCoverage()[$originalPath] ?? [],
+ ];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Reduces the paths by cutting the longest common start path.
+ *
+ * For instance,
+ *
+ * <code>
+ * Array
+ * (
+ * [/home/sb/Money/Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [/home/sb/Money/MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * </code>
+ *
+ * is reduced to
+ *
+ * <code>
+ * Array
+ * (
+ * [Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * </code>
+ */
+ private function reducePaths(ProcessedCodeCoverageData $coverage): string
+ {
+ if (empty($coverage->coveredFiles())) {
+ return '.';
+ }
+
+ $commonPath = '';
+ $paths = $coverage->coveredFiles();
+
+ if (count($paths) === 1) {
+ $commonPath = dirname($paths[0]) . DIRECTORY_SEPARATOR;
+ $coverage->renameFile($paths[0], basename($paths[0]));
+
+ return $commonPath;
+ }
+
+ $max = count($paths);
+
+ for ($i = 0; $i < $max; $i++) {
+ // strip phar:// prefixes
+ if (strpos($paths[$i], 'phar://') === 0) {
+ $paths[$i] = substr($paths[$i], 7);
+ $paths[$i] = str_replace('/', DIRECTORY_SEPARATOR, $paths[$i]);
+ }
+ $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);
+
+ if (empty($paths[$i][0])) {
+ $paths[$i][0] = DIRECTORY_SEPARATOR;
+ }
+ }
+
+ $done = false;
+ $max = count($paths);
+
+ while (!$done) {
+ for ($i = 0; $i < $max - 1; $i++) {
+ if (!isset($paths[$i][0]) ||
+ !isset($paths[$i + 1][0]) ||
+ $paths[$i][0] !== $paths[$i + 1][0]) {
+ $done = true;
+
+ break;
+ }
+ }
+
+ if (!$done) {
+ $commonPath .= $paths[0][0];
+
+ if ($paths[0][0] !== DIRECTORY_SEPARATOR) {
+ $commonPath .= DIRECTORY_SEPARATOR;
+ }
+
+ for ($i = 0; $i < $max; $i++) {
+ array_shift($paths[$i]);
+ }
+ }
+ }
+
+ $original = $coverage->coveredFiles();
+ $max = count($original);
+
+ for ($i = 0; $i < $max; $i++) {
+ $coverage->renameFile($original[$i], implode(DIRECTORY_SEPARATOR, $paths[$i]));
+ }
+
+ return substr($commonPath, 0, -1);
+ }
+}
diff --git a/vendor/phpunit/php-code-coverage/src/Node/CrapIndex.php b/vendor/phpunit/php-code-coverage/src/Node/CrapIndex.php
new file mode 100644
index 000000000..30b86b7d2
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/Node/CrapIndex.php
@@ -0,0 +1,50 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use function sprintf;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final class CrapIndex
+{
+ /**
+ * @var int
+ */
+ private $cyclomaticComplexity;
+
+ /**
+ * @var float
+ */
+ private $codeCoverage;
+
+ public function __construct(int $cyclomaticComplexity, float $codeCoverage)
+ {
+ $this->cyclomaticComplexity = $cyclomaticComplexity;
+ $this->codeCoverage = $codeCoverage;
+ }
+
+ public function asString(): string
+ {
+ if ($this->codeCoverage === 0.0) {
+ return (string) ($this->cyclomaticComplexity ** 2 + $this->cyclomaticComplexity);
+ }
+
+ if ($this->codeCoverage >= 95) {
+ return (string) $this->cyclomaticComplexity;
+ }
+
+ return sprintf(
+ '%01.2F',
+ $this->cyclomaticComplexity ** 2 * (1 - $this->codeCoverage / 100) ** 3 + $this->cyclomaticComplexity
+ );
+ }
+}
diff --git a/vendor/phpunit/php-code-coverage/src/Node/Directory.php b/vendor/phpunit/php-code-coverage/src/Node/Directory.php
new file mode 100644
index 000000000..d6ee07e4d
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/Node/Directory.php
@@ -0,0 +1,440 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use function array_merge;
+use function count;
+use IteratorAggregate;
+use RecursiveIteratorIterator;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final class Directory extends AbstractNode implements IteratorAggregate
+{
+ /**
+ * @var AbstractNode[]
+ */
+ private $children = [];
+
+ /**
+ * @var Directory[]
+ */
+ private $directories = [];
+
+ /**
+ * @var File[]
+ */
+ private $files = [];
+
+ /**
+ * @var array
+ */
+ private $classes;
+
+ /**
+ * @var array
+ */
+ private $traits;
+
+ /**
+ * @var array
+ */
+ private $functions;
+
+ /**
+ * @psalm-var null|array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
+ */
+ private $linesOfCode;
+
+ /**
+ * @var int
+ */
+ private $numFiles = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutableLines = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutedLines = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutableBranches = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutedBranches = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutablePaths = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutedPaths = -1;
+
+ /**
+ * @var int
+ */
+ private $numClasses = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedClasses = -1;
+
+ /**
+ * @var int
+ */
+ private $numTraits = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedTraits = -1;
+
+ /**
+ * @var int
+ */
+ private $numMethods = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedMethods = -1;
+
+ /**
+ * @var int
+ */
+ private $numFunctions = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedFunctions = -1;
+
+ public function count(): int
+ {
+ if ($this->numFiles === -1) {
+ $this->numFiles = 0;
+
+ foreach ($this->children as $child) {
+ $this->numFiles += count($child);
+ }
+ }
+
+ return $this->numFiles;
+ }
+
+ public function getIterator(): RecursiveIteratorIterator
+ {
+ return new RecursiveIteratorIterator(
+ new Iterator($this),
+ RecursiveIteratorIterator::SELF_FIRST
+ );
+ }
+
+ public function addDirectory(string $name): self
+ {
+ $directory = new self($name, $this);
+
+ $this->children[] = $directory;
+ $this->directories[] = &$this->children[count($this->children) - 1];
+
+ return $directory;
+ }
+
+ public function addFile(File $file): void
+ {
+ $this->children[] = $file;
+ $this->files[] = &$this->children[count($this->children) - 1];
+
+ $this->numExecutableLines = -1;
+ $this->numExecutedLines = -1;
+ }
+
+ public function directories(): array
+ {
+ return $this->directories;
+ }
+
+ public function files(): array
+ {
+ return $this->files;
+ }
+
+ public function children(): array
+ {
+ return $this->children;
+ }
+
+ public function classes(): array
+ {
+ if ($this->classes === null) {
+ $this->classes = [];
+
+ foreach ($this->children as $child) {
+ $this->classes = array_merge(
+ $this->classes,
+ $child->classes()
+ );
+ }
+ }
+
+ return $this->classes;
+ }
+
+ public function traits(): array
+ {
+ if ($this->traits === null) {
+ $this->traits = [];
+
+ foreach ($this->children as $child) {
+ $this->traits = array_merge(
+ $this->traits,
+ $child->traits()
+ );
+ }
+ }
+
+ return $this->traits;
+ }
+
+ public function functions(): array
+ {
+ if ($this->functions === null) {
+ $this->functions = [];
+
+ foreach ($this->children as $child) {
+ $this->functions = array_merge(
+ $this->functions,
+ $child->functions()
+ );
+ }
+ }
+
+ return $this->functions;
+ }
+
+ /**
+ * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
+ */
+ public function linesOfCode(): array
+ {
+ if ($this->linesOfCode === null) {
+ $this->linesOfCode = [
+ 'linesOfCode' => 0,
+ 'commentLinesOfCode' => 0,
+ 'nonCommentLinesOfCode' => 0,
+ ];
+
+ foreach ($this->children as $child) {
+ $childLinesOfCode = $child->linesOfCode();
+
+ $this->linesOfCode['linesOfCode'] += $childLinesOfCode['linesOfCode'];
+ $this->linesOfCode['commentLinesOfCode'] += $childLinesOfCode['commentLinesOfCode'];
+ $this->linesOfCode['nonCommentLinesOfCode'] += $childLinesOfCode['nonCommentLinesOfCode'];
+ }
+ }
+
+ return $this->linesOfCode;
+ }
+
+ public function numberOfExecutableLines(): int
+ {
+ if ($this->numExecutableLines === -1) {
+ $this->numExecutableLines = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutableLines += $child->numberOfExecutableLines();
+ }
+ }
+
+ return $this->numExecutableLines;
+ }
+
+ public function numberOfExecutedLines(): int
+ {
+ if ($this->numExecutedLines === -1) {
+ $this->numExecutedLines = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutedLines += $child->numberOfExecutedLines();
+ }
+ }
+
+ return $this->numExecutedLines;
+ }
+
+ public function numberOfExecutableBranches(): int
+ {
+ if ($this->numExecutableBranches === -1) {
+ $this->numExecutableBranches = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutableBranches += $child->numberOfExecutableBranches();
+ }
+ }
+
+ return $this->numExecutableBranches;
+ }
+
+ public function numberOfExecutedBranches(): int
+ {
+ if ($this->numExecutedBranches === -1) {
+ $this->numExecutedBranches = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutedBranches += $child->numberOfExecutedBranches();
+ }
+ }
+
+ return $this->numExecutedBranches;
+ }
+
+ public function numberOfExecutablePaths(): int
+ {
+ if ($this->numExecutablePaths === -1) {
+ $this->numExecutablePaths = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutablePaths += $child->numberOfExecutablePaths();
+ }
+ }
+
+ return $this->numExecutablePaths;
+ }
+
+ public function numberOfExecutedPaths(): int
+ {
+ if ($this->numExecutedPaths === -1) {
+ $this->numExecutedPaths = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutedPaths += $child->numberOfExecutedPaths();
+ }
+ }
+
+ return $this->numExecutedPaths;
+ }
+
+ public function numberOfClasses(): int
+ {
+ if ($this->numClasses === -1) {
+ $this->numClasses = 0;
+
+ foreach ($this->children as $child) {
+ $this->numClasses += $child->numberOfClasses();
+ }
+ }
+
+ return $this->numClasses;
+ }
+
+ public function numberOfTestedClasses(): int
+ {
+ if ($this->numTestedClasses === -1) {
+ $this->numTestedClasses = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedClasses += $child->numberOfTestedClasses();
+ }
+ }
+
+ return $this->numTestedClasses;
+ }
+
+ public function numberOfTraits(): int
+ {
+ if ($this->numTraits === -1) {
+ $this->numTraits = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTraits += $child->numberOfTraits();
+ }
+ }
+
+ return $this->numTraits;
+ }
+
+ public function numberOfTestedTraits(): int
+ {
+ if ($this->numTestedTraits === -1) {
+ $this->numTestedTraits = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedTraits += $child->numberOfTestedTraits();
+ }
+ }
+
+ return $this->numTestedTraits;
+ }
+
+ public function numberOfMethods(): int
+ {
+ if ($this->numMethods === -1) {
+ $this->numMethods = 0;
+
+ foreach ($this->children as $child) {
+ $this->numMethods += $child->numberOfMethods();
+ }
+ }
+
+ return $this->numMethods;
+ }
+
+ public function numberOfTestedMethods(): int
+ {
+ if ($this->numTestedMethods === -1) {
+ $this->numTestedMethods = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedMethods += $child->numberOfTestedMethods();
+ }
+ }
+
+ return $this->numTestedMethods;
+ }
+
+ public function numberOfFunctions(): int
+ {
+ if ($this->numFunctions === -1) {
+ $this->numFunctions = 0;
+
+ foreach ($this->children as $child) {
+ $this->numFunctions += $child->numberOfFunctions();
+ }
+ }
+
+ return $this->numFunctions;
+ }
+
+ public function numberOfTestedFunctions(): int
+ {
+ if ($this->numTestedFunctions === -1) {
+ $this->numTestedFunctions = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedFunctions += $child->numberOfTestedFunctions();
+ }
+ }
+
+ return $this->numTestedFunctions;
+ }
+}
diff --git a/vendor/phpunit/php-code-coverage/src/Node/File.php b/vendor/phpunit/php-code-coverage/src/Node/File.php
new file mode 100644
index 000000000..af3764e41
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/Node/File.php
@@ -0,0 +1,651 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use function array_filter;
+use function count;
+use function range;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final class File extends AbstractNode
+{
+ /**
+ * @var array
+ */
+ private $lineCoverageData;
+
+ /**
+ * @var array
+ */
+ private $functionCoverageData;
+
+ /**
+ * @var array
+ */
+ private $testData;
+
+ /**
+ * @var int
+ */
+ private $numExecutableLines = 0;
+
+ /**
+ * @var int
+ */
+ private $numExecutedLines = 0;
+
+ /**
+ * @var int
+ */
+ private $numExecutableBranches = 0;
+
+ /**
+ * @var int
+ */
+ private $numExecutedBranches = 0;
+
+ /**
+ * @var int
+ */
+ private $numExecutablePaths = 0;
+
+ /**
+ * @var int
+ */
+ private $numExecutedPaths = 0;
+
+ /**
+ * @var array
+ */
+ private $classes = [];
+
+ /**
+ * @var array
+ */
+ private $traits = [];
+
+ /**
+ * @var array
+ */
+ private $functions = [];
+
+ /**
+ * @psalm-var array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
+ */
+ private $linesOfCode;
+
+ /**
+ * @var int
+ */
+ private $numClasses;
+
+ /**
+ * @var int
+ */
+ private $numTestedClasses = 0;
+
+ /**
+ * @var int
+ */
+ private $numTraits;
+
+ /**
+ * @var int
+ */
+ private $numTestedTraits = 0;
+
+ /**
+ * @var int
+ */
+ private $numMethods;
+
+ /**
+ * @var int
+ */
+ private $numTestedMethods;
+
+ /**
+ * @var int
+ */
+ private $numTestedFunctions;
+
+ /**
+ * @var array
+ */
+ private $codeUnitsByLine = [];
+
+ /**
+ * @psalm-param array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} $linesOfCode
+ */
+ public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, array $linesOfCode)
+ {
+ parent::__construct($name, $parent);
+
+ $this->lineCoverageData = $lineCoverageData;
+ $this->functionCoverageData = $functionCoverageData;
+ $this->testData = $testData;
+ $this->linesOfCode = $linesOfCode;
+
+ $this->calculateStatistics($classes, $traits, $functions);
+ }
+
+ public function count(): int
+ {
+ return 1;
+ }
+
+ public function lineCoverageData(): array
+ {
+ return $this->lineCoverageData;
+ }
+
+ public function functionCoverageData(): array
+ {
+ return $this->functionCoverageData;
+ }
+
+ public function testData(): array
+ {
+ return $this->testData;
+ }
+
+ public function classes(): array
+ {
+ return $this->classes;
+ }
+
+ public function traits(): array
+ {
+ return $this->traits;
+ }
+
+ public function functions(): array
+ {
+ return $this->functions;
+ }
+
+ /**
+ * @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
+ */
+ public function linesOfCode(): array
+ {
+ return $this->linesOfCode;
+ }
+
+ public function numberOfExecutableLines(): int
+ {
+ return $this->numExecutableLines;
+ }
+
+ public function numberOfExecutedLines(): int
+ {
+ return $this->numExecutedLines;
+ }
+
+ public function numberOfExecutableBranches(): int
+ {
+ return $this->numExecutableBranches;
+ }
+
+ public function numberOfExecutedBranches(): int
+ {
+ return $this->numExecutedBranches;
+ }
+
+ public function numberOfExecutablePaths(): int
+ {
+ return $this->numExecutablePaths;
+ }
+
+ public function numberOfExecutedPaths(): int
+ {
+ return $this->numExecutedPaths;
+ }
+
+ public function numberOfClasses(): int
+ {
+ if ($this->numClasses === null) {
+ $this->numClasses = 0;
+
+ foreach ($this->classes as $class) {
+ foreach ($class['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numClasses++;
+
+ continue 2;
+ }
+ }
+ }
+ }
+
+ return $this->numClasses;
+ }
+
+ public function numberOfTestedClasses(): int
+ {
+ return $this->numTestedClasses;
+ }
+
+ public function numberOfTraits(): int
+ {
+ if ($this->numTraits === null) {
+ $this->numTraits = 0;
+
+ foreach ($this->traits as $trait) {
+ foreach ($trait['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numTraits++;
+
+ continue 2;
+ }
+ }
+ }
+ }
+
+ return $this->numTraits;
+ }
+
+ public function numberOfTestedTraits(): int
+ {
+ return $this->numTestedTraits;
+ }
+
+ public function numberOfMethods(): int
+ {
+ if ($this->numMethods === null) {
+ $this->numMethods = 0;
+
+ foreach ($this->classes as $class) {
+ foreach ($class['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numMethods++;
+ }
+ }
+ }
+
+ foreach ($this->traits as $trait) {
+ foreach ($trait['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numMethods++;
+ }
+ }
+ }
+ }
+
+ return $this->numMethods;
+ }
+
+ public function numberOfTestedMethods(): int
+ {
+ if ($this->numTestedMethods === null) {
+ $this->numTestedMethods = 0;
+
+ foreach ($this->classes as $class) {
+ foreach ($class['methods'] as $method) {
+ if ($method['executableLines'] > 0 &&
+ $method['coverage'] === 100) {
+ $this->numTestedMethods++;
+ }
+ }
+ }
+
+ foreach ($this->traits as $trait) {
+ foreach ($trait['methods'] as $method) {
+ if ($method['executableLines'] > 0 &&
+ $method['coverage'] === 100) {
+ $this->numTestedMethods++;
+ }
+ }
+ }
+ }
+
+ return $this->numTestedMethods;
+ }
+
+ public function numberOfFunctions(): int
+ {
+ return count($this->functions);
+ }
+
+ public function numberOfTestedFunctions(): int
+ {
+ if ($this->numTestedFunctions === null) {
+ $this->numTestedFunctions = 0;
+
+ foreach ($this->functions as $function) {
+ if ($function['executableLines'] > 0 &&
+ $function['coverage'] === 100) {
+ $this->numTestedFunctions++;
+ }
+ }
+ }
+
+ return $this->numTestedFunctions;
+ }
+
+ private function calculateStatistics(array $classes, array $traits, array $functions): void
+ {
+ foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
+ $this->codeUnitsByLine[$lineNumber] = [];
+ }
+
+ $this->processClasses($classes);
+ $this->processTraits($traits);
+ $this->processFunctions($functions);
+
+ foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
+ if (isset($this->lineCoverageData[$lineNumber])) {
+ foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
+ $codeUnit['executableLines']++;
+ }
+
+ unset($codeUnit);
+
+ $this->numExecutableLines++;
+
+ if (count($this->lineCoverageData[$lineNumber]) > 0) {
+ foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
+ $codeUnit['executedLines']++;
+ }
+
+ unset($codeUnit);
+
+ $this->numExecutedLines++;
+ }
+ }
+ }
+
+ foreach ($this->traits as &$trait) {
+ foreach ($trait['methods'] as &$method) {
+ $methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100;
+ $methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0;
+ $methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0;
+
+ $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage;
+ $method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString();
+
+ $trait['ccn'] += $method['ccn'];
+ }
+
+ unset($method);
+
+ $traitLineCoverage = $trait['executableLines'] ? ($trait['executedLines'] / $trait['executableLines']) * 100 : 100;
+ $traitBranchCoverage = $trait['executableBranches'] ? ($trait['executedBranches'] / $trait['executableBranches']) * 100 : 0;
+ $traitPathCoverage = $trait['executablePaths'] ? ($trait['executedPaths'] / $trait['executablePaths']) * 100 : 0;
+
+ $trait['coverage'] = $traitBranchCoverage ?: $traitLineCoverage;
+ $trait['crap'] = (new CrapIndex($trait['ccn'], $traitPathCoverage ?: $traitLineCoverage))->asString();
+
+ if ($trait['executableLines'] > 0 && $trait['coverage'] === 100) {
+ $this->numTestedClasses++;
+ }
+ }
+
+ unset($trait);
+
+ foreach ($this->classes as &$class) {
+ foreach ($class['methods'] as &$method) {
+ $methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100;
+ $methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0;
+ $methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0;
+
+ $method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage;
+ $method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString();
+
+ $class['ccn'] += $method['ccn'];
+ }
+
+ unset($method);
+
+ $classLineCoverage = $class['executableLines'] ? ($class['executedLines'] / $class['executableLines']) * 100 : 100;
+ $classBranchCoverage = $class['executableBranches'] ? ($class['executedBranches'] / $class['executableBranches']) * 100 : 0;
+ $classPathCoverage = $class['executablePaths'] ? ($class['executedPaths'] / $class['executablePaths']) * 100 : 0;
+
+ $class['coverage'] = $classBranchCoverage ?: $classLineCoverage;
+ $class['crap'] = (new CrapIndex($class['ccn'], $classPathCoverage ?: $classLineCoverage))->asString();
+
+ if ($class['executableLines'] > 0 && $class['coverage'] === 100) {
+ $this->numTestedClasses++;
+ }
+ }
+
+ unset($class);
+
+ foreach ($this->functions as &$function) {
+ $functionLineCoverage = $function['executableLines'] ? ($function['executedLines'] / $function['executableLines']) * 100 : 100;
+ $functionBranchCoverage = $function['executableBranches'] ? ($function['executedBranches'] / $function['executableBranches']) * 100 : 0;
+ $functionPathCoverage = $function['executablePaths'] ? ($function['executedPaths'] / $function['executablePaths']) * 100 : 0;
+
+ $function['coverage'] = $functionBranchCoverage ?: $functionLineCoverage;
+ $function['crap'] = (new CrapIndex($function['ccn'], $functionPathCoverage ?: $functionLineCoverage))->asString();
+
+ if ($function['coverage'] === 100) {
+ $this->numTestedFunctions++;
+ }
+ }
+ }
+
+ private function processClasses(array $classes): void
+ {
+ $link = $this->id() . '.html#';
+
+ foreach ($classes as $className => $class) {
+ $this->classes[$className] = [
+ 'className' => $className,
+ 'namespace' => $class['namespace'],
+ 'methods' => [],
+ 'startLine' => $class['startLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'executableBranches' => 0,
+ 'executedBranches' => 0,
+ 'executablePaths' => 0,
+ 'executedPaths' => 0,
+ 'ccn' => 0,
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'link' => $link . $class['startLine'],
+ ];
+
+ foreach ($class['methods'] as $methodName => $method) {
+ $methodData = $this->newMethod($className, $methodName, $method, $link);
+ $this->classes[$className]['methods'][$methodName] = $methodData;
+
+ $this->classes[$className]['executableBranches'] += $methodData['executableBranches'];
+ $this->classes[$className]['executedBranches'] += $methodData['executedBranches'];
+ $this->classes[$className]['executablePaths'] += $methodData['executablePaths'];
+ $this->classes[$className]['executedPaths'] += $methodData['executedPaths'];
+
+ $this->numExecutableBranches += $methodData['executableBranches'];
+ $this->numExecutedBranches += $methodData['executedBranches'];
+ $this->numExecutablePaths += $methodData['executablePaths'];
+ $this->numExecutedPaths += $methodData['executedPaths'];
+
+ foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
+ $this->codeUnitsByLine[$lineNumber] = [
+ &$this->classes[$className],
+ &$this->classes[$className]['methods'][$methodName],
+ ];
+ }
+ }
+ }
+ }
+
+ private function processTraits(array $traits): void
+ {
+ $link = $this->id() . '.html#';
+
+ foreach ($traits as $traitName => $trait) {
+ $this->traits[$traitName] = [
+ 'traitName' => $traitName,
+ 'namespace' => $trait['namespace'],
+ 'methods' => [],
+ 'startLine' => $trait['startLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'executableBranches' => 0,
+ 'executedBranches' => 0,
+ 'executablePaths' => 0,
+ 'executedPaths' => 0,
+ 'ccn' => 0,
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'link' => $link . $trait['startLine'],
+ ];
+
+ foreach ($trait['methods'] as $methodName => $method) {
+ $methodData = $this->newMethod($traitName, $methodName, $method, $link);
+ $this->traits[$traitName]['methods'][$methodName] = $methodData;
+
+ $this->traits[$traitName]['executableBranches'] += $methodData['executableBranches'];
+ $this->traits[$traitName]['executedBranches'] += $methodData['executedBranches'];
+ $this->traits[$traitName]['executablePaths'] += $methodData['executablePaths'];
+ $this->traits[$traitName]['executedPaths'] += $methodData['executedPaths'];
+
+ $this->numExecutableBranches += $methodData['executableBranches'];
+ $this->numExecutedBranches += $methodData['executedBranches'];
+ $this->numExecutablePaths += $methodData['executablePaths'];
+ $this->numExecutedPaths += $methodData['executedPaths'];
+
+ foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
+ $this->codeUnitsByLine[$lineNumber] = [
+ &$this->traits[$traitName],
+ &$this->traits[$traitName]['methods'][$methodName],
+ ];
+ }
+ }
+ }
+ }
+
+ private function processFunctions(array $functions): void
+ {
+ $link = $this->id() . '.html#';
+
+ foreach ($functions as $functionName => $function) {
+ $this->functions[$functionName] = [
+ 'functionName' => $functionName,
+ 'namespace' => $function['namespace'],
+ 'signature' => $function['signature'],
+ 'startLine' => $function['startLine'],
+ 'endLine' => $function['endLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'executableBranches' => 0,
+ 'executedBranches' => 0,
+ 'executablePaths' => 0,
+ 'executedPaths' => 0,
+ 'ccn' => $function['ccn'],
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'link' => $link . $function['startLine'],
+ ];
+
+ foreach (range($function['startLine'], $function['endLine']) as $lineNumber) {
+ $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]];
+ }
+
+ if (isset($this->functionCoverageData[$functionName]['branches'])) {
+ $this->functions[$functionName]['executableBranches'] = count(
+ $this->functionCoverageData[$functionName]['branches']
+ );
+
+ $this->functions[$functionName]['executedBranches'] = count(
+ array_filter(
+ $this->functionCoverageData[$functionName]['branches'],
+ static function (array $branch)
+ {
+ return (bool) $branch['hit'];
+ }
+ )
+ );
+ }
+
+ if (isset($this->functionCoverageData[$functionName]['paths'])) {
+ $this->functions[$functionName]['executablePaths'] = count(
+ $this->functionCoverageData[$functionName]['paths']
+ );
+
+ $this->functions[$functionName]['executedPaths'] = count(
+ array_filter(
+ $this->functionCoverageData[$functionName]['paths'],
+ static function (array $path)
+ {
+ return (bool) $path['hit'];
+ }
+ )
+ );
+ }
+
+ $this->numExecutableBranches += $this->functions[$functionName]['executableBranches'];
+ $this->numExecutedBranches += $this->functions[$functionName]['executedBranches'];
+ $this->numExecutablePaths += $this->functions[$functionName]['executablePaths'];
+ $this->numExecutedPaths += $this->functions[$functionName]['executedPaths'];
+ }
+ }
+
+ private function newMethod(string $className, string $methodName, array $method, string $link): array
+ {
+ $methodData = [
+ 'methodName' => $methodName,
+ 'visibility' => $method['visibility'],
+ 'signature' => $method['signature'],
+ 'startLine' => $method['startLine'],
+ 'endLine' => $method['endLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'executableBranches' => 0,
+ 'executedBranches' => 0,
+ 'executablePaths' => 0,
+ 'executedPaths' => 0,
+ 'ccn' => $method['ccn'],
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'link' => $link . $method['startLine'],
+ ];
+
+ $key = $className . '->' . $methodName;
+
+ if (isset($this->functionCoverageData[$key]['branches'])) {
+ $methodData['executableBranches'] = count(
+ $this->functionCoverageData[$key]['branches']
+ );
+
+ $methodData['executedBranches'] = count(
+ array_filter(
+ $this->functionCoverageData[$key]['branches'],
+ static function (array $branch)
+ {
+ return (bool) $branch['hit'];
+ }
+ )
+ );
+ }
+
+ if (isset($this->functionCoverageData[$key]['paths'])) {
+ $methodData['executablePaths'] = count(
+ $this->functionCoverageData[$key]['paths']
+ );
+
+ $methodData['executedPaths'] = count(
+ array_filter(
+ $this->functionCoverageData[$key]['paths'],
+ static function (array $path)
+ {
+ return (bool) $path['hit'];
+ }
+ )
+ );
+ }
+
+ return $methodData;
+ }
+}
diff --git a/vendor/phpunit/php-code-coverage/src/Node/Iterator.php b/vendor/phpunit/php-code-coverage/src/Node/Iterator.php
new file mode 100644
index 000000000..d0a5a0654
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/Node/Iterator.php
@@ -0,0 +1,92 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use function count;
+use RecursiveIterator;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final class Iterator implements RecursiveIterator
+{
+ /**
+ * @var int
+ */
+ private $position;
+
+ /**
+ * @var AbstractNode[]
+ */
+ private $nodes;
+
+ public function __construct(Directory $node)
+ {
+ $this->nodes = $node->children();
+ }
+
+ /**
+ * Rewinds the Iterator to the first element.
+ */
+ public function rewind(): void
+ {
+ $this->position = 0;
+ }
+
+ /**
+ * Checks if there is a current element after calls to rewind() or next().
+ */
+ public function valid(): bool
+ {
+ return $this->position < count($this->nodes);
+ }
+
+ /**
+ * Returns the key of the current element.
+ */
+ public function key(): int
+ {
+ return $this->position;
+ }
+
+ /**
+ * Returns the current element.
+ */
+ public function current(): ?AbstractNode
+ {
+ return $this->valid() ? $this->nodes[$this->position] : null;
+ }
+
+ /**
+ * Moves forward to next element.
+ */
+ public function next(): void
+ {
+ $this->position++;
+ }
+
+ /**
+ * Returns the sub iterator for the current element.
+ *
+ * @return Iterator
+ */
+ public function getChildren(): self
+ {
+ return new self($this->nodes[$this->position]);
+ }
+
+ /**
+ * Checks whether the current element has children.
+ */
+ public function hasChildren(): bool
+ {
+ return $this->nodes[$this->position] instanceof Directory;
+ }
+}