3
* This file is part of the PHP_CodeCoverage package.
5
* (c) Sebastian Bergmann <sebastian@phpunit.de>
7
* For the full copyright and license information, please view the LICENSE
8
* file that was distributed with this source code.
12
* Represents a file in the code coverage information tree.
15
* @package CodeCoverage
16
* @author Sebastian Bergmann <sebastian@phpunit.de>
17
* @copyright Sebastian Bergmann <sebastian@phpunit.de>
18
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
19
* @link http://github.com/sebastianbergmann/php-code-coverage
20
* @since Class available since Release 1.1.0
22
class PHP_CodeCoverage_Report_Node_File extends PHP_CodeCoverage_Report_Node
27
protected $coverageData;
37
protected $numExecutableLines = 0;
42
protected $numExecutedLines = 0;
47
protected $classes = array();
52
protected $traits = array();
57
protected $functions = array();
62
protected $linesOfCode = array();
67
protected $numTestedTraits = 0;
72
protected $numTestedClasses = 0;
77
protected $numMethods = null;
82
protected $numTestedMethods = null;
87
protected $numTestedFunctions = null;
92
protected $startLines = array();
97
protected $endLines = array();
102
protected $cacheTokens;
107
* @param string $name
108
* @param PHP_CodeCoverage_Report_Node $parent
109
* @param array $coverageData
110
* @param array $testData
111
* @param boolean $cacheTokens
112
* @throws PHP_CodeCoverage_Exception
114
public function __construct($name, PHP_CodeCoverage_Report_Node $parent, array $coverageData, array $testData, $cacheTokens)
116
if (!is_bool($cacheTokens)) {
117
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
122
parent::__construct($name, $parent);
124
$this->coverageData = $coverageData;
125
$this->testData = $testData;
126
$this->cacheTokens = $cacheTokens;
128
$this->calculateStatistics();
132
* Returns the number of files in/under this node.
136
public function count()
142
* Returns the code coverage data of this node.
146
public function getCoverageData()
148
return $this->coverageData;
152
* Returns the test data of this node.
156
public function getTestData()
158
return $this->testData;
162
* Returns the classes of this node.
166
public function getClasses()
168
return $this->classes;
172
* Returns the traits of this node.
176
public function getTraits()
178
return $this->traits;
182
* Returns the functions of this node.
186
public function getFunctions()
188
return $this->functions;
192
* Returns the LOC/CLOC/NCLOC of this node.
196
public function getLinesOfCode()
198
return $this->linesOfCode;
202
* Returns the number of executable lines.
206
public function getNumExecutableLines()
208
return $this->numExecutableLines;
212
* Returns the number of executed lines.
216
public function getNumExecutedLines()
218
return $this->numExecutedLines;
222
* Returns the number of classes.
226
public function getNumClasses()
228
return count($this->classes);
232
* Returns the number of tested classes.
236
public function getNumTestedClasses()
238
return $this->numTestedClasses;
242
* Returns the number of traits.
246
public function getNumTraits()
248
return count($this->traits);
252
* Returns the number of tested traits.
256
public function getNumTestedTraits()
258
return $this->numTestedTraits;
262
* Returns the number of methods.
266
public function getNumMethods()
268
if ($this->numMethods === null) {
269
$this->numMethods = 0;
271
foreach ($this->classes as $class) {
272
foreach ($class['methods'] as $method) {
273
if ($method['executableLines'] > 0) {
279
foreach ($this->traits as $trait) {
280
foreach ($trait['methods'] as $method) {
281
if ($method['executableLines'] > 0) {
288
return $this->numMethods;
292
* Returns the number of tested methods.
296
public function getNumTestedMethods()
298
if ($this->numTestedMethods === null) {
299
$this->numTestedMethods = 0;
301
foreach ($this->classes as $class) {
302
foreach ($class['methods'] as $method) {
303
if ($method['executableLines'] > 0 &&
304
$method['coverage'] == 100) {
305
$this->numTestedMethods++;
310
foreach ($this->traits as $trait) {
311
foreach ($trait['methods'] as $method) {
312
if ($method['executableLines'] > 0 &&
313
$method['coverage'] == 100) {
314
$this->numTestedMethods++;
320
return $this->numTestedMethods;
324
* Returns the number of functions.
328
public function getNumFunctions()
330
return count($this->functions);
334
* Returns the number of tested functions.
338
public function getNumTestedFunctions()
340
if ($this->numTestedFunctions === null) {
341
$this->numTestedFunctions = 0;
343
foreach ($this->functions as $function) {
344
if ($function['executableLines'] > 0 &&
345
$function['coverage'] == 100) {
346
$this->numTestedFunctions++;
351
return $this->numTestedFunctions;
355
* Calculates coverage statistics for the file.
357
protected function calculateStatistics()
359
if ($this->cacheTokens) {
360
$tokens = PHP_Token_Stream_CachingFactory::get($this->getPath());
362
$tokens = new PHP_Token_Stream($this->getPath());
365
$this->processClasses($tokens);
366
$this->processTraits($tokens);
367
$this->processFunctions($tokens);
368
$this->linesOfCode = $tokens->getLinesOfCode();
371
for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) {
372
if (isset($this->startLines[$lineNumber])) {
373
// Start line of a class.
374
if (isset($this->startLines[$lineNumber]['className'])) {
375
$currentClass = &$this->startLines[$lineNumber];
378
// Start line of a trait.
379
elseif (isset($this->startLines[$lineNumber]['traitName'])) {
380
$currentTrait = &$this->startLines[$lineNumber];
383
// Start line of a method.
384
elseif (isset($this->startLines[$lineNumber]['methodName'])) {
385
$currentMethod = &$this->startLines[$lineNumber];
388
// Start line of a function.
389
elseif (isset($this->startLines[$lineNumber]['functionName'])) {
390
$currentFunction = &$this->startLines[$lineNumber];
394
if (isset($this->coverageData[$lineNumber]) &&
395
$this->coverageData[$lineNumber] !== null) {
396
if (isset($currentClass)) {
397
$currentClass['executableLines']++;
400
if (isset($currentTrait)) {
401
$currentTrait['executableLines']++;
404
if (isset($currentMethod)) {
405
$currentMethod['executableLines']++;
408
if (isset($currentFunction)) {
409
$currentFunction['executableLines']++;
412
$this->numExecutableLines++;
414
if (count($this->coverageData[$lineNumber]) > 0) {
415
if (isset($currentClass)) {
416
$currentClass['executedLines']++;
419
if (isset($currentTrait)) {
420
$currentTrait['executedLines']++;
423
if (isset($currentMethod)) {
424
$currentMethod['executedLines']++;
427
if (isset($currentFunction)) {
428
$currentFunction['executedLines']++;
431
$this->numExecutedLines++;
435
if (isset($this->endLines[$lineNumber])) {
436
// End line of a class.
437
if (isset($this->endLines[$lineNumber]['className'])) {
438
unset($currentClass);
441
// End line of a trait.
442
elseif (isset($this->endLines[$lineNumber]['traitName'])) {
443
unset($currentTrait);
446
// End line of a method.
447
elseif (isset($this->endLines[$lineNumber]['methodName'])) {
448
unset($currentMethod);
451
// End line of a function.
452
elseif (isset($this->endLines[$lineNumber]['functionName'])) {
453
unset($currentFunction);
458
foreach ($this->traits as &$trait) {
459
foreach ($trait['methods'] as &$method) {
460
if ($method['executableLines'] > 0) {
461
$method['coverage'] = ($method['executedLines'] /
462
$method['executableLines']) * 100;
464
$method['coverage'] = 100;
467
$method['crap'] = $this->crap(
468
$method['ccn'], $method['coverage']
471
$trait['ccn'] += $method['ccn'];
474
if ($trait['executableLines'] > 0) {
475
$trait['coverage'] = ($trait['executedLines'] /
476
$trait['executableLines']) * 100;
478
$trait['coverage'] = 100;
481
if ($trait['coverage'] == 100) {
482
$this->numTestedClasses++;
485
$trait['crap'] = $this->crap(
486
$trait['ccn'], $trait['coverage']
490
foreach ($this->classes as &$class) {
491
foreach ($class['methods'] as &$method) {
492
if ($method['executableLines'] > 0) {
493
$method['coverage'] = ($method['executedLines'] /
494
$method['executableLines']) * 100;
496
$method['coverage'] = 100;
499
$method['crap'] = $this->crap(
500
$method['ccn'], $method['coverage']
503
$class['ccn'] += $method['ccn'];
506
if ($class['executableLines'] > 0) {
507
$class['coverage'] = ($class['executedLines'] /
508
$class['executableLines']) * 100;
510
$class['coverage'] = 100;
513
if ($class['coverage'] == 100) {
514
$this->numTestedClasses++;
517
$class['crap'] = $this->crap(
518
$class['ccn'], $class['coverage']
524
* @param PHP_Token_Stream $tokens
526
protected function processClasses(PHP_Token_Stream $tokens)
528
$classes = $tokens->getClasses();
531
$link = $this->getId() . '.html#';
533
foreach ($classes as $className => $class) {
534
$this->classes[$className] = array(
535
'className' => $className,
536
'methods' => array(),
537
'startLine' => $class['startLine'],
538
'executableLines' => 0,
539
'executedLines' => 0,
543
'package' => $class['package'],
544
'link' => $link . $class['startLine']
547
$this->startLines[$class['startLine']] = &$this->classes[$className];
548
$this->endLines[$class['endLine']] = &$this->classes[$className];
550
foreach ($class['methods'] as $methodName => $method) {
551
$this->classes[$className]['methods'][$methodName] = array(
552
'methodName' => $methodName,
553
'signature' => $method['signature'],
554
'startLine' => $method['startLine'],
555
'endLine' => $method['endLine'],
556
'executableLines' => 0,
557
'executedLines' => 0,
558
'ccn' => $method['ccn'],
561
'link' => $link . $method['startLine']
564
$this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName];
565
$this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName];
571
* @param PHP_Token_Stream $tokens
573
protected function processTraits(PHP_Token_Stream $tokens)
575
$traits = $tokens->getTraits();
578
$link = $this->getId() . '.html#';
580
foreach ($traits as $traitName => $trait) {
581
$this->traits[$traitName] = array(
582
'traitName' => $traitName,
583
'methods' => array(),
584
'startLine' => $trait['startLine'],
585
'executableLines' => 0,
586
'executedLines' => 0,
590
'package' => $trait['package'],
591
'link' => $link . $trait['startLine']
594
$this->startLines[$trait['startLine']] = &$this->traits[$traitName];
595
$this->endLines[$trait['endLine']] = &$this->traits[$traitName];
597
foreach ($trait['methods'] as $methodName => $method) {
598
$this->traits[$traitName]['methods'][$methodName] = array(
599
'methodName' => $methodName,
600
'signature' => $method['signature'],
601
'startLine' => $method['startLine'],
602
'endLine' => $method['endLine'],
603
'executableLines' => 0,
604
'executedLines' => 0,
605
'ccn' => $method['ccn'],
608
'link' => $link . $method['startLine']
611
$this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName];
612
$this->endLines[$method['endLine']] = &$this->traits[$traitName]['methods'][$methodName];
618
* @param PHP_Token_Stream $tokens
620
protected function processFunctions(PHP_Token_Stream $tokens)
622
$functions = $tokens->getFunctions();
625
$link = $this->getId() . '.html#';
627
foreach ($functions as $functionName => $function) {
628
$this->functions[$functionName] = array(
629
'functionName' => $functionName,
630
'signature' => $function['signature'],
631
'startLine' => $function['startLine'],
632
'executableLines' => 0,
633
'executedLines' => 0,
634
'ccn' => $function['ccn'],
637
'link' => $link . $function['startLine']
640
$this->startLines[$function['startLine']] = &$this->functions[$functionName];
641
$this->endLines[$function['endLine']] = &$this->functions[$functionName];
646
* Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code
647
* based on its cyclomatic complexity and percentage of code coverage.
649
* @param integer $ccn
650
* @param float $coverage
652
* @since Method available since Release 1.2.0
654
protected function crap($ccn, $coverage)
656
if ($coverage == 0) {
657
return (string) pow($ccn, 2) + $ccn;
660
if ($coverage >= 95) {
661
return (string) $ccn;
665
'%01.2F', pow($ccn, 2) * pow(1 - $coverage/100, 3) + $ccn