blob: 0166bbb2e86562448a8f2cdc378137ca55a4d293 [file]
<?php
/**
* PHPUnit
*
* Copyright (c) 2002-2008, Sebastian Bergmann <sb@sebastian-bergmann.de>.
* 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 <sb@sebastian-bergmann.de>
* @copyright 2002-2008 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version SVN: $Id: Class.php 1985 2007-12-26 18:11:55Z sb $
* @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 <sb@sebastian-bergmann.de>
* @copyright 2002-2008 Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 3.2.9
* @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
* @access protected
*/
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
* @access public
* @static
*/
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
* @access public
*/
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
* @access public
*/
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
* @access public
*/
public function getClass() {
return $this->class;
}
/**
* Returns the package of this class.
*
* @return string
* @access public
*/
public function getPackage() {
return $this->package;
}
/**
* Returns the methods of this class.
*
* @return array
* @access public
*/
public function getMethods() {
return $this->methods;
}
/**
* Returns the names of the classes this class depends on.
*
* @return array
* @access public
*/
public function getDependencies() {
return $this->dependencies;
}
/**
* Lines of Code (LOC).
*
* @return int
* @access public
*/
public function getLoc() {
return $this->loc;
}
/**
* Executable Lines of Code (ELOC).
*
* @return int
* @access public
*/
public function getLocExecutable() {
return $this->locExecutable;
}
/**
* Executed Lines of Code.
*
* @return int
* @access public
*/
public function getLocExecuted() {
return $this->locExecuted;
}
/**
* Returns the Number of Public Methods of the class.
*
* @return integer
* @access public
*/
public function getPublicMethods() {
return $this->publicMethods;
}
/**
* Returns the Attribute Inheritance Factor (AIF) for the class.
*
* @return integer
* @access public
* @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
* @access public
* @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
* @access public
*/
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
* @access public
*/
public function getCe() {
$this->calculateDependencyMetrics();
return $this->ce;
}
/**
* Returns the Class Size (CSZ) of the class.
*
* @return integer
* @access public
* @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
* @access public
* @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
* @access public
*/
public function getCoverage() {
return $this->coverage;
}
/**
* Returns the Depth of Inheritance Tree (DIT) for the class.
*
* @return integer
* @access public
* @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
* @access public
*/
public function getI() {
$this->calculateDependencyMetrics();
return $this->i;
}
/**
* Returns the Number of Interfaces Implemented by the class (IMPL).
*
* @return integer
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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
* @access public
* @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.
*
* @access protected
*/
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.
*
* @access protected
*/
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.
*
* @access protected
*/
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
* @access protected
*/
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
* @access protected
*/
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.
*
* @access protected
*/
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.
*
* @access protected
*/
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;
}
}
}
}
?>