5 * Copyright (c) 2002-2009, 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-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
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-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
60 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
61 * @version Release: 3.3.17
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
71 protected $coverage = 0;
76 protected $locExecutable = 0;
77 protected $locExecuted = 0;
83 protected $varsNp = 0;
90 protected $package = '';
92 protected $methods = array();
93 protected $inheritedMethods = array();
94 protected $dependencies = array();
95 protected $publicMethods = 0;
97 protected static $cache = array();
98 protected static $nocCache = array();
103 * @param ReflectionClass $class
104 * @param array $codeCoverage
106 protected function __construct(ReflectionClass $class, &$codeCoverage = array())
108 $this->class = $class;
110 $className = $class->getName();
112 $packageInformation = PHPUnit_Util_Class::getPackageInformation($className);
114 if (!empty($packageInformation['fullPackage'])) {
115 $this->package = $packageInformation['fullPackage'];
118 $this->setCoverage($codeCoverage);
120 $this->dit = count(PHPUnit_Util_Class::getHierarchy($class->getName())) - 1;
121 $this->impl = count($class->getInterfaces());
123 foreach ($this->class->getMethods() as $method) {
124 if ($method->getDeclaringClass()->getName() == $className) {
125 $this->methods[$method->getName()] = PHPUnit_Util_Metrics_Function::factory($method, $codeCoverage);
127 $this->inheritedMethods[$method->getName()] = PHPUnit_Util_Metrics_Function::factory($method, $codeCoverage);
131 $this->calculateAttributeMetrics();
132 $this->calculateMethodMetrics();
133 $this->calculateNumberOfChildren();
134 $this->calculatePolymorphismFactor();
135 $this->calculateDependencies();
141 * @param ReflectionClass $class
142 * @param array $codeCoverage
143 * @return PHPUnit_Util_Metrics_Class
145 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
163 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
177 public function setProject(PHPUnit_Util_Metrics_Project $project)
179 $this->project = $project;
189 * @return ReflectionClass
191 public function getClass()
197 * Returns the package of this class.
201 public function getPackage()
203 return $this->package;
207 * Returns the methods of this class.
211 public function getMethods()
213 return $this->methods;
217 * Returns the names of the classes this class depends on.
221 public function getDependencies()
223 return $this->dependencies;
227 * Lines of Code (LOC).
231 public function getLoc()
237 * Executable Lines of Code (ELOC).
241 public function getLocExecutable()
243 return $this->locExecutable;
247 * Executed Lines of Code.
251 public function getLocExecuted()
253 return $this->locExecuted;
257 * Returns the Number of Public Methods of the class.
261 public function getPublicMethods()
263 return $this->publicMethods;
267 * Returns the Attribute Inheritance Factor (AIF) for the class.
270 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
272 public function getAIF()
278 * Returns the Attribute Hiding Factor (AHF) for the class.
281 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
283 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.
296 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.
311 public function getCe()
313 $this->calculateDependencyMetrics();
319 * Returns the Class Size (CSZ) of the class.
322 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
324 public function getCSZ()
326 return count($this->methods) + $this->vars;
330 * Returns the Class Interface Size (CIS) of the class.
333 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
335 public function getCIS()
337 return $this->publicMethods + $this->varsNp;
341 * Returns the Code Coverage for the class.
345 public function getCoverage()
347 return $this->coverage;
351 * Returns the Depth of Inheritance Tree (DIT) for the class.
354 * @see http://www.aivosto.com/project/help/pm-oo-ck.html
356 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.
373 public function getI()
375 $this->calculateDependencyMetrics();
381 * Returns the Number of Interfaces Implemented by the class (IMPL).
384 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
386 public function getIMPL()
392 * Returns the Method Inheritance Factor (MIF) for the class.
395 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
397 public function getMIF()
403 * Returns the Method Hiding Factor (MHF) for the class.
406 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
408 public function getMHF()
414 * Returns the Number of Children (NOC) for the class.
417 * @see http://www.aivosto.com/project/help/pm-oo-ck.html
419 public function getNOC()
425 * Returns the Polymorphism Factor (PF) for the class.
428 * @see http://www.aivosto.com/project/help/pm-oo-mood.html
430 public function getPF()
436 * Returns the Number of Variables (VARS) defined by the class.
439 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
441 public function getVARS()
447 * Returns the Number of Non-Private Variables (VARSnp) defined by the class.
450 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
452 public function getVARSnp()
454 return $this->varsNp;
458 * Returns the Number of Variables (VARSi) defined and inherited by the class.
461 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
463 public function getVARSi()
469 * Returns the Weighted Methods Per Class (WMC) for the class.
472 * @see http://www.aivosto.com/project/help/pm-oo-ck.html
474 public function getWMC()
480 * Returns the Weighted Non-Private Methods Per Class (WMCnp) for the class.
483 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
485 public function getWMCnp()
491 * Returns the Weighted Inherited Methods Per Class (WMCi) for the class.
494 * @see http://www.aivosto.com/project/help/pm-oo-misc.html
496 public function getWMCi()
502 * Calculates the Attribute Inheritance Factor (AIF) and
503 * Attribute Hiding Factor (AHF) metrics for the class.
506 protected function calculateAttributeMetrics()
509 $hiddenAttributes = 0;
510 $inheritedAttributes = 0;
512 foreach ($this->class->getProperties() as $attribute) {
513 if ($attribute->isPublic()) {
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.
542 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;
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.
582 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
617 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
653 protected function calculateCodeCoverage(&$codeCoverage)
655 $statistics = PHPUnit_Util_CodeCoverage::getStatistics(
657 $this->class->getFileName(),
658 $this->class->getStartLine(),
659 $this->class->getEndLine()
662 $this->coverage = $statistics['coverage'];
663 $this->loc = $statistics['loc'];
664 $this->locExecutable = $statistics['locExecutable'];
665 $this->locExecuted = $statistics['locExecuted'];
669 * Calculates the dependencies for this class.
672 protected function calculateDependencies()
674 $parent = $this->class->getParentClass();
676 if ($parent && $parent->isUserDefined() && !in_array($parent->getName(), $this->dependencies)) {
677 $this->dependencies[] = $parent->getName();
680 $interfaces = $this->class->getInterfaces();
682 foreach ($interfaces as $interface) {
683 if ($interface->isUserDefined() && !in_array($interface->getName(), $this->dependencies)) {
684 $this->dependencies[] = $interface->getName();
688 $methods = array_merge($this->methods, $this->inheritedMethods);
690 foreach ($methods as $method) {
691 foreach ($method->getDependencies() as $dependency) {
692 if (!in_array($dependency, $this->dependencies)) {
693 $this->dependencies[] = $dependency;
700 * Calculates the dependency-based metrics for this class.
703 protected function calculateDependencyMetrics()
705 if ($this->ca == 0 && $this->ce == 0 && $this->i == 0) {
706 $className = $this->class->getName();
707 $dependencies = $this->project->getDependencies();
709 foreach ($dependencies[$className] as $dependency) {
710 if ($dependency > 0) {
715 unset($dependencies[$className]);
717 foreach ($dependencies as $_className => $_dependencies) {
718 if ($_dependencies[$className] > 0) {
723 $sum = $this->ce + $this->ca;
726 $this->i = $this->ce / $sum;