PageRenderTime 66ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/src/frapi/tests/phpunit/PHPUnit/Util/Metrics/Project.php

http://github.com/frapi/frapi
PHP | 476 lines | 256 code | 56 blank | 164 comment | 29 complexity | cfa5200f7738ada11d4ce674d5cafc8e MD5 | raw file
Possible License(s): BSD-2-Clause
  1. <?php
  2. /**
  3. * PHPUnit
  4. *
  5. * Copyright (c) 2002-2009, Sebastian Bergmann <sb@sebastian-bergmann.de>.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * * Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  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
  18. * distribution.
  19. *
  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.
  23. *
  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.
  36. *
  37. * @category Testing
  38. * @package PHPUnit
  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
  42. * @version SVN: $Id: Project.php 4561 2009-01-26 15:22:43Z sb $
  43. * @link http://www.phpunit.de/
  44. * @since File available since Release 3.2.0
  45. */
  46. require_once 'PHPUnit/Util/Filter.php';
  47. require_once 'PHPUnit/Util/Metrics.php';
  48. PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
  49. /**
  50. * Project-Level Metrics.
  51. *
  52. * @category Testing
  53. * @package PHPUnit
  54. * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
  55. * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
  56. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  57. * @version Release: @package_version@
  58. * @link http://www.phpunit.de/
  59. * @since Class available since Release 3.2.0
  60. */
  61. class PHPUnit_Util_Metrics_Project extends PHPUnit_Util_Metrics
  62. {
  63. protected static $CPD_IGNORE_LIST = array(
  64. T_INLINE_HTML,
  65. T_COMMENT,
  66. T_DOC_COMMENT,
  67. T_OPEN_TAG,
  68. T_OPEN_TAG_WITH_ECHO,
  69. T_CLOSE_TAG,
  70. T_WHITESPACE
  71. );
  72. protected $classes = array();
  73. protected $files = array();
  74. protected $functions = array();
  75. protected $cls = 0;
  76. protected $clsa = 0;
  77. protected $clsc = 0;
  78. protected $interfs = 0;
  79. protected $roots = 0;
  80. protected $leafs = 0;
  81. protected $maxDit = 0;
  82. protected $cpdDuplicates = array();
  83. protected $cpdHashes = array();
  84. protected $dependencies = array();
  85. /**
  86. * Constructor.
  87. *
  88. * @param array $files
  89. * @param array $codeCoverage
  90. * @param boolean $cpd
  91. * @param integer $cpdMinLines
  92. * @param integer $cpdMinMatches
  93. */
  94. public function __construct(Array $files, &$codeCoverage = array(), $cpd = FALSE, $cpdMinLines = 5, $cpdMinMatches = 70)
  95. {
  96. foreach ($files as $file) {
  97. if (file_exists($file)) {
  98. $this->files[$file] = PHPUnit_Util_Metrics_File::factory(
  99. $file, $codeCoverage
  100. );
  101. foreach ($this->files[$file]->getFunctions() as $function) {
  102. $this->functions[$function->getFunction()->getName()] = $function;
  103. }
  104. foreach ($this->files[$file]->getClasses() as $class) {
  105. $className = $class->getClass()->getName();
  106. $package = $class->getPackage();
  107. $this->classes[$className] = $class;
  108. if ($class->getClass()->isInterface()) {
  109. $this->interfs++;
  110. } else {
  111. if ($class->getClass()->isAbstract()) {
  112. $this->clsa++;
  113. } else {
  114. $this->clsc++;
  115. }
  116. $this->cls++;
  117. }
  118. }
  119. }
  120. }
  121. $numClasses = count($this->classes);
  122. foreach ($this->classes as $a => $b) {
  123. foreach ($this->classes as $c => $d) {
  124. $this->dependencies[$a][$c] = 0;
  125. }
  126. }
  127. foreach ($this->classes as $className => $class) {
  128. foreach ($class->getDependencies() as $dependency) {
  129. $this->dependencies[$className][$dependency] = 1;
  130. }
  131. $class->setProject($this);
  132. if ($class->getNOC() == 0) {
  133. $this->leafs++;
  134. }
  135. else if ($class->getClass()->getParentClass() === FALSE) {
  136. $this->roots++;
  137. }
  138. $this->maxDit = max($this->maxDit, $class->getDit());
  139. }
  140. if ($cpd) {
  141. $this->copyPasteDetection($cpdMinLines, $cpdMinMatches);
  142. }
  143. }
  144. /**
  145. * Returns the classes of this project.
  146. *
  147. * @return array
  148. */
  149. public function getClasses()
  150. {
  151. return $this->classes;
  152. }
  153. /**
  154. * A class.
  155. *
  156. * @param string $className
  157. * @return ReflectionClass
  158. */
  159. public function getClass($className)
  160. {
  161. return $this->classes[$className];
  162. }
  163. /**
  164. * Returns the dependencies between the classes of this project.
  165. *
  166. * @return array
  167. */
  168. public function getDependencies()
  169. {
  170. return $this->dependencies;
  171. }
  172. /**
  173. * Returns the dependencies between the classes of this project
  174. * as GraphViz/DOT markup.
  175. *
  176. * @return Image_GraphViz
  177. * @since Method available since Release 3.2.2
  178. */
  179. public function getDependenciesAsDOT()
  180. {
  181. if (PHPUnit_Util_Filesystem::fileExistsInIncludePath('Image/GraphViz.php')) {
  182. require_once 'Image/GraphViz.php';
  183. } else {
  184. throw new RuntimeException('Image_GraphViz is not available.');
  185. }
  186. $graph = new Image_GraphViz(
  187. TRUE,
  188. array(
  189. 'overlap' => 'scale',
  190. 'splines' => 'true',
  191. 'sep' => '.1',
  192. 'fontsize' => '8'
  193. )
  194. );
  195. foreach (array_keys($this->dependencies) as $className) {
  196. $graph->addNode($className);
  197. }
  198. foreach ($this->dependencies as $from => $dependencies) {
  199. foreach ($dependencies as $to => $flag) {
  200. if ($flag === 1) {
  201. $graph->addEdge(array($from => $to));
  202. }
  203. }
  204. }
  205. return $graph;
  206. }
  207. /**
  208. * Returns the duplicates found by the Copy & Paste Detection (CPD).
  209. *
  210. * @return array
  211. */
  212. public function getDuplicates()
  213. {
  214. return $this->cpdDuplicates;
  215. }
  216. /**
  217. * Returns the files of this project.
  218. *
  219. * @return array
  220. */
  221. public function getFiles()
  222. {
  223. return $this->files;
  224. }
  225. /**
  226. * A file.
  227. *
  228. * @param string $className
  229. * @return ReflectionClass
  230. */
  231. public function getFile($filename)
  232. {
  233. return $this->files[$filename];
  234. }
  235. /**
  236. * Functions.
  237. *
  238. * @return array
  239. */
  240. public function getFunctions()
  241. {
  242. return $this->functions;
  243. }
  244. /**
  245. * A function.
  246. *
  247. * @param string $functionName
  248. * @return ReflectionClass
  249. */
  250. public function getFunction($functionName)
  251. {
  252. return $this->functions[$functionName];
  253. }
  254. /**
  255. * Returns the Number of Classes (CLS) for the project.
  256. *
  257. * @return integer
  258. * @see http://www.aivosto.com/project/help/pm-oo-misc.html
  259. */
  260. public function getCLS()
  261. {
  262. return $this->cls;
  263. }
  264. /**
  265. * Returns the Number of Abstract Classes (CLSa) for the project.
  266. *
  267. * @return integer
  268. * @see http://www.aivosto.com/project/help/pm-oo-misc.html
  269. */
  270. public function getCLSa()
  271. {
  272. return $this->clsa;
  273. }
  274. /**
  275. * Returns the Number of Concrete Classes (CLSc) for the project.
  276. *
  277. * @return integer
  278. * @see http://www.aivosto.com/project/help/pm-oo-misc.html
  279. */
  280. public function getCLSc()
  281. {
  282. return $this->clsc;
  283. }
  284. /**
  285. * Returns the Number of Root Classes (ROOTS) for the project.
  286. *
  287. * @return integer
  288. * @see http://www.aivosto.com/project/help/pm-oo-misc.html
  289. */
  290. public function getRoots()
  291. {
  292. return $this->roots;
  293. }
  294. /**
  295. * Returns the Number of Leaf Classes (LEAFS) for the project.
  296. *
  297. * @return integer
  298. * @see http://www.aivosto.com/project/help/pm-oo-misc.html
  299. */
  300. public function getLeafs()
  301. {
  302. return $this->leafs;
  303. }
  304. /**
  305. * Returns the Number of Interfaces (INTERFS) for the project.
  306. *
  307. * @return integer
  308. * @see http://www.aivosto.com/project/help/pm-oo-misc.html
  309. */
  310. public function getInterfs()
  311. {
  312. return $this->interfs;
  313. }
  314. /**
  315. * Returns the Maximum Depth of Intheritance Tree (maxDIT) for the project.
  316. *
  317. * @return integer
  318. * @see http://www.aivosto.com/project/help/pm-oo-misc.html
  319. */
  320. public function getMaxDit()
  321. {
  322. return $this->maxDit;
  323. }
  324. /**
  325. * Copy & Paste Detection (CPD).
  326. *
  327. * @param integer $minLines
  328. * @param integer $minMatches
  329. * @author Johann-Peter Hartmann <johann-peter.hartmann@mayflower.de>
  330. */
  331. protected function copyPasteDetection($minLines, $minMatches)
  332. {
  333. foreach ($this->files as $file) {
  334. $currentTokenPositions = array();
  335. $currentSignature = '';
  336. $tokens = $file->getTokens();
  337. $tokenNr = 0;
  338. $line = 1;
  339. foreach (array_keys($tokens) as $key) {
  340. $token = $tokens[$key];
  341. if (is_string($token)) {
  342. $line += substr_count($token, "\n");
  343. } else {
  344. if (!in_array($token[0], self::$CPD_IGNORE_LIST)) {
  345. $currentTokenPositions[$tokenNr++] = $line;
  346. $currentSignature .= chr($token[0] & 255) . pack('N*', crc32($token[1]));
  347. }
  348. $line += substr_count($token[1], "\n");
  349. }
  350. }
  351. $tokenNr = 0;
  352. $firstLine = 0;
  353. $found = FALSE;
  354. if (count($currentTokenPositions) > 0) {
  355. do {
  356. $line = $currentTokenPositions[$tokenNr];
  357. $hash = substr(
  358. md5(
  359. substr(
  360. $currentSignature, $tokenNr * 5,
  361. $minMatches * 5
  362. ),
  363. TRUE
  364. ),
  365. 0,
  366. 8
  367. );
  368. if (isset($this->cpdHashes[$hash])) {
  369. $found = TRUE;
  370. if ($firstLine === 0) {
  371. $firstLine = $line;
  372. $firstHash = $hash;
  373. $firstToken = $tokenNr;
  374. }
  375. } else {
  376. if ($found) {
  377. $fileA = $this->cpdHashes[$firstHash][0];
  378. $firstLineA = $this->cpdHashes[$firstHash][1];
  379. if ($line + 1 - $firstLine > $minLines &&
  380. ($fileA->getPath() != $file->getPath() ||
  381. $firstLineA != $firstLine)) {
  382. $this->cpdDuplicates[] = array(
  383. 'fileA' => $fileA,
  384. 'firstLineA' => $firstLineA,
  385. 'fileB' => $file,
  386. 'firstLineB' => $firstLine,
  387. 'numLines' => $line + 1 - $firstLine,
  388. 'numTokens' => $tokenNr + 1 - $firstToken
  389. );
  390. }
  391. $found = FALSE;
  392. $firstLine = 0;
  393. }
  394. $this->cpdHashes[$hash] = array($file, $line);
  395. }
  396. $tokenNr++;
  397. } while ($tokenNr <= (count($currentTokenPositions) -
  398. $minMatches )+1);
  399. }
  400. if ($found) {
  401. $fileA = $this->cpdHashes[$firstHash][0];
  402. $firstLineA = $this->cpdHashes[$firstHash][1];
  403. if ($line + 1 - $firstLine > $minLines &&
  404. ($fileA->getPath() != $file->getPath() ||
  405. $firstLineA != $firstLine)) {
  406. $this->cpdDuplicates[] = array(
  407. 'fileA' => $fileA,
  408. 'firstLineA' => $firstLineA,
  409. 'fileB' => $file,
  410. 'firstLineB' => $firstLine,
  411. 'numLines' => $line + 1 - $firstLine,
  412. 'numTokens' => $tokenNr + 1 - $firstToken
  413. );
  414. }
  415. $found = FALSE;
  416. }
  417. }
  418. }
  419. }
  420. ?>