#!/usr/bin/env php main(); class PHPTAL_Lint_CLI { function main() { try { if (! empty($_SERVER['REQUEST_URI'])) { throw new Exception("Please use this tool from command line"); } $options = $this->extended_getopt(array('-i', '-e')); if (isset($options['i'])) { $this->include_path($options['i']); } $this->require_phptal(); if (isset($options['--filenames--'])) { $paths = $options['--filenames--']; } if (!count($paths)) { $this->usage(); exit(1); } $lint = new PHPTAL_Lint(); if (empty($options['i'])) { $lint->skipUnknownModifiers(true); } $custom_extensions = NULL; if (isset($options['e'])) { $custom_extensions = preg_split('/[\s,.]+/', $options['e'][0]); $lint->acceptExtensions($custom_extensions); echo "Looking for *.", implode(', *.', $custom_extensions), " files:\n"; } foreach ($paths as $arg) { if (is_dir($arg)) { $lint->scan(rtrim($arg, DIRECTORY_SEPARATOR)); } else { $lint->testFile($arg); } } echo "\n\n"; echo "Checked ".$this->plural($lint->checked, 'file')."."; if ($lint->skipped) { echo " Skipped ".$this->plural($lint->skipped, "non-template file")."."; } echo "\n"; if (! $custom_extensions && count($lint->skipped_filenames)) { echo "Skipped file(s): ", implode(', ', array_keys($lint->skipped_filenames)), ".\n"; } if (count($lint->errors)) { echo "Found ".$this->plural(count($lint->errors), "error").":\n"; $this->display_erorr_array($lint->errors); echo "\n"; exit(2); } else if (count($lint->warnings)) { echo "Found ".$this->plural(count($lint->warnings),"warning").":\n"; $this->display_erorr_array($lint->warnings); echo "\n"; exit(0); } else { echo "No errors found!\n"; exit($lint->checked ? 0 : 1); } } catch(Exception $e) { fwrite(STDERR, $e->getMessage() . "\n"); $errcode = $e->getCode(); exit($errcode ? $errcode : 1); } } function display_erorr_array(array $errors) { $last_dir = '.'; foreach ($errors as $errinfo) { if ($errinfo[0] !== $last_dir) { echo "In ", $errinfo[0], ":\n"; $last_dir = $errinfo[0]; } echo $errinfo[1], ": ", $errinfo[2], ' (line ', $errinfo[3], ')'; echo "\n"; } } function usage() { $this->require_phptal(); echo "PHPTAL Lint 1.1.3 (PHPTAL ", strtr(PHPTAL_VERSION,"_","."), ")\n"; echo "Usage: phptal_lint.php [-e extensions] [-i php_file_or_directory] file_or_directory_to_check ...\n"; echo " -e comma-separated list of extensions\n"; echo " -i phptales file/include file, or directory\n"; echo " Use 'phptal_lint.php .' to scan current directory\n\n"; } function plural($num, $word) { if ($num == 1) return "$num $word"; return "$num {$word}s"; } function extended_getopt(array $options) { $results = array('--filenames--'=>array()); for ($i = 1; $i < count($_SERVER['argv']); $i ++) { if (in_array($_SERVER['argv'][$i], $options)) { $results[substr($_SERVER['argv'][$i], 1)][] = $_SERVER['argv'][++ $i]; } else if ($_SERVER['argv'][$i] == '--') { $results['--filenames--'] = array_merge($results['--filenames--'], array_slice($_SERVER['argv'],$i+1)); break; } else if (substr($_SERVER['argv'][$i], 0, 1) == '-') { $this->usage(); throw new Exception("{$_SERVER['argv'][$i]} is not a valid option\n\n"); } else { $results['--filenames--'][] = $_SERVER['argv'][$i]; } } return $results; } function include_path($tales) { foreach ($tales as $path) { if (is_dir($path)) { foreach (new DirectoryIterator($path) as $file) { if (preg_match('/\.php$/', "$path/$file") && is_file("$path/$file")) { include_once ("$path/$file"); } } } else if (preg_match('/\.php$/', $path) && is_file($path)) { include_once ("$path"); } } } function require_phptal() { if (class_exists('PHPTAL', false)) return; $myphptal = dirname(__FILE__) . '/../classes/PHPTAL.php'; if (file_exists($myphptal)) { require_once $myphptal; } else { require_once "PHPTAL.php"; } if (!class_exists('PHPTAL') || !defined('PHPTAL_VERSION')) { throw new Exception("Your PHPTAL installation is broken or too new for this tool"); } } } class PHPTAL_Lint { 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]+$/'; private $accept_pattern = '/\.(?:xml|[px]?html|zpt|phptal|tal|tpl)$/i'; private $skipUnknownModifiers = false; public $errors = array(); public $warnings = array(); public $ignored = array(); public $skipped = 0, $skipped_filenames = array(); public $checked = 0; function skipUnknownModifiers($bool) { $this->skipUnknownModifiers = $bool; } function acceptExtensions(array $ext) { $this->accept_pattern = '/\.(?:' . implode('|', $ext) . ')$/i'; } protected function reportProgress($symbol) { echo $symbol; } function scan($path) { foreach (new DirectoryIterator($path) as $entry) { $filename = $entry->getFilename(); if ($filename === '.' || $filename === '..') { continue; } if (preg_match($this->ignore_pattern, $filename)) { $this->skipped++; continue; } if ($entry->isDir()) { $this->reportProgress('.'); $this->scan($path . DIRECTORY_SEPARATOR . $filename); continue; } if (! preg_match($this->accept_pattern, $filename)) { $this->skipped++; $this->skipped_filenames[$filename] = true; continue; } $result = $this->testFile($path . DIRECTORY_SEPARATOR . $filename); if (self::TEST_OK == $result) { $this->reportProgress('.'); } else if (self::TEST_ERROR == $result) { $this->reportProgress('E'); } else if (self::TEST_SKIPPED == $result) { $this->reportProgress('S'); } } } const TEST_OK = 1; const TEST_ERROR = 2; const TEST_SKIPPED = 3; /** * @return int - one of TEST_* constants */ function testFile($fullpath) { try { $this->checked ++; $phptal = new PHPTAL($fullpath); $phptal->setForceReparse(true); $phptal->prepare(); return self::TEST_OK; } catch(PHPTAL_UnknownModifierException $e) { if ($this->skipUnknownModifiers && is_callable(array($e, 'getModifierName'))) { $this->warnings[] = array(dirname($fullpath), basename($fullpath), "Unknown expression modifier: ".$e->getModifierName()." (use -i to include your custom modifier functions)", $e->getLine()); return self::TEST_SKIPPED; } $log_exception = $e; } catch(Exception $e) { $log_exception = $e; } // Takes exception from either of the two catch blocks above $this->errors[] = array(dirname($fullpath) , basename($fullpath) , $log_exception->getMessage() , $log_exception->getLine()); return self::TEST_ERROR; } }