| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- <?php
- /*
- * This file is part of the php-code-coverage package.
- *
- * (c) Sebastian Bergmann <sebastian@phpunit.de>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace SebastianBergmann\CodeCoverage\Node;
- /**
- * Represents a file in the code coverage information tree.
- */
- final class File extends AbstractNode
- {
- /**
- * @var array
- */
- private $coverageData;
- /**
- * @var array
- */
- private $testData;
- /**
- * @var int
- */
- private $numExecutableLines = 0;
- /**
- * @var int
- */
- private $numExecutedLines = 0;
- /**
- * @var array
- */
- private $classes = [];
- /**
- * @var array
- */
- private $traits = [];
- /**
- * @var array
- */
- private $functions = [];
- /**
- * @var array
- */
- 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 bool
- */
- private $cacheTokens;
- /**
- * @var array
- */
- private $codeUnitsByLine = [];
- public function __construct(string $name, AbstractNode $parent, array $coverageData, array $testData, bool $cacheTokens)
- {
- parent::__construct($name, $parent);
- $this->coverageData = $coverageData;
- $this->testData = $testData;
- $this->cacheTokens = $cacheTokens;
- $this->calculateStatistics();
- }
- /**
- * Returns the number of files in/under this node.
- */
- public function count(): int
- {
- return 1;
- }
- /**
- * Returns the code coverage data of this node.
- */
- public function getCoverageData(): array
- {
- return $this->coverageData;
- }
- /**
- * Returns the test data of this node.
- */
- public function getTestData(): array
- {
- return $this->testData;
- }
- /**
- * Returns the classes of this node.
- */
- public function getClasses(): array
- {
- return $this->classes;
- }
- /**
- * Returns the traits of this node.
- */
- public function getTraits(): array
- {
- return $this->traits;
- }
- /**
- * Returns the functions of this node.
- */
- public function getFunctions(): array
- {
- return $this->functions;
- }
- /**
- * Returns the LOC/CLOC/NCLOC of this node.
- */
- public function getLinesOfCode(): array
- {
- return $this->linesOfCode;
- }
- /**
- * Returns the number of executable lines.
- */
- public function getNumExecutableLines(): int
- {
- return $this->numExecutableLines;
- }
- /**
- * Returns the number of executed lines.
- */
- public function getNumExecutedLines(): int
- {
- return $this->numExecutedLines;
- }
- /**
- * Returns the number of classes.
- */
- public function getNumClasses(): 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;
- }
- /**
- * Returns the number of tested classes.
- */
- public function getNumTestedClasses(): int
- {
- return $this->numTestedClasses;
- }
- /**
- * Returns the number of traits.
- */
- public function getNumTraits(): 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;
- }
- /**
- * Returns the number of tested traits.
- */
- public function getNumTestedTraits(): int
- {
- return $this->numTestedTraits;
- }
- /**
- * Returns the number of methods.
- */
- public function getNumMethods(): 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;
- }
- /**
- * Returns the number of tested methods.
- */
- public function getNumTestedMethods(): 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;
- }
- /**
- * Returns the number of functions.
- */
- public function getNumFunctions(): int
- {
- return \count($this->functions);
- }
- /**
- * Returns the number of tested functions.
- */
- public function getNumTestedFunctions(): 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(): void
- {
- if ($this->cacheTokens) {
- $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath());
- } else {
- $tokens = new \PHP_Token_Stream($this->getPath());
- }
- $this->linesOfCode = $tokens->getLinesOfCode();
- foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) {
- $this->codeUnitsByLine[$lineNumber] = [];
- }
- try {
- $this->processClasses($tokens);
- $this->processTraits($tokens);
- $this->processFunctions($tokens);
- } catch (\OutOfBoundsException $e) {
- // This can happen with PHP_Token_Stream if the file is syntactically invalid,
- // and probably affects a file that wasn't executed.
- }
- unset($tokens);
- foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) {
- if (isset($this->coverageData[$lineNumber])) {
- foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
- $codeUnit['executableLines']++;
- }
- unset($codeUnit);
- $this->numExecutableLines++;
- if (\count($this->coverageData[$lineNumber]) > 0) {
- foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
- $codeUnit['executedLines']++;
- }
- unset($codeUnit);
- $this->numExecutedLines++;
- }
- }
- }
- foreach ($this->traits as &$trait) {
- foreach ($trait['methods'] as &$method) {
- if ($method['executableLines'] > 0) {
- $method['coverage'] = ($method['executedLines'] /
- $method['executableLines']) * 100;
- } else {
- $method['coverage'] = 100;
- }
- $method['crap'] = $this->crap(
- $method['ccn'],
- $method['coverage']
- );
- $trait['ccn'] += $method['ccn'];
- }
- unset($method);
- if ($trait['executableLines'] > 0) {
- $trait['coverage'] = ($trait['executedLines'] /
- $trait['executableLines']) * 100;
- if ($trait['coverage'] === 100) {
- $this->numTestedClasses++;
- }
- } else {
- $trait['coverage'] = 100;
- }
- $trait['crap'] = $this->crap(
- $trait['ccn'],
- $trait['coverage']
- );
- }
- unset($trait);
- foreach ($this->classes as &$class) {
- foreach ($class['methods'] as &$method) {
- if ($method['executableLines'] > 0) {
- $method['coverage'] = ($method['executedLines'] /
- $method['executableLines']) * 100;
- } else {
- $method['coverage'] = 100;
- }
- $method['crap'] = $this->crap(
- $method['ccn'],
- $method['coverage']
- );
- $class['ccn'] += $method['ccn'];
- }
- unset($method);
- if ($class['executableLines'] > 0) {
- $class['coverage'] = ($class['executedLines'] /
- $class['executableLines']) * 100;
- if ($class['coverage'] === 100) {
- $this->numTestedClasses++;
- }
- } else {
- $class['coverage'] = 100;
- }
- $class['crap'] = $this->crap(
- $class['ccn'],
- $class['coverage']
- );
- }
- unset($class);
- foreach ($this->functions as &$function) {
- if ($function['executableLines'] > 0) {
- $function['coverage'] = ($function['executedLines'] /
- $function['executableLines']) * 100;
- } else {
- $function['coverage'] = 100;
- }
- if ($function['coverage'] === 100) {
- $this->numTestedFunctions++;
- }
- $function['crap'] = $this->crap(
- $function['ccn'],
- $function['coverage']
- );
- }
- }
- private function processClasses(\PHP_Token_Stream $tokens): void
- {
- $classes = $tokens->getClasses();
- $link = $this->getId() . '.html#';
- foreach ($classes as $className => $class) {
- if (\strpos($className, 'anonymous') === 0) {
- continue;
- }
- if (!empty($class['package']['namespace'])) {
- $className = $class['package']['namespace'] . '\\' . $className;
- }
- $this->classes[$className] = [
- 'className' => $className,
- 'methods' => [],
- 'startLine' => $class['startLine'],
- 'executableLines' => 0,
- 'executedLines' => 0,
- 'ccn' => 0,
- 'coverage' => 0,
- 'crap' => 0,
- 'package' => $class['package'],
- 'link' => $link . $class['startLine'],
- ];
- foreach ($class['methods'] as $methodName => $method) {
- if (\strpos($methodName, 'anonymous') === 0) {
- continue;
- }
- $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);
- foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) {
- $this->codeUnitsByLine[$lineNumber] = [
- &$this->classes[$className],
- &$this->classes[$className]['methods'][$methodName],
- ];
- }
- }
- }
- }
- private function processTraits(\PHP_Token_Stream $tokens): void
- {
- $traits = $tokens->getTraits();
- $link = $this->getId() . '.html#';
- foreach ($traits as $traitName => $trait) {
- $this->traits[$traitName] = [
- 'traitName' => $traitName,
- 'methods' => [],
- 'startLine' => $trait['startLine'],
- 'executableLines' => 0,
- 'executedLines' => 0,
- 'ccn' => 0,
- 'coverage' => 0,
- 'crap' => 0,
- 'package' => $trait['package'],
- 'link' => $link . $trait['startLine'],
- ];
- foreach ($trait['methods'] as $methodName => $method) {
- if (\strpos($methodName, 'anonymous') === 0) {
- continue;
- }
- $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);
- foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) {
- $this->codeUnitsByLine[$lineNumber] = [
- &$this->traits[$traitName],
- &$this->traits[$traitName]['methods'][$methodName],
- ];
- }
- }
- }
- }
- private function processFunctions(\PHP_Token_Stream $tokens): void
- {
- $functions = $tokens->getFunctions();
- $link = $this->getId() . '.html#';
- foreach ($functions as $functionName => $function) {
- if (\strpos($functionName, 'anonymous') === 0) {
- continue;
- }
- $this->functions[$functionName] = [
- 'functionName' => $functionName,
- 'signature' => $function['signature'],
- 'startLine' => $function['startLine'],
- 'executableLines' => 0,
- 'executedLines' => 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]];
- }
- }
- }
- private function crap(int $ccn, float $coverage): string
- {
- if ($coverage === 0) {
- return (string) ($ccn ** 2 + $ccn);
- }
- if ($coverage >= 95) {
- return (string) $ccn;
- }
- return \sprintf(
- '%01.2F',
- $ccn ** 2 * (1 - $coverage / 100) ** 3 + $ccn
- );
- }
- private function newMethod(string $methodName, array $method, string $link): array
- {
- return [
- 'methodName' => $methodName,
- 'visibility' => $method['visibility'],
- 'signature' => $method['signature'],
- 'startLine' => $method['startLine'],
- 'endLine' => $method['endLine'],
- 'executableLines' => 0,
- 'executedLines' => 0,
- 'ccn' => $method['ccn'],
- 'coverage' => 0,
- 'crap' => 0,
- 'link' => $link . $method['startLine'],
- ];
- }
- }
|