. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of Sebastian Bergmann nor the names of his * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @category Testing * @package PHPUnit * @author Sebastian Bergmann * @copyright 2002-2009 Sebastian Bergmann * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @link http://www.phpunit.de/ * @since File available since Release 3.2.0 */ require_once 'PHPUnit/Util/Class.php'; require_once 'PHPUnit/Util/Metrics.php'; require_once 'PHPUnit/Util/Filter.php'; PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT'); /** * Class-Level Metrics. * * @category Testing * @package PHPUnit * @author Sebastian Bergmann * @copyright 2002-2009 Sebastian Bergmann * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version Release: 3.3.17 * @link http://www.phpunit.de/ * @since Class available since Release 3.2.0 */ class PHPUnit_Util_Metrics_Class extends PHPUnit_Util_Metrics { protected $aif = 0; protected $ahf = 0; protected $ca = 0; protected $ce = 0; protected $coverage = 0; protected $dit = 0; protected $i = 0; protected $impl = 0; protected $loc = 0; protected $locExecutable = 0; protected $locExecuted = 0; protected $mif = 0; protected $mhf = 0; protected $noc = 0; protected $pf = 0; protected $vars = 0; protected $varsNp = 0; protected $varsI = 0; protected $wmc = 0; protected $wmcNp = 0; protected $wmcI = 0; protected $project; protected $package = ''; protected $class; protected $methods = array(); protected $inheritedMethods = array(); protected $dependencies = array(); protected $publicMethods = 0; protected static $cache = array(); protected static $nocCache = array(); /** * Constructor. * * @param ReflectionClass $class * @param array $codeCoverage */ protected function __construct(ReflectionClass $class, &$codeCoverage = array()) { $this->class = $class; $className = $class->getName(); $packageInformation = PHPUnit_Util_Class::getPackageInformation($className); if (!empty($packageInformation['fullPackage'])) { $this->package = $packageInformation['fullPackage']; } $this->setCoverage($codeCoverage); $this->dit = count(PHPUnit_Util_Class::getHierarchy($class->getName())) - 1; $this->impl = count($class->getInterfaces()); foreach ($this->class->getMethods() as $method) { if ($method->getDeclaringClass()->getName() == $className) { $this->methods[$method->getName()] = PHPUnit_Util_Metrics_Function::factory($method, $codeCoverage); } else { $this->inheritedMethods[$method->getName()] = PHPUnit_Util_Metrics_Function::factory($method, $codeCoverage); } } $this->calculateAttributeMetrics(); $this->calculateMethodMetrics(); $this->calculateNumberOfChildren(); $this->calculatePolymorphismFactor(); $this->calculateDependencies(); } /** * Factory. * * @param ReflectionClass $class * @param array $codeCoverage * @return PHPUnit_Util_Metrics_Class */ public static function factory(ReflectionClass $class, &$codeCoverage = array()) { $className = $class->getName(); if (!isset(self::$cache[$className])) { self::$cache[$className] = new PHPUnit_Util_Metrics_Class($class, $codeCoverage); } else if (!empty($codeCoverage) && self::$cache[$className]->getCoverage() == 0) { self::$cache[$className]->setCoverage($codeCoverage); } return self::$cache[$className]; } /** * @param array $codeCoverage */ public function setCoverage(array &$codeCoverage) { if (!empty($codeCoverage)) { $this->calculateCodeCoverage($codeCoverage); foreach ($this->methods as $method) { $method->setCoverage($codeCoverage); } } } /** * @param PHPUnit_Util_Metrics_Project $project */ public function setProject(PHPUnit_Util_Metrics_Project $project) { $this->project = $project; $this->ca = 0; $this->ce = 0; $this->i = 0; } /** * Returns the class. * * @return ReflectionClass */ public function getClass() { return $this->class; } /** * Returns the package of this class. * * @return string */ public function getPackage() { return $this->package; } /** * Returns the methods of this class. * * @return array */ public function getMethods() { return $this->methods; } /** * Returns the names of the classes this class depends on. * * @return array */ public function getDependencies() { return $this->dependencies; } /** * Lines of Code (LOC). * * @return int */ public function getLoc() { return $this->loc; } /** * Executable Lines of Code (ELOC). * * @return int */ public function getLocExecutable() { return $this->locExecutable; } /** * Executed Lines of Code. * * @return int */ public function getLocExecuted() { return $this->locExecuted; } /** * Returns the Number of Public Methods of the class. * * @return integer */ public function getPublicMethods() { return $this->publicMethods; } /** * Returns the Attribute Inheritance Factor (AIF) for the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-mood.html */ public function getAIF() { return $this->aif; } /** * Returns the Attribute Hiding Factor (AHF) for the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-mood.html */ public function getAHF() { return $this->ahf; } /** * Returns the Afferent Couplings (Ca) for the class. * * The number of other classes that depend upon a class is an indicator of * the class' responsibility. * * @return integer */ public function getCa() { $this->calculateDependencyMetrics(); return $this->ca; } /** * Returns the Efferent Couplings (Ce) for the class. * * The number of other classes that the class depends upon is an indicator * of the class' independence. * * @return integer */ public function getCe() { $this->calculateDependencyMetrics(); return $this->ce; } /** * Returns the Class Size (CSZ) of the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getCSZ() { return count($this->methods) + $this->vars; } /** * Returns the Class Interface Size (CIS) of the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getCIS() { return $this->publicMethods + $this->varsNp; } /** * Returns the Code Coverage for the class. * * @return float */ public function getCoverage() { return $this->coverage; } /** * Returns the Depth of Inheritance Tree (DIT) for the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-ck.html */ public function getDIT() { return $this->dit; } /** * Returns the Instability (I) for the class. * * The ratio of efferent coupling (Ce) to total coupling (Ce + Ca) such that * I = Ce / (Ce + Ca). This metric is an indicator of the class' resilience * to change. * * The range for this metric is 0 to 1, with I=0 indicating a completely * stable class and I=1 indicating a completely instable class. * * @return float */ public function getI() { $this->calculateDependencyMetrics(); return $this->i; } /** * Returns the Number of Interfaces Implemented by the class (IMPL). * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getIMPL() { return $this->impl; } /** * Returns the Method Inheritance Factor (MIF) for the class. * * @return float * @see http://www.aivosto.com/project/help/pm-oo-mood.html */ public function getMIF() { return $this->mif; } /** * Returns the Method Hiding Factor (MHF) for the class. * * @return float * @see http://www.aivosto.com/project/help/pm-oo-mood.html */ public function getMHF() { return $this->mhf; } /** * Returns the Number of Children (NOC) for the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-ck.html */ public function getNOC() { return $this->noc; } /** * Returns the Polymorphism Factor (PF) for the class. * * @return float * @see http://www.aivosto.com/project/help/pm-oo-mood.html */ public function getPF() { return $this->pf; } /** * Returns the Number of Variables (VARS) defined by the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getVARS() { return $this->vars; } /** * Returns the Number of Non-Private Variables (VARSnp) defined by the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getVARSnp() { return $this->varsNp; } /** * Returns the Number of Variables (VARSi) defined and inherited by the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getVARSi() { return $this->varsI; } /** * Returns the Weighted Methods Per Class (WMC) for the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-ck.html */ public function getWMC() { return $this->wmc; } /** * Returns the Weighted Non-Private Methods Per Class (WMCnp) for the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getWMCnp() { return $this->wmcNp; } /** * Returns the Weighted Inherited Methods Per Class (WMCi) for the class. * * @return integer * @see http://www.aivosto.com/project/help/pm-oo-misc.html */ public function getWMCi() { return $this->wmcI; } /** * Calculates the Attribute Inheritance Factor (AIF) and * Attribute Hiding Factor (AHF) metrics for the class. * */ protected function calculateAttributeMetrics() { $attributes = 0; $hiddenAttributes = 0; $inheritedAttributes = 0; foreach ($this->class->getProperties() as $attribute) { if ($attribute->isPublic()) { $this->varsNp++; } else { $hiddenAttributes++; } if ($attribute->getDeclaringClass()->getName() == $this->class->getName()) { $this->vars++; } else { $inheritedAttributes++; } $this->varsI++; $attributes++; } if ($attributes > 0) { $this->aif = (100 * $inheritedAttributes) / $attributes; $this->ahf = (100 * $hiddenAttributes) / $attributes; } } /** * Calculates the Method Inheritance Factor (MIF) * Method Hiding Factor (MHF), Weighted Methods Per Class (WMC), * Weighted Non-Private Methods Per Class (WMCnp), and * Weighted Inherited Methods Per Class (WMCi) metrics for the class. * */ protected function calculateMethodMetrics() { $numMethods = 0; $hiddenMethods = 0; $inheritedMethods = 0; $methods = array_merge($this->methods, $this->inheritedMethods); foreach ($methods as $method) { $ccn = $method->getCCN(); if ($method->getMethod()->getDeclaringClass()->getName() == $this->class->getName()) { $this->wmc += $ccn; if ($method->getMethod()->isPublic()) { $this->publicMethods++; $this->wmcNp += $ccn; } } else { $inheritedMethods++; } if (!$method->getMethod()->isPublic()) { $hiddenMethods++; } $this->wmcI += $ccn; $numMethods++; } if ($numMethods > 0) { $this->mif = (100 * $inheritedMethods) / $numMethods; $this->mhf = (100 * $hiddenMethods) / $numMethods; } } /** * Calculates the Number of Children (NOC) metric for the class. * */ protected function calculateNumberOfChildren() { $className = $this->class->getName(); if (!isset(self::$nocCache[$className])) { self::$nocCache = array(); } if (empty(self::$nocCache)) { foreach (get_declared_classes() as $_className) { $class = new ReflectionClass($_className); $parent = $class->getParentClass(); if ($parent !== FALSE) { $parentName = $parent->getName(); if (isset(self::$nocCache[$parentName])) { self::$nocCache[$parentName]++; } else { self::$nocCache[$parentName] = 1; } } } } if (isset(self::$nocCache[$className])) { $this->noc = self::$nocCache[$className]; } } /** * Calculates the Polymorphism Factor (PF) metric for the class. * * @param ReflectionClass $class */ protected function calculatePolymorphismFactor() { $parentClass = $this->class->getParentClass(); if ($parentClass !== FALSE) { $overridableMethods = array(); foreach ($parentClass->getMethods() as $method) { if (!$method->isPrivate() && !$method->isFinal() && !$method->isAbstract()) { $overridableMethods[] = $method->getName(); } } if (!empty($overridableMethods)) { $overriddenMethods = 0; foreach ($this->class->getMethods() as $method) { if ($method->getDeclaringClass()->getName() == $this->class->getName()) { $methodName = $method->getName(); if (in_array($methodName, $overridableMethods)) { $overriddenMethods++; } } } $this->pf = (100 * $overriddenMethods) / count($overridableMethods); } } } /** * Calculates the Code Coverage for the class. * * @param array $codeCoverage */ protected function calculateCodeCoverage(&$codeCoverage) { $statistics = PHPUnit_Util_CodeCoverage::getStatistics( $codeCoverage, $this->class->getFileName(), $this->class->getStartLine(), $this->class->getEndLine() ); $this->coverage = $statistics['coverage']; $this->loc = $statistics['loc']; $this->locExecutable = $statistics['locExecutable']; $this->locExecuted = $statistics['locExecuted']; } /** * Calculates the dependencies for this class. * */ protected function calculateDependencies() { $parent = $this->class->getParentClass(); if ($parent && $parent->isUserDefined() && !in_array($parent->getName(), $this->dependencies)) { $this->dependencies[] = $parent->getName(); } $interfaces = $this->class->getInterfaces(); foreach ($interfaces as $interface) { if ($interface->isUserDefined() && !in_array($interface->getName(), $this->dependencies)) { $this->dependencies[] = $interface->getName(); } } $methods = array_merge($this->methods, $this->inheritedMethods); foreach ($methods as $method) { foreach ($method->getDependencies() as $dependency) { if (!in_array($dependency, $this->dependencies)) { $this->dependencies[] = $dependency; } } } } /** * Calculates the dependency-based metrics for this class. * */ protected function calculateDependencyMetrics() { if ($this->ca == 0 && $this->ce == 0 && $this->i == 0) { $className = $this->class->getName(); $dependencies = $this->project->getDependencies(); foreach ($dependencies[$className] as $dependency) { if ($dependency > 0) { $this->ce++; } } unset($dependencies[$className]); foreach ($dependencies as $_className => $_dependencies) { if ($_dependencies[$className] > 0) { $this->ca++; } } $sum = $this->ce + $this->ca; if ($sum > 0) { $this->i = $this->ce / $sum; } } } } ?>