PageRenderTime 64ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 1ms

/libs/php/pear/PHP/Depend/Metrics/Coupling/Analyzer.php

https://github.com/KaRLsM/SIFO
PHP | 481 lines | 195 code | 48 blank | 238 comment | 10 complexity | 544b2a06326f1e735d0260c58240a4a0 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of PHP_Depend.
  4. *
  5. * PHP Version 5
  6. *
  7. * Copyright (c) 2008-2011, Manuel Pichler <mapi@pdepend.org>.
  8. * All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * * Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in
  19. * the documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * * Neither the name of Manuel Pichler nor the names of his
  23. * contributors may be used to endorse or promote products derived
  24. * from this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  27. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  28. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  29. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  30. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  31. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  32. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  33. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  34. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  35. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  36. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  37. * POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. * @category QualityAssurance
  40. * @package PHP_Depend
  41. * @subpackage Metrics
  42. * @author Manuel Pichler <mapi@pdepend.org>
  43. * @copyright 2008-2011 Manuel Pichler. All rights reserved.
  44. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  45. * @version SVN: $Id$
  46. * @link http://pdepend.org/
  47. */
  48. /**
  49. * This analyzer collects coupling values for the hole project. It calculates
  50. * all function and method <b>calls</b> and the <b>fanout</b>, that means the
  51. * number of referenced types.
  52. *
  53. * The FANOUT calculation is based on the definition used by the apache maven
  54. * project.
  55. *
  56. * <ul>
  57. * <li>field declarations (Uses doc comment annotations)</li>
  58. * <li>formal parameters and return types (The return type uses doc comment
  59. * annotations)</li>
  60. * <li>throws declarations (Uses doc comment annotations)</li>
  61. * <li>local variables</li>
  62. * </ul>
  63. *
  64. * http://www.jajakarta.org/turbine/en/turbine/maven/reference/metrics.html
  65. *
  66. * The implemented algorithm counts each type only once for a method and function.
  67. * Any type that is either a supertype or a subtype of the class is not counted.
  68. *
  69. * @category QualityAssurance
  70. * @package PHP_Depend
  71. * @subpackage Metrics
  72. * @author Manuel Pichler <mapi@pdepend.org>
  73. * @copyright 2008-2011 Manuel Pichler. All rights reserved.
  74. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  75. * @version Release: 0.10.6
  76. * @link http://pdepend.org/
  77. */
  78. class PHP_Depend_Metrics_Coupling_Analyzer
  79. extends PHP_Depend_Metrics_AbstractAnalyzer
  80. implements PHP_Depend_Metrics_NodeAwareI,
  81. PHP_Depend_Metrics_ProjectAwareI
  82. {
  83. /**
  84. * Type of this analyzer class.
  85. */
  86. const CLAZZ = __CLASS__;
  87. /**
  88. * Metrics provided by the analyzer implementation.
  89. */
  90. const M_CALLS = 'calls',
  91. M_FANOUT = 'fanout',
  92. M_CA = 'ca',
  93. M_CBO = 'cbo',
  94. M_CE = 'ce';
  95. /**
  96. * Has this analyzer already processed the source under test?
  97. *
  98. * @var boolean
  99. * @since 0.10.2
  100. */
  101. private $_uninitialized = true;
  102. /**
  103. * The number of method or function calls.
  104. *
  105. * @var integer
  106. */
  107. private $_calls = 0;
  108. /**
  109. * Number of fanouts.
  110. *
  111. * @var integer
  112. */
  113. private $_fanout = 0;
  114. /**
  115. * Temporary map that is used to hold the uuid combinations of dependee and
  116. * depender.
  117. *
  118. * @var array(string=>array)
  119. * @since 0.10.2
  120. */
  121. private $_temporaryCouplingMap = array();
  122. /**
  123. * This array holds a mapping between node identifiers and an array with
  124. * the node's metrics.
  125. *
  126. * @var array(string=>array)
  127. * @since 0.10.2
  128. */
  129. private $_nodeMetrics = array();
  130. /**
  131. * Provides the project summary as an <b>array</b>.
  132. *
  133. * <code>
  134. * array(
  135. * 'calls' => 23,
  136. * 'fanout' => 42
  137. * )
  138. * </code>
  139. *
  140. * @return array(string=>mixed)
  141. */
  142. public function getProjectMetrics()
  143. {
  144. return array(
  145. self::M_CALLS => $this->_calls,
  146. self::M_FANOUT => $this->_fanout
  147. );
  148. }
  149. /**
  150. * This method will return an <b>array</b> with all generated metric values
  151. * for the given node instance. If there are no metrics for the given node
  152. * this method will return an empty <b>array</b>.
  153. *
  154. * <code>
  155. * array(
  156. * 'loc' => 42,
  157. * 'ncloc' => 17,
  158. * 'cc' => 12
  159. * )
  160. * </code>
  161. *
  162. * @param PHP_Depend_Code_NodeI $node The context node instance.
  163. *
  164. * @return array(string=>mixed)
  165. */
  166. public function getNodeMetrics(PHP_Depend_Code_NodeI $node)
  167. {
  168. if (isset($this->_nodeMetrics[$node->getUUID()])) {
  169. return $this->_nodeMetrics[$node->getUUID()];
  170. }
  171. return array();
  172. }
  173. /**
  174. * Processes all {@link PHP_Depend_Code_Package} code nodes.
  175. *
  176. * @param PHP_Depend_Code_NodeIterator $packages All code packages.
  177. *
  178. * @return void
  179. */
  180. public function analyze(PHP_Depend_Code_NodeIterator $packages)
  181. {
  182. if ($this->_uninitialized) {
  183. $this->_analyze($packages);
  184. $this->_uninitialized = false;
  185. }
  186. }
  187. /**
  188. * This method traverses all packages in the given iterator and calculates
  189. * the coupling metrics for them.
  190. *
  191. * @param PHP_Depend_Code_NodeIterator $packages All parsed code packages.
  192. *
  193. * @return void
  194. * @since 0.10.2
  195. */
  196. private function _analyze(PHP_Depend_Code_NodeIterator $packages)
  197. {
  198. $this->fireStartAnalyzer();
  199. $this->_reset();
  200. foreach ($packages as $package) {
  201. $package->accept($this);
  202. }
  203. $this->_postProcessTemporaryCouplingMap();
  204. $this->fireEndAnalyzer();
  205. }
  206. /**
  207. * This method resets all internal state variables before the analyzer can
  208. * start the object tree traversal.
  209. *
  210. * @return void
  211. * @since 0.10.2
  212. */
  213. private function _reset()
  214. {
  215. $this->_calls = 0;
  216. $this->_fanout = 0;
  217. $this->_nodeMetrics = array();
  218. $this->_temporaryCouplingMap = array();
  219. }
  220. /**
  221. * This method takes the temporary coupling map with node UUIDs and calculates
  222. * the concrete node metrics.
  223. *
  224. * @return void
  225. * @since 0.10.2
  226. */
  227. private function _postProcessTemporaryCouplingMap()
  228. {
  229. foreach ($this->_temporaryCouplingMap as $uuid => $metrics) {
  230. $afferentCoupling = count($metrics['ca']);
  231. $efferentCoupling = count($metrics['ce']);
  232. $this->_nodeMetrics[$uuid] = array(
  233. self::M_CA => $afferentCoupling,
  234. self::M_CBO => $efferentCoupling,
  235. self::M_CE => $efferentCoupling
  236. );
  237. $this->_fanout += $efferentCoupling;
  238. }
  239. $this->_temporaryCouplingMap = array();
  240. }
  241. /**
  242. * Visits a function node.
  243. *
  244. * @param PHP_Depend_Code_Function $function The current function node.
  245. *
  246. * @return void
  247. */
  248. public function visitFunction(PHP_Depend_Code_Function $function)
  249. {
  250. $this->fireStartFunction($function);
  251. $fanouts = array();
  252. if (($type = $function->getReturnClass()) !== null) {
  253. $fanouts[] = $type;
  254. ++$this->_fanout;
  255. }
  256. foreach ($function->getExceptionClasses() as $type) {
  257. if (in_array($type, $fanouts, true) === false) {
  258. $fanouts[] = $type;
  259. ++$this->_fanout;
  260. }
  261. }
  262. foreach ($function->getDependencies() as $type) {
  263. if (in_array($type, $fanouts, true) === false) {
  264. $fanouts[] = $type;
  265. ++$this->_fanout;
  266. }
  267. }
  268. foreach ($fanouts as $fanout) {
  269. $this->_initClassOrInterfaceDependencyMap($fanout);
  270. $this->_temporaryCouplingMap[
  271. $fanout->getUUID()
  272. ]['ca'][
  273. $function->getUUID()
  274. ] = true;
  275. }
  276. $this->_countCalls($function);
  277. $this->fireEndFunction($function);
  278. }
  279. /**
  280. * Visit method for classes that will be called by PHP_Depend during the
  281. * analysis phase with the current context class.
  282. *
  283. * @param PHP_Depend_Code_Class $class The currently analyzed class.
  284. *
  285. * @return void
  286. * @since 0.10.2
  287. */
  288. public function visitClass(PHP_Depend_Code_Class $class)
  289. {
  290. $this->_initClassOrInterfaceDependencyMap($class);
  291. return parent::visitClass($class);
  292. }
  293. /**
  294. * Visit method for interfaces that will be called by PHP_Depend during the
  295. * analysis phase with the current context interface.
  296. *
  297. * @param PHP_Depend_Code_Interface $interface The currently analyzed interface.
  298. *
  299. * @return void
  300. * @since 0.10.2
  301. */
  302. public function visitInterface(PHP_Depend_Code_Interface $interface)
  303. {
  304. $this->_initClassOrInterfaceDependencyMap($interface);
  305. return parent::visitInterface($interface);
  306. }
  307. /**
  308. * Visits a method node.
  309. *
  310. * @param PHP_Depend_Code_Class $method The method class node.
  311. *
  312. * @return void
  313. */
  314. public function visitMethod(PHP_Depend_Code_Method $method)
  315. {
  316. $this->fireStartMethod($method);
  317. $declaringClass = $method->getParent();
  318. $this->_calculateClassOrInterfaceCoupling(
  319. $declaringClass,
  320. $method->getReturnClass()
  321. );
  322. foreach ($method->getExceptionClasses() as $type) {
  323. $this->_calculateClassOrInterfaceCoupling($declaringClass, $type);
  324. }
  325. foreach ($method->getDependencies() as $type) {
  326. $this->_calculateClassOrInterfaceCoupling($declaringClass, $type);
  327. }
  328. $this->_countCalls($method);
  329. $this->fireEndMethod($method);
  330. }
  331. /**
  332. * Visits a property node.
  333. *
  334. * @param PHP_Depend_Code_Property $property The property class node.
  335. *
  336. * @return void
  337. */
  338. public function visitProperty(PHP_Depend_Code_Property $property)
  339. {
  340. $this->fireStartProperty($property);
  341. $this->_calculateClassOrInterfaceCoupling(
  342. $property->getDeclaringClass(),
  343. $property->getClass()
  344. );
  345. $this->fireEndProperty($property);
  346. }
  347. /**
  348. * Calculates the coupling between the given types.
  349. *
  350. * @param PHP_Depend_Code_AbstractClassOrInterface $declaringClass The declaring
  351. * or context class.
  352. * @param PHP_Depend_Code_AbstractClassOrInterface $coupledClass The class that
  353. * is used by the declaring class or <b>null</b> when no class is defined.
  354. *
  355. * @return void
  356. * @since 0.10.2
  357. */
  358. private function _calculateClassOrInterfaceCoupling(
  359. PHP_Depend_Code_AbstractClassOrInterface $declaringClass,
  360. PHP_Depend_Code_AbstractClassOrInterface $coupledClass = null
  361. ) {
  362. $this->_initClassOrInterfaceDependencyMap($declaringClass);
  363. if (null === $coupledClass) {
  364. return;
  365. }
  366. if ($coupledClass->isSubtypeOf($declaringClass)
  367. || $declaringClass->isSubtypeOf($coupledClass)
  368. ) {
  369. return;
  370. }
  371. $this->_initClassOrInterfaceDependencyMap($coupledClass);
  372. $this->_temporaryCouplingMap[
  373. $declaringClass->getUUID()
  374. ]['ce'][
  375. $coupledClass->getUUID()
  376. ] = true;
  377. $this->_temporaryCouplingMap[
  378. $coupledClass->getUUID()
  379. ]['ca'][
  380. $declaringClass->getUUID()
  381. ] = true;
  382. }
  383. /**
  384. * This method will initialize a temporary coupling container for the given
  385. * given class or interface instance.
  386. *
  387. * @param PHP_Depend_Code_AbstractClassOrInterface $classOrInterface The
  388. * currently visited/traversed class or interface instance.
  389. *
  390. * @return void
  391. * @since 0.10.2
  392. */
  393. private function _initClassOrInterfaceDependencyMap(
  394. PHP_Depend_Code_AbstractClassOrInterface $classOrInterface
  395. ) {
  396. if (isset($this->_temporaryCouplingMap[$classOrInterface->getUUID()])) {
  397. return;
  398. }
  399. $this->_temporaryCouplingMap[$classOrInterface->getUUID()] = array(
  400. 'ce' => array(),
  401. 'ca' => array()
  402. );
  403. }
  404. /**
  405. * Counts all calls within the given <b>$callable</b>
  406. *
  407. * @param PHP_Depend_Code_AbstractCallable $callable Context callable.
  408. *
  409. * @return void
  410. */
  411. private function _countCalls(PHP_Depend_Code_AbstractCallable $callable)
  412. {
  413. $invocations = $callable->findChildrenOfType(
  414. PHP_Depend_Code_ASTInvocation::CLAZZ
  415. );
  416. $invoked = array();
  417. foreach ($invocations as $invocation) {
  418. $parents = $invocation->getParentsOfType(
  419. PHP_Depend_Code_ASTMemberPrimaryPrefix::CLAZZ
  420. );
  421. $image = '';
  422. foreach ($parents as $parent) {
  423. $child = $parent->getChild(0);
  424. if ($child !== $invocation) {
  425. $image .= $child->getImage() . '.';
  426. }
  427. }
  428. $image .= $invocation->getImage() . '()';
  429. $invoked[$image] = $image;
  430. }
  431. $this->_calls += count($invoked);
  432. }
  433. }