5 * Copyright (c) 2002-2008, 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.
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2002-2008 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42 * @version SVN: $Id: Class.php 1985 2007-12-26 18:11:55Z sb $
43 * @link http://www.phpunit.de/
44 * @since File available since Release 3.2.0
47 require_once 'PHPUnit/Util/Class.php';
48 require_once 'PHPUnit/Util/Metrics.php';
49 require_once 'PHPUnit/Util/Filter.php';
51 PHPUnit_Util_Filter
::addFileToFilter(__FILE__
, 'PHPUNIT');
54 * Class-Level Metrics.
58 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
59 * @copyright 2002-2008 Sebastian Bergmann <sb@sebastian-bergmann.de>
60 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
61 * @version Release: 3.2.9
62 * @link http://www.phpunit.de/
63 * @since Class available since Release 3.2.0
65 class PHPUnit_Util_Metrics_Class
extends PHPUnit_Util_Metrics
{
70 protected $coverage = 0;
75 protected $locExecutable = 0;
76 protected $locExecuted = 0;
82 protected $varsNp = 0;
89 protected $package = '';
91 protected $methods = array();
92 protected $inheritedMethods = array();
93 protected $dependencies = array();
94 protected $publicMethods = 0;
96 protected static $cache = array();
97 protected static $nocCache = array();
102 * @param ReflectionClass $class
103 * @param array $codeCoverage
106 protected function __construct(ReflectionClass
$class, &$codeCoverage = array()) {
107 $this->class = $class;
109 $className = $class->getName();
111 $packageInformation = PHPUnit_Util_Class
::getPackageInformation($className);
113 if (! empty($packageInformation['fullPackage'])) {
114 $this->package
= $packageInformation['fullPackage'];
117 $this->setCoverage($codeCoverage);
119 $this->dit
= count(PHPUnit_Util_Class
::getHierarchy($class->getName())) - 1;
120 $this->impl
= count($class->getInterfaces());
122 foreach ($this->class->getMethods() as $method) {
123 if ($method->getDeclaringClass()->getName() == $className) {
124 $this->methods
[$method->getName()] = PHPUnit_Util_Metrics_Function
::factory($method, $codeCoverage);
126 $this->inheritedMethods
[$method->getName()] = PHPUnit_Util_Metrics_Function
::factory($method, $codeCoverage);
130 $this->calculateAttributeMetrics();
131 $this->calculateMethodMetrics();
132 $this->calculateNumberOfChildren();
133 $this->calculatePolymorphismFactor();
134 $this->calculateDependencies();
140 * @param ReflectionClass $class
141 * @param array $codeCoverage
142 * @return PHPUnit_Util_Metrics_Class
146 public static function factory(ReflectionClass
$class, &$codeCoverage = array()) {
147 $className = $class->getName();
149 if (! isset(self
::$cache[$className])) {
150 self
::$cache[$className] = new PHPUnit_Util_Metrics_Class($class, $codeCoverage);
153 else if (! empty($codeCoverage) && self
::$cache[$className]->getCoverage() == 0) {
154 self
::$cache[$className]->setCoverage($codeCoverage);
157 return self
::$cache[$className];
161 * @param array $codeCoverage
164 public function setCoverage(array &$codeCoverage) {
165 if (! empty($codeCoverage)) {
166 $this->calculateCodeCoverage($codeCoverage);
168 foreach ($this->methods
as $method) {
169 $method->setCoverage($codeCoverage);
175 * @param PHPUnit_Util_Metrics_Project $project
178 public function setProject(PHPUnit_Util_Metrics_Project
$project) {
179 $this->project
= $project;
189 * @return ReflectionClass
192 public function getClass() {
197 * Returns the package of this class.
202 public function getPackage() {
203 return $this->package
;
207 * Returns the methods of this class.
212 public function getMethods() {
213 return $this->methods
;
217 * Returns the names of the classes this class depends on.
222 public function getDependencies() {
223 return $this->dependencies
;
227 * Lines of Code (LOC).
232 public function getLoc() {
237 * Executable Lines of Code (ELOC).
242 public function getLocExecutable() {
243 return $this->locExecutable
;
247 * Executed Lines of Code.
252 public function getLocExecuted() {
253 return $this->locExecuted
;
257 * Returns the Number of Public Methods of the class.
262 public function getPublicMethods() {
263 return $this->publicMethods
;
267 * Returns the Attribute Inheritance Factor (AIF) for the class.
271 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
273 public function getAIF() {
278 * Returns the Attribute Hiding Factor (AHF) for the class.
282 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
284 public function getAHF() {
289 * Returns the Afferent Couplings (Ca) for the class.
291 * The number of other classes that depend upon a class is an indicator of
292 * the class' responsibility.
297 public function getCa() {
298 $this->calculateDependencyMetrics();
304 * Returns the Efferent Couplings (Ce) for the class.
306 * The number of other classes that the class depends upon is an indicator
307 * of the class' independence.
312 public function getCe() {
313 $this->calculateDependencyMetrics();
319 * Returns the Class Size (CSZ) of the class.
323 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
325 public function getCSZ() {
326 return count($this->methods
) +
$this->vars
;
330 * Returns the Class Interface Size (CIS) of the class.
334 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
336 public function getCIS() {
337 return $this->publicMethods +
$this->varsNp
;
341 * Returns the Code Coverage for the class.
346 public function getCoverage() {
347 return $this->coverage
;
351 * Returns the Depth of Inheritance Tree (DIT) for the class.
355 * @see http://www.aivosto.com/project/help/pm-oo-ck.html
357 public function getDIT() {
362 * Returns the Instability (I) for the class.
364 * The ratio of efferent coupling (Ce) to total coupling (Ce + Ca) such that
365 * I = Ce / (Ce + Ca). This metric is an indicator of the class' resilience
368 * The range for this metric is 0 to 1, with I=0 indicating a completely
369 * stable class and I=1 indicating a completely instable class.
374 public function getI() {
375 $this->calculateDependencyMetrics();
381 * Returns the Number of Interfaces Implemented by the class (IMPL).
385 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
387 public function getIMPL() {
392 * Returns the Method Inheritance Factor (MIF) for the class.
396 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
398 public function getMIF() {
403 * Returns the Method Hiding Factor (MHF) for the class.
407 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
409 public function getMHF() {
414 * Returns the Number of Children (NOC) for the class.
418 * @see http://www.aivosto.com/project/help/pm-oo-ck.html
420 public function getNOC() {
425 * Returns the Polymorphism Factor (PF) for the class.
429 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
431 public function getPF() {
436 * Returns the Number of Variables (VARS) defined by the class.
440 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
442 public function getVARS() {
447 * Returns the Number of Non-Private Variables (VARSnp) defined by the class.
451 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
453 public function getVARSnp() {
454 return $this->varsNp
;
458 * Returns the Number of Variables (VARSi) defined and inherited by the class.
462 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
464 public function getVARSi() {
469 * Returns the Weighted Methods Per Class (WMC) for the class.
473 * @see http://www.aivosto.com/project/help/pm-oo-ck.html
475 public function getWMC() {
480 * Returns the Weighted Non-Private Methods Per Class (WMCnp) for the class.
484 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
486 public function getWMCnp() {
491 * Returns the Weighted Inherited Methods Per Class (WMCi) for the class.
495 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
497 public function getWMCi() {
502 * Calculates the Attribute Inheritance Factor (AIF) and
503 * Attribute Hiding Factor (AHF) metrics for the class.
507 protected function calculateAttributeMetrics() {
509 $hiddenAttributes = 0;
510 $inheritedAttributes = 0;
512 foreach ($this->class->getProperties() as $attribute) {
513 if ($attribute->isPublic()) {
516 $hiddenAttributes ++
;
519 if ($attribute->getDeclaringClass()->getName() == $this->class->getName()) {
522 $inheritedAttributes ++
;
529 if ($attributes > 0) {
530 $this->aif
= (100 * $inheritedAttributes) / $attributes;
531 $this->ahf
= (100 * $hiddenAttributes) / $attributes;
536 * Calculates the Method Inheritance Factor (MIF)
537 * Method Hiding Factor (MHF), Weighted Methods Per Class (WMC),
538 * Weighted Non-Private Methods Per Class (WMCnp), and
539 * Weighted Inherited Methods Per Class (WMCi) metrics for the class.
543 protected function calculateMethodMetrics() {
546 $inheritedMethods = 0;
548 $methods = array_merge($this->methods
, $this->inheritedMethods
);
550 foreach ($methods as $method) {
551 $ccn = $method->getCCN();
553 if ($method->getMethod()->getDeclaringClass()->getName() == $this->class->getName()) {
556 if ($method->getMethod()->isPublic()) {
557 $this->publicMethods ++
;
558 $this->wmcNp +
= $ccn;
561 $inheritedMethods ++
;
564 if (! $method->getMethod()->isPublic()) {
572 if ($numMethods > 0) {
573 $this->mif
= (100 * $inheritedMethods) / $numMethods;
574 $this->mhf
= (100 * $hiddenMethods) / $numMethods;
579 * Calculates the Number of Children (NOC) metric for the class.
583 protected function calculateNumberOfChildren() {
584 $className = $this->class->getName();
586 if (! isset(self
::$nocCache[$className])) {
587 self
::$nocCache = array();
590 if (empty(self
::$nocCache)) {
591 foreach (get_declared_classes() as $_className) {
592 $class = new ReflectionClass($_className);
593 $parent = $class->getParentClass();
595 if ($parent !== FALSE) {
596 $parentName = $parent->getName();
598 if (isset(self
::$nocCache[$parentName])) {
599 self
::$nocCache[$parentName] ++
;
601 self
::$nocCache[$parentName] = 1;
607 if (isset(self
::$nocCache[$className])) {
608 $this->noc
= self
::$nocCache[$className];
613 * Calculates the Polymorphism Factor (PF) metric for the class.
615 * @param ReflectionClass $class
618 protected function calculatePolymorphismFactor() {
619 $parentClass = $this->class->getParentClass();
621 if ($parentClass !== FALSE) {
622 $overridableMethods = array();
624 foreach ($parentClass->getMethods() as $method) {
625 if (! $method->isPrivate() && ! $method->isFinal() && ! $method->isAbstract()) {
626 $overridableMethods[] = $method->getName();
630 if (! empty($overridableMethods)) {
631 $overriddenMethods = 0;
633 foreach ($this->class->getMethods() as $method) {
634 if ($method->getDeclaringClass()->getName() == $this->class->getName()) {
635 $methodName = $method->getName();
637 if (in_array($methodName, $overridableMethods)) {
638 $overriddenMethods ++
;
643 $this->pf
= (100 * $overriddenMethods) / count($overridableMethods);
649 * Calculates the Code Coverage for the class.
651 * @param array $codeCoverage
654 protected function calculateCodeCoverage(&$codeCoverage) {
655 $statistics = PHPUnit_Util_CodeCoverage
::getStatistics($codeCoverage, $this->class->getFileName(), $this->class->getStartLine(), $this->class->getEndLine());
657 $this->coverage
= $statistics['coverage'];
658 $this->loc
= $statistics['loc'];
659 $this->locExecutable
= $statistics['locExecutable'];
660 $this->locExecuted
= $statistics['locExecuted'];
664 * Calculates the dependencies for this class.
668 protected function calculateDependencies() {
669 $parent = $this->class->getParentClass();
671 if ($parent && $parent->isUserDefined() && ! in_array($parent->getName(), $this->dependencies
)) {
672 $this->dependencies
[] = $parent->getName();
675 $interfaces = $this->class->getInterfaces();
677 foreach ($interfaces as $interface) {
678 if ($interface->isUserDefined() && ! in_array($interface->getName(), $this->dependencies
)) {
679 $this->dependencies
[] = $interface->getName();
683 $methods = array_merge($this->methods
, $this->inheritedMethods
);
685 foreach ($methods as $method) {
686 foreach ($method->getDependencies() as $dependency) {
687 if (! in_array($dependency, $this->dependencies
)) {
688 $this->dependencies
[] = $dependency;
695 * Calculates the dependency-based metrics for this class.
699 protected function calculateDependencyMetrics() {
700 if ($this->ca
== 0 && $this->ce
== 0 && $this->i
== 0) {
701 $className = $this->class->getName();
702 $dependencies = $this->project
->getDependencies();
704 foreach ($dependencies[$className] as $dependency) {
705 if ($dependency > 0) {
710 unset($dependencies[$className]);
712 foreach ($dependencies as $_className => $_dependencies) {
713 if ($_dependencies[$className] > 0) {
718 $sum = $this->ce +
$this->ca
;
721 $this->i
= $this->ce
/ $sum;