5 * Copyright (c) 2009-2011, Sebastian Bergmann <sb@sebastian-bergmann.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
38 * @package CodeCoverage
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2009-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42 * @link http://github.com/sebastianbergmann/php-code-coverage
43 * @since File available since Release 1.1.0
47 * Represents a file in the code coverage information tree.
50 * @package CodeCoverage
51 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
52 * @copyright 2009-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
53 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
54 * @version Release: 1.1.1
55 * @link http://github.com/sebastianbergmann/php-code-coverage
56 * @since Class available since Release 1.1.0
58 class PHP_CodeCoverage_Report_Node_File extends PHP_CodeCoverage_Report_Node
63 protected $coverageData;
73 protected $ignoredLines;
78 protected $numExecutableLines = 0;
83 protected $numExecutedLines = 0;
88 protected $classes = array();
93 protected $traits = array();
98 protected $functions = array();
103 protected $linesOfCode = array();
108 protected $numTestedTraits = 0;
113 protected $numTestedClasses = 0;
118 protected $numMethods = NULL;
123 protected $numTestedMethods = NULL;
128 protected $numTestedFunctions = NULL;
133 protected $startLines = array();
138 protected $endLines = array();
143 protected $cacheTokens;
148 * @param string $name
149 * @param PHP_CodeCoverage_Report_Node $parent
150 * @param array $coverageData
151 * @param array $testData
152 * @param boolean $cacheTokens
154 public function __construct($name, PHP_CodeCoverage_Report_Node $parent, array $coverageData, array $testData, $cacheTokens)
156 if (!is_bool($cacheTokens)) {
157 throw new InvalidArgumentException;
160 parent::__construct($name, $parent);
162 $this->coverageData = $coverageData;
163 $this->testData = $testData;
164 $this->ignoredLines = PHP_CodeCoverage_Util::getLinesToBeIgnored(
165 $this->getPath(), $cacheTokens
167 $this->cacheTokens = $cacheTokens;
169 $this->calculateStatistics();
173 * Returns the number of files in/under this node.
177 public function count()
183 * Returns the code coverage data of this node.
187 public function getCoverageData()
189 return $this->coverageData;
193 * Returns the test data of this node.
197 public function getTestData()
199 return $this->testData;
205 public function getIgnoredLines()
207 return $this->ignoredLines;
211 * Returns the classes of this node.
215 public function getClasses()
217 return $this->classes;
221 * Returns the traits of this node.
225 public function getTraits()
227 return $this->traits;
231 * Returns the functions of this node.
235 public function getFunctions()
237 return $this->functions;
241 * Returns the LOC/CLOC/NCLOC of this node.
245 public function getLinesOfCode()
247 return $this->linesOfCode;
251 * Returns the number of executable lines.
255 public function getNumExecutableLines()
257 return $this->numExecutableLines;
261 * Returns the number of executed lines.
265 public function getNumExecutedLines()
267 return $this->numExecutedLines;
271 * Returns the number of classes.
275 public function getNumClasses()
277 return count($this->classes);
281 * Returns the number of tested classes.
285 public function getNumTestedClasses()
287 return $this->numTestedClasses;
291 * Returns the number of traits.
295 public function getNumTraits()
297 return count($this->traits);
301 * Returns the number of tested traits.
305 public function getNumTestedTraits()
307 return $this->numTestedTraits;
311 * Returns the number of methods.
315 public function getNumMethods()
317 if ($this->numMethods === NULL) {
318 $this->numMethods = 0;
320 foreach ($this->classes as $class) {
321 foreach ($class['methods'] as $method) {
322 if ($method['executableLines'] > 0) {
328 foreach ($this->traits as $trait) {
329 foreach ($trait['methods'] as $method) {
330 if ($method['executableLines'] > 0) {
337 return $this->numMethods;
341 * Returns the number of tested methods.
345 public function getNumTestedMethods()
347 if ($this->numTestedMethods === NULL) {
348 $this->numTestedMethods = 0;
350 foreach ($this->classes as $class) {
351 foreach ($class['methods'] as $method) {
352 if ($method['executableLines'] > 0 &&
353 $method['coverage'] == 100) {
354 $this->numTestedMethods++;
359 foreach ($this->traits as $trait) {
360 foreach ($trait['methods'] as $method) {
361 if ($method['executableLines'] > 0 &&
362 $method['coverage'] == 100) {
363 $this->numTestedMethods++;
369 return $this->numTestedMethods;
373 * Returns the number of functions.
377 public function getNumFunctions()
379 return count($this->functions);
383 * Returns the number of tested functions.
387 public function getNumTestedFunctions()
389 if ($this->numTestedFunctions === NULL) {
390 $this->numTestedFunctions = 0;
392 foreach ($this->functions as $function) {
393 if ($function['executableLines'] > 0 &&
394 $function['coverage'] == 100) {
395 $this->numTestedFunctions++;
400 return $this->numTestedFunctions;
404 * Calculates coverage statistics for the file.
406 protected function calculateStatistics()
408 if ($this->cacheTokens) {
409 $tokens = PHP_Token_Stream_CachingFactory::get($this->getPath());
411 $tokens = new PHP_Token_Stream($this->getPath());
414 $this->processClasses($tokens);
415 $this->processTraits($tokens);
416 $this->processFunctions($tokens);
417 $this->linesOfCode = $tokens->getLinesOfCode();
420 for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) {
421 if (isset($this->startLines[$lineNumber])) {
422 // Start line of a class.
423 if (isset($this->startLines[$lineNumber]['className'])) {
424 $currentClass = &$this->startLines[$lineNumber];
427 // Start line of a trait.
428 else if (isset($this->startLines[$lineNumber]['traitName'])) {
429 $currentTrait = &$this->startLines[$lineNumber];
432 // Start line of a method.
433 else if (isset($this->startLines[$lineNumber]['methodName'])) {
434 $currentMethod = &$this->startLines[$lineNumber];
437 // Start line of a function.
438 else if (isset($this->startLines[$lineNumber]['functionName'])) {
439 $currentFunction = &$this->startLines[$lineNumber];
443 if (!isset($this->ignoredLines[$lineNumber]) &&
444 isset($this->coverageData[$lineNumber]) &&
445 $this->coverageData[$lineNumber] !== NULL) {
446 if (isset($currentClass)) {
447 $currentClass['executableLines']++;
450 if (isset($currentTrait)) {
451 $currentTrait['executableLines']++;
454 if (isset($currentMethod)) {
455 $currentMethod['executableLines']++;
458 if (isset($currentFunction)) {
459 $currentFunction['executableLines']++;
462 $this->numExecutableLines++;
464 if (count($this->coverageData[$lineNumber]) > 0 ||
465 isset($this->ignoredLines[$lineNumber])) {
466 if (isset($currentClass)) {
467 $currentClass['executedLines']++;
470 if (isset($currentTrait)) {
471 $currentTrait['executedLines']++;
474 if (isset($currentMethod)) {
475 $currentMethod['executedLines']++;
478 if (isset($currentFunction)) {
479 $currentFunction['executedLines']++;
482 $this->numExecutedLines++;
486 if (isset($this->endLines[$lineNumber])) {
487 // End line of a class.
488 if (isset($this->endLines[$lineNumber]['className'])) {
489 unset($currentClass);
492 // End line of a trait.
493 else if (isset($this->endLines[$lineNumber]['traitName'])) {
494 unset($currentTrait);
497 // End line of a method.
498 else if (isset($this->endLines[$lineNumber]['methodName'])) {
499 unset($currentMethod);
502 // End line of a function.
503 else if (isset($this->endLines[$lineNumber]['functionName'])) {
504 unset($currentFunction);
509 foreach ($this->traits as $traitName => &$trait) {
510 foreach ($trait['methods'] as &$method) {
511 if ($method['executableLines'] > 0) {
512 $method['coverage'] = ($method['executedLines'] /
513 $method['executableLines']) * 100;
515 $method['coverage'] = 100;
518 $method['crap'] = PHP_CodeCoverage_Util::crap(
519 $method['ccn'], $method['coverage']
522 $trait['ccn'] += $method['ccn'];
525 if ($trait['executableLines'] > 0) {
526 $trait['coverage'] = ($trait['executedLines'] /
527 $trait['executableLines']) * 100;
529 $trait['coverage'] = 100;
532 if ($trait['coverage'] == 100) {
533 $this->numTestedClasses++;
536 $trait['crap'] = PHP_CodeCoverage_Util::crap(
537 $trait['ccn'], $trait['coverage']
541 foreach ($this->classes as $className => &$class) {
542 foreach ($class['methods'] as &$method) {
543 if ($method['executableLines'] > 0) {
544 $method['coverage'] = ($method['executedLines'] /
545 $method['executableLines']) * 100;
547 $method['coverage'] = 100;
550 $method['crap'] = PHP_CodeCoverage_Util::crap(
551 $method['ccn'], $method['coverage']
554 $class['ccn'] += $method['ccn'];
557 if ($class['executableLines'] > 0) {
558 $class['coverage'] = ($class['executedLines'] /
559 $class['executableLines']) * 100;
561 $class['coverage'] = 100;
564 if ($class['coverage'] == 100) {
565 $this->numTestedClasses++;
568 $class['crap'] = PHP_CodeCoverage_Util::crap(
569 $class['ccn'], $class['coverage']
575 * @param PHP_Token_Stream $tokens
577 protected function processClasses(PHP_Token_Stream $tokens)
579 $classes = $tokens->getClasses();
582 $link = $this->getId() . '.html#';
584 foreach ($classes as $className => $class) {
585 $this->classes[$className] = array(
586 'className' => $className,
587 'methods' => array(),
588 'startLine' => $class['startLine'],
589 'executableLines' => 0,
590 'executedLines' => 0,
594 'package' => $class['package'],
595 'link' => $link . $class['startLine']
598 $this->startLines[$class['startLine']] = &$this->classes[$className];
599 $this->endLines[$class['endLine']] = &$this->classes[$className];
601 foreach ($class['methods'] as $methodName => $method) {
602 $this->classes[$className]['methods'][$methodName] = array(
603 'methodName' => $methodName,
604 'signature' => $method['signature'],
605 'startLine' => $method['startLine'],
606 'endLine' => $method['endLine'],
607 'executableLines' => 0,
608 'executedLines' => 0,
609 'ccn' => $method['ccn'],
612 'link' => $link . $method['startLine']
615 $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName];
616 $this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName];
622 * @param PHP_Token_Stream $tokens
624 protected function processTraits(PHP_Token_Stream $tokens)
626 $traits = $tokens->getTraits();
629 $link = $this->getId() . '.html#';
631 foreach ($traits as $traitName => $trait) {
632 $this->traits[$traitName] = array(
633 'traitName' => $traitName,
634 'methods' => array(),
635 'startLine' => $trait['startLine'],
636 'executableLines' => 0,
637 'executedLines' => 0,
641 'package' => $trait['package'],
642 'link' => $link . $trait['startLine']
645 $this->startLines[$trait['startLine']] = &$this->traits[$traitName];
646 $this->endLines[$trait['endLine']] = &$this->traits[$traitName];
648 foreach ($trait['methods'] as $methodName => $method) {
649 $this->traits[$traitName]['methods'][$methodName] = array(
650 'methodName' => $methodName,
651 'signature' => $method['signature'],
652 'startLine' => $method['startLine'],
653 'executableLines' => 0,
654 'executedLines' => 0,
655 'ccn' => $method['ccn'],
658 'link' => $link . $method['startLine']
661 $this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName];
662 $this->endLines[$method['endLine']] = &$this->traits[$traitName]['methods'][$methodName];
668 * @param PHP_Token_Stream $tokens
670 protected function processFunctions(PHP_Token_Stream $tokens)
672 $functions = $tokens->getFunctions();
675 $link = $this->getId() . '.html#';
677 foreach ($functions as $functionName => $function) {
678 $this->functions[$functionName] = array(
679 'functionName' => $functionName,
680 'signature' => $function['signature'],
681 'startLine' => $function['startLine'],
682 'executableLines' => 0,
683 'executedLines' => 0,
684 'ccn' => $function['ccn'],
687 'link' => $link . $function['startLine']
690 $this->startLines[$function['startLine']] = &$this->functions[$functionName];
691 $this->endLines[$function['endLine']] = &$this->functions[$functionName];