PhpDependTask.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. <?php
  2. /**
  3. * $Id: PhpDependTask.php 905 2010-10-05 16:28:03Z mrook $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information please see
  19. * <http://phing.info>.
  20. */
  21. require_once 'phing/Task.php';
  22. /**
  23. * Runs the PHP_Depend software analyzer and metric tool.
  24. * Performs static code analysis on a given source base.
  25. *
  26. * @package phing.tasks.ext.pdepend
  27. * @author Benjamin Schultz <bschultz@proqrent.de>
  28. * @version $Id: PhpDependTask.php 905 2010-10-05 16:28:03Z mrook $
  29. * @since 2.4.1
  30. */
  31. class PhpDependTask extends Task
  32. {
  33. /**
  34. * A php source code filename or directory
  35. *
  36. * @var PhingFile
  37. */
  38. protected $_file = null;
  39. /**
  40. * All fileset objects assigned to this task
  41. *
  42. * @var array<FileSet>
  43. */
  44. protected $_filesets = array();
  45. /**
  46. * List of allowed file extensions. Default file extensions are <b>php</b>
  47. * and <p>php5</b>.
  48. *
  49. * @var array<string>
  50. */
  51. protected $_allowedFileExtensions = array('php', 'php5');
  52. /**
  53. * List of exclude directories. Default exclude dirs are <b>.git</b>,
  54. * <b>.svn</b> and <b>CVS</b>.
  55. *
  56. * @var array<string>
  57. */
  58. protected $_excludeDirectories = array('.git', '.svn', 'CVS');
  59. /**
  60. * List of exclude packages
  61. *
  62. * @var array<string>
  63. */
  64. protected $_excludePackages = array();
  65. /**
  66. * Should the parse ignore doc comment annotations?
  67. *
  68. * @var boolean
  69. */
  70. protected $_withoutAnnotations = false;
  71. /**
  72. * Should PHP_Depend treat <b>+global</b> as a regular project package?
  73. *
  74. * @var boolean
  75. */
  76. protected $_supportBadDocumentation = false;
  77. /**
  78. * Flag for enable/disable debugging
  79. *
  80. * @var boolean
  81. */
  82. protected $_debug = false;
  83. /**
  84. * PHP_Depend configuration file
  85. *
  86. * @var PhingFile
  87. */
  88. protected $_configFile = null;
  89. /**
  90. * Logger elements
  91. *
  92. * @var array<PhpDependLoggerElement>
  93. */
  94. protected $_loggers = array();
  95. /**
  96. * Analyzer elements
  97. *
  98. * @var array<PhpDependAnalyzerElement>
  99. */
  100. protected $_analyzers = array();
  101. /**
  102. * Holds the PHP_Depend runner instance
  103. *
  104. * @var PHP_Depend_TextUI_Runner
  105. */
  106. protected $_runner = null;
  107. /**
  108. * Holds the optimization
  109. *
  110. * @var string
  111. */
  112. protected $_optimization = '';
  113. /**
  114. * Holds the available optimizations
  115. *
  116. * @var array<string>
  117. */
  118. private $_optimizations = array();
  119. /**
  120. * Flag that determines whether to halt on error
  121. *
  122. * @var boolean
  123. */
  124. protected $_haltonerror = false;
  125. /**
  126. * Load the necessary environment for running PHP_Depend
  127. *
  128. * @return void
  129. * @throws BuildException
  130. */
  131. public function init()
  132. {
  133. /**
  134. * Determine PHP_Depend installation
  135. */
  136. @include_once 'PHP/Depend/TextUI/Runner.php';
  137. if (! class_exists('PHP_Depend_TextUI_Runner')) {
  138. throw new BuildException(
  139. 'PhpDependTask depends on PHP_Depend being installed '
  140. . 'and on include_path',
  141. $this->getLocation()
  142. );
  143. }
  144. $this->_optimizations[] = PHP_Depend_TextUI_Runner::OPTIMZATION_BEST;
  145. $this->_optimizations[] = PHP_Depend_TextUI_Runner::OPTIMZATION_NONE;
  146. /**
  147. * Other dependencies that should only be loaded
  148. * when class is actually used
  149. */
  150. require_once 'phing/tasks/ext/pdepend/PhpDependLoggerElement.php';
  151. require_once 'phing/tasks/ext/pdepend/PhpDependAnalyzerElement.php';
  152. require_once 'PHP/Depend/TextUI/ResultPrinter.php';
  153. require_once 'PHP/Depend/Util/Configuration.php';
  154. require_once 'PHP/Depend/Util/ConfigurationInstance.php';
  155. }
  156. /**
  157. * Set the input source file or directory
  158. *
  159. * @param PhingFile $file The input source file or directory
  160. *
  161. * @return void
  162. */
  163. public function setFile(PhingFile $file)
  164. {
  165. $this->_file = $file;
  166. }
  167. /**
  168. * Nested creator, adds a set of files (nested fileset attribute)
  169. *
  170. * @return FileSet The created fileset object
  171. */
  172. public function createFileSet()
  173. {
  174. $num = array_push($this->_filesets, new FileSet());
  175. return $this->_filesets[$num-1];
  176. }
  177. /**
  178. * Sets a list of filename extensions for valid php source code files
  179. *
  180. * @param string $fileExtensions List of valid file extensions
  181. *
  182. * @return void
  183. */
  184. public function setAllowedFileExtensions($fileExtensions)
  185. {
  186. $this->_allowedFileExtensions = array();
  187. $token = ' ,;';
  188. $ext = strtok($fileExtensions, $token);
  189. while ($ext !== false) {
  190. $this->_allowedFileExtensions[] = $ext;
  191. $ext = strtok($token);
  192. }
  193. }
  194. /**
  195. * Sets a list of exclude directories
  196. *
  197. * @param string $excludeDirectories List of exclude directories
  198. *
  199. * @return void
  200. */
  201. public function setExcludeDirectories($excludeDirectories)
  202. {
  203. $this->_excludeDirectories = array();
  204. $token = ' ,;';
  205. $pattern = strtok($excludeDirectories, $token);
  206. while ($pattern !== false) {
  207. $this->_excludeDirectories[] = $pattern;
  208. $pattern = strtok($token);
  209. }
  210. }
  211. /**
  212. * Sets a list of exclude packages
  213. *
  214. * @param string $excludePackages Exclude packages
  215. *
  216. * @return void
  217. */
  218. public function setExcludePackages($excludePackages)
  219. {
  220. $this->_excludePackages = array();
  221. $token = ' ,;';
  222. $pattern = strtok($excludePackages, $token);
  223. while ($pattern !== false) {
  224. $this->_excludePackages[] = $pattern;
  225. $pattern = strtok($token);
  226. }
  227. }
  228. /**
  229. * Should the parser ignore doc comment annotations?
  230. *
  231. * @param boolean $withoutAnnotations
  232. *
  233. * @return void
  234. */
  235. public function setWithoutAnnotations($withoutAnnotations)
  236. {
  237. $this->_withoutAnnotations = StringHelper::booleanValue(
  238. $withoutAnnotations
  239. );
  240. }
  241. /**
  242. * Should PHP_Depend support projects with a bad documentation. If this
  243. * option is set to <b>true</b>, PHP_Depend will treat the default package
  244. * <b>+global</b> as a regular project package.
  245. *
  246. * @param boolean $supportBadDocumentation
  247. *
  248. * @return void
  249. */
  250. public function setSupportBadDocumentation($supportBadDocumentation)
  251. {
  252. $this->_supportBadDocumentation = StringHelper::booleanValue(
  253. $supportBadDocumentation
  254. );
  255. }
  256. /**
  257. * Set debugging On/Off
  258. *
  259. * @param boolean $debug
  260. *
  261. * @return void
  262. */
  263. public function setDebug($debug)
  264. {
  265. $this->_debug = StringHelper::booleanValue($debug);
  266. }
  267. /**
  268. * Set halt on error
  269. *
  270. * @param boolean $haltonerror
  271. *
  272. * @return void
  273. */
  274. public function setHaltonerror($haltonerror)
  275. {
  276. $this->_haltonerror = StringHelper::booleanValue($haltonerror);
  277. }
  278. /**
  279. * Set the configuration file
  280. *
  281. * @param PhingFile $configFile The configuration file
  282. *
  283. * @return void
  284. */
  285. public function setConfigFile(PhingFile $configFile)
  286. {
  287. $this->_configFile = $configFile;
  288. }
  289. /**
  290. * Create object for nested logger element
  291. *
  292. * @return PhpDependLoggerElement
  293. */
  294. public function createLogger()
  295. {
  296. $num = array_push($this->_loggers, new PhpDependLoggerElement());
  297. return $this->_loggers[$num-1];
  298. }
  299. /**
  300. * Create object for nested analyzer element
  301. *
  302. * @return PhpDependAnalyzerElement
  303. */
  304. public function createAnalyzer()
  305. {
  306. $num = array_push($this->_analyzers, new PhpDependAnalyzerElement());
  307. return $this->_analyzers[$num-1];
  308. }
  309. /**
  310. * Executes PHP_Depend_TextUI_Runner against PhingFile or a FileSet
  311. *
  312. * @return void
  313. * @throws BuildException
  314. */
  315. public function main()
  316. {
  317. if (!isset($this->_file) and count($this->_filesets) == 0) {
  318. throw new BuildException(
  319. "Missing either a nested fileset or attribute 'file' set"
  320. );
  321. }
  322. if (count($this->_loggers) == 0) {
  323. throw new BuildException("Missing nested 'logger' element");
  324. }
  325. $this->validateLoggers();
  326. $this->validateAnalyzers();
  327. $filesToParse = array();
  328. if ($this->_file instanceof PhingFile) {
  329. $filesToParse[] = $this->_file->__toString();
  330. } else {
  331. // append any files in filesets
  332. foreach ($this->_filesets as $fs) {
  333. $files = $fs->getDirectoryScanner($this->project)
  334. ->getIncludedFiles();
  335. foreach ($files as $filename) {
  336. $f = new PhingFile($fs->getDir($this->project), $filename);
  337. $filesToParse[] = $f->getAbsolutePath();
  338. }
  339. }
  340. }
  341. $this->_runner = new PHP_Depend_TextUI_Runner();
  342. $this->_runner->addProcessListener(new PHP_Depend_TextUI_ResultPrinter());
  343. $this->_runner->setSourceArguments($filesToParse);
  344. foreach ($this->_loggers as $logger) {
  345. // Register logger
  346. $this->_runner->addLogger(
  347. $logger->getType(),
  348. $logger->getOutfile()->__toString()
  349. );
  350. }
  351. foreach ($this->_analyzers as $analyzer) {
  352. // Register additional analyzer
  353. $this->_runner->addOption(
  354. $analyzer->getType(),
  355. $analyzer->getValue()
  356. );
  357. }
  358. // Disable annotation parsing
  359. if ($this->_withoutAnnotations) {
  360. $this->_runner->setWithoutAnnotations();
  361. }
  362. // Enable bad documentation support
  363. if ($this->_supportBadDocumentation) {
  364. $this->_runner->setSupportBadDocumentation();
  365. }
  366. // Check for suffix
  367. if (count($this->_allowedFileExtensions) > 0) {
  368. $this->_runner->setFileExtensions($this->_allowedFileExtensions);
  369. }
  370. // Check for ignore directories
  371. if (count($this->_excludeDirectories) > 0) {
  372. $this->_runner->setExcludeDirectories($this->_excludeDirectories);
  373. }
  374. // Check for exclude packages
  375. if (count($this->_excludePackages) > 0) {
  376. $this->_runner->setExcludePackages($this->_excludePackages);
  377. }
  378. // Check optimization strategy
  379. if ($this->_optimization !== '') {
  380. if (in_array($this->_optimization, $this->_optimizations)) {
  381. // Set optimization strategy
  382. $this->_runner->setOptimization($this->_optimization);
  383. } else {
  384. throw new BuildException(
  385. 'Invalid optimization "' . $this->_optimization . '" given.'
  386. );
  387. }
  388. }
  389. // Check for configuration option
  390. if ($this->_configFile instanceof PhingFile) {
  391. if (file_exists($this->_configFile->__toString()) === false) {
  392. throw new BuildException(
  393. 'The configuration file "'
  394. . $this->_configFile->__toString() . '" doesn\'t exist.'
  395. );
  396. }
  397. // Load configuration file
  398. $config = new PHP_Depend_Util_Configuration(
  399. $this->_configFile->__toString(),
  400. null,
  401. true
  402. );
  403. // Store in config registry
  404. PHP_Depend_Util_ConfigurationInstance::set($config);
  405. }
  406. if ($this->_debug) {
  407. require_once 'PHP/Depend/Util/Log.php';
  408. // Enable debug logging
  409. PHP_Depend_Util_Log::setSeverity(PHP_Depend_Util_Log::DEBUG);
  410. }
  411. $this->_runner->run();
  412. if ($this->_runner->hasParseErrors() === true) {
  413. $this->log('Following errors occurred:');
  414. foreach ($this->_runner->getParseErrors() as $error) {
  415. $this->log($error);
  416. }
  417. if ($this->_haltonerror === true) {
  418. throw new BuildException('Errors occurred during parse process');
  419. }
  420. }
  421. }
  422. /**
  423. * Validates the available loggers
  424. *
  425. * @return void
  426. * @throws BuildException
  427. */
  428. protected function validateLoggers()
  429. {
  430. foreach ($this->_loggers as $logger) {
  431. if ($logger->getType() === '') {
  432. throw new BuildException(
  433. "Logger missing required 'type' attribute"
  434. );
  435. }
  436. if ($logger->getOutfile() === null) {
  437. throw new BuildException(
  438. "Logger requires 'outfile' attribute"
  439. );
  440. }
  441. }
  442. }
  443. /**
  444. * Validates the available analyzers
  445. *
  446. * @return void
  447. * @throws BuildException
  448. */
  449. protected function validateAnalyzers()
  450. {
  451. foreach ($this->_analyzers as $analyzer) {
  452. if ($analyzer->getType() === '') {
  453. throw new BuildException(
  454. "Analyzer missing required 'type' attribute"
  455. );
  456. }
  457. if (count($analyzer->getValue()) === 0) {
  458. throw new BuildException(
  459. "Analyzer missing required 'value' attribute"
  460. );
  461. }
  462. }
  463. }
  464. }