PageRenderTime 38ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/phptal_lint.php

http://github.com/pornel/PHPTAL
PHP | 283 lines | 221 code | 40 blank | 22 comment | 55 complexity | fbe69e95cb167f03e1bf1de483cb8461 MD5 | raw file
Possible License(s): LGPL-2.1
  1. #!/usr/bin/env php
  2. <?php
  3. /**
  4. * This is lint tool for checking corectness of template syntax.
  5. *
  6. * You can run it on all your templates after upgrade of PHPTAL to check
  7. * for potential incompatibilities.
  8. *
  9. * Another good idea is to use it as SVN hook to ensure that you
  10. * commit only good templates to your repository.
  11. *
  12. * See more:
  13. * http://phptal.org/wiki/doku.php/lint
  14. *
  15. * or run
  16. *
  17. * ./phptal_lint.php -h
  18. *
  19. */
  20. $lint = new PHPTAL_Lint_CLI();
  21. $lint->main();
  22. class PHPTAL_Lint_CLI
  23. {
  24. function main()
  25. {
  26. try
  27. {
  28. if (! empty($_SERVER['REQUEST_URI'])) {
  29. throw new Exception("Please use this tool from command line");
  30. }
  31. $options = $this->extended_getopt(array('-i', '-e'));
  32. if (isset($options['i'])) {
  33. $this->include_path($options['i']);
  34. }
  35. $this->require_phptal();
  36. if (isset($options['--filenames--'])) {
  37. $paths = $options['--filenames--'];
  38. }
  39. if (!count($paths)) {
  40. $this->usage();
  41. exit(1);
  42. }
  43. $lint = new PHPTAL_Lint();
  44. if (empty($options['i'])) {
  45. $lint->skipUnknownModifiers(true);
  46. }
  47. $custom_extensions = NULL;
  48. if (isset($options['e'])) {
  49. $custom_extensions = preg_split('/[\s,.]+/', $options['e'][0]);
  50. $lint->acceptExtensions($custom_extensions);
  51. echo "Looking for *.", implode(', *.', $custom_extensions), " files:\n";
  52. }
  53. foreach ($paths as $arg) {
  54. if (is_dir($arg)) {
  55. $lint->scan(rtrim($arg, DIRECTORY_SEPARATOR));
  56. } else {
  57. $lint->testFile($arg);
  58. }
  59. }
  60. echo "\n\n";
  61. echo "Checked ".$this->plural($lint->checked, 'file').".";
  62. if ($lint->skipped) {
  63. echo " Skipped ".$this->plural($lint->skipped, "non-template file").".";
  64. }
  65. echo "\n";
  66. if (! $custom_extensions && count($lint->skipped_filenames)) {
  67. echo "Skipped file(s): ", implode(', ', array_keys($lint->skipped_filenames)), ".\n";
  68. }
  69. if (count($lint->errors)) {
  70. echo "Found ".$this->plural(count($lint->errors), "error").":\n";
  71. $this->display_erorr_array($lint->errors);
  72. echo "\n";
  73. exit(2);
  74. } else if (count($lint->warnings)) {
  75. echo "Found ".$this->plural(count($lint->warnings),"warning").":\n";
  76. $this->display_erorr_array($lint->warnings);
  77. echo "\n";
  78. exit(0);
  79. } else {
  80. echo "No errors found!\n";
  81. exit($lint->checked ? 0 : 1);
  82. }
  83. }
  84. catch(Exception $e) {
  85. fwrite(STDERR, $e->getMessage() . "\n");
  86. $errcode = $e->getCode();
  87. exit($errcode ? $errcode : 1);
  88. }
  89. }
  90. function display_erorr_array(array $errors)
  91. {
  92. $last_dir = '.';
  93. foreach ($errors as $errinfo) {
  94. if ($errinfo[0] !== $last_dir) {
  95. echo "In ", $errinfo[0], ":\n";
  96. $last_dir = $errinfo[0];
  97. }
  98. echo $errinfo[1], ": ", $errinfo[2], ' (line ', $errinfo[3], ')';
  99. echo "\n";
  100. }
  101. }
  102. function usage()
  103. {
  104. $this->require_phptal();
  105. echo "PHPTAL Lint 1.1.3 (PHPTAL ", strtr(PHPTAL_VERSION,"_","."), ")\n";
  106. echo "Usage: phptal_lint.php [-e extensions] [-i php_file_or_directory] file_or_directory_to_check ...\n";
  107. echo " -e comma-separated list of extensions\n";
  108. echo " -i phptales file/include file, or directory\n";
  109. echo " Use 'phptal_lint.php .' to scan current directory\n\n";
  110. }
  111. function plural($num, $word)
  112. {
  113. if ($num == 1) return "$num $word";
  114. return "$num {$word}s";
  115. }
  116. function extended_getopt(array $options)
  117. {
  118. $results = array('--filenames--'=>array());
  119. for ($i = 1; $i < count($_SERVER['argv']); $i ++) {
  120. if (in_array($_SERVER['argv'][$i], $options)) {
  121. $results[substr($_SERVER['argv'][$i], 1)][] = $_SERVER['argv'][++ $i];
  122. } else if ($_SERVER['argv'][$i] == '--') {
  123. $results['--filenames--'] = array_merge($results['--filenames--'], array_slice($_SERVER['argv'],$i+1));
  124. break;
  125. } else if (substr($_SERVER['argv'][$i], 0, 1) == '-') {
  126. $this->usage();
  127. throw new Exception("{$_SERVER['argv'][$i]} is not a valid option\n\n");
  128. } else {
  129. $results['--filenames--'][] = $_SERVER['argv'][$i];
  130. }
  131. }
  132. return $results;
  133. }
  134. function include_path($tales)
  135. {
  136. foreach ($tales as $path) {
  137. if (is_dir($path)) {
  138. foreach (new DirectoryIterator($path) as $file) {
  139. if (preg_match('/\.php$/', "$path/$file") && is_file("$path/$file")) {
  140. include_once ("$path/$file");
  141. }
  142. }
  143. } else if (preg_match('/\.php$/', $path) && is_file($path)) {
  144. include_once ("$path");
  145. }
  146. }
  147. }
  148. function require_phptal()
  149. {
  150. if (class_exists('PHPTAL', false)) return;
  151. $myphptal = dirname(__FILE__) . '/../classes/PHPTAL.php';
  152. if (file_exists($myphptal)) {
  153. require_once $myphptal;
  154. } else {
  155. require_once "PHPTAL.php";
  156. }
  157. if (!class_exists('PHPTAL') || !defined('PHPTAL_VERSION')) {
  158. throw new Exception("Your PHPTAL installation is broken or too new for this tool");
  159. }
  160. }
  161. }
  162. class PHPTAL_Lint
  163. {
  164. private $ignore_pattern = '/^\.|\.(?i:php|inc|jpe?g|gif|png|mo|po|txt|orig|rej|xsl|xsd|sh|in|ini|conf|css|js|py|pdf|swf|csv|ico|jar|htc)$|^Makefile|^[A-Z]+$/';
  165. private $accept_pattern = '/\.(?:xml|[px]?html|zpt|phptal|tal|tpl)$/i';
  166. private $skipUnknownModifiers = false;
  167. public $errors = array();
  168. public $warnings = array();
  169. public $ignored = array();
  170. public $skipped = 0, $skipped_filenames = array();
  171. public $checked = 0;
  172. function skipUnknownModifiers($bool)
  173. {
  174. $this->skipUnknownModifiers = $bool;
  175. }
  176. function acceptExtensions(array $ext) {
  177. $this->accept_pattern = '/\.(?:' . implode('|', $ext) . ')$/i';
  178. }
  179. protected function reportProgress($symbol)
  180. {
  181. echo $symbol;
  182. }
  183. function scan($path)
  184. {
  185. foreach (new DirectoryIterator($path) as $entry) {
  186. $filename = $entry->getFilename();
  187. if ($filename === '.' || $filename === '..') {
  188. continue;
  189. }
  190. if (preg_match($this->ignore_pattern, $filename)) {
  191. $this->skipped++;
  192. continue;
  193. }
  194. if ($entry->isDir()) {
  195. $this->reportProgress('.');
  196. $this->scan($path . DIRECTORY_SEPARATOR . $filename);
  197. continue;
  198. }
  199. if (! preg_match($this->accept_pattern, $filename)) {
  200. $this->skipped++;
  201. $this->skipped_filenames[$filename] = true;
  202. continue;
  203. }
  204. $result = $this->testFile($path . DIRECTORY_SEPARATOR . $filename);
  205. if (self::TEST_OK == $result) {
  206. $this->reportProgress('.');
  207. } else if (self::TEST_ERROR == $result) {
  208. $this->reportProgress('E');
  209. } else if (self::TEST_SKIPPED == $result) {
  210. $this->reportProgress('S');
  211. }
  212. }
  213. }
  214. const TEST_OK = 1;
  215. const TEST_ERROR = 2;
  216. const TEST_SKIPPED = 3;
  217. /**
  218. * @return int - one of TEST_* constants
  219. */
  220. function testFile($fullpath)
  221. {
  222. try {
  223. $this->checked ++;
  224. $phptal = new PHPTAL($fullpath);
  225. $phptal->setForceReparse(true);
  226. $phptal->prepare();
  227. return self::TEST_OK;
  228. }
  229. catch(PHPTAL_UnknownModifierException $e) {
  230. if ($this->skipUnknownModifiers && is_callable(array($e, 'getModifierName'))) {
  231. $this->warnings[] = array(dirname($fullpath), basename($fullpath), "Unknown expression modifier: ".$e->getModifierName()." (use -i to include your custom modifier functions)", $e->getLine());
  232. return self::TEST_SKIPPED;
  233. }
  234. $log_exception = $e;
  235. }
  236. catch(Exception $e) {
  237. $log_exception = $e;
  238. }
  239. // Takes exception from either of the two catch blocks above
  240. $this->errors[] = array(dirname($fullpath) , basename($fullpath) , $log_exception->getMessage() , $log_exception->getLine());
  241. return self::TEST_ERROR;
  242. }
  243. }