From 89726a06721c1c91569426b98d2187316a9253d9 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Wed, 9 Dec 2015 20:59:29 +0100 Subject: [PATCH] rework everything; add emacs output mode --- bin/php-sqllint | 88 ++++----------- src/phpsqllint/Autoloader.php | 57 ++++++++++ src/phpsqllint/Cli.php | 178 ++++++++++++++++++++++++++++++ src/phpsqllint/Renderer.php | 54 +++++++++ src/phpsqllint/Renderer/Emacs.php | 70 ++++++++++++ src/phpsqllint/Renderer/Text.php | 84 ++++++++++++++ 6 files changed, 466 insertions(+), 65 deletions(-) create mode 100644 src/phpsqllint/Autoloader.php create mode 100644 src/phpsqllint/Cli.php create mode 100644 src/phpsqllint/Renderer.php create mode 100644 src/phpsqllint/Renderer/Emacs.php create mode 100644 src/phpsqllint/Renderer/Text.php diff --git a/bin/php-sqllint b/bin/php-sqllint index 69372a0..f746813 100755 --- a/bin/php-sqllint +++ b/bin/php-sqllint @@ -1,68 +1,26 @@ #!/usr/bin/env php errors) == 0) { - echo "No syntax errors detected\n"; - exit(0); -} - -$lines = array(1 => 0); -$pos = -1; -$line = 1; -while (false !== $pos = strpos($sql, "\n", ++$pos)) { - $lines[++$line] = $pos; -} - -echo "Syntax errors found\n"; -foreach ($parser->errors as $error) { - reset($lines); - $line = 1; - while (next($lines) && $error->token->position >= current($lines)) { - ++$line; - } - $col = $error->token->position - $lines[$line]; - - /** @var SqlParser\Exceptions\ParserException $error) */ - echo 'Line ' . $line - . ' col ' . $col - //FIXME: ->token or ->value? - . ' at "' . niceToken($error->token->token) . '":' - . ' ' . $error->getMessage() - . "\n"; - //var_dump($error->token); -} - -function niceToken($str) -{ - return str_replace( - ["\n", "\r", "\t"], - ['\n', '\r', '\t'], - $str - ); -} +/** + * SQL linter (syntax checker) written in PHP + * + * PHP version 5 + * + * @category Tools + * @package PHP-SQLlint + * @author Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +namespace phpsqllint; + +if (file_exists(__DIR__ . '/../vendor/autoload.php')) { + include_once __DIR__ . '/../vendor/autoload.php'; +} +if (file_exists(__DIR__ . '/../src/phpsqllint/Autoloader.php')) { + include_once __DIR__ . '/../src/phpsqllint/Autoloader.php'; + Autoloader::register(); +} + +$cli = new Cli(); +$cli->run(); ?> \ No newline at end of file diff --git a/src/phpsqllint/Autoloader.php b/src/phpsqllint/Autoloader.php new file mode 100644 index 0000000..6811b82 --- /dev/null +++ b/src/phpsqllint/Autoloader.php @@ -0,0 +1,57 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +namespace phpsqllint; + +/** + * Class autoloader, PSR-0 compliant. + * + * @category Tools + * @package PHP-SQLlint + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/php-sqllint.htm + */ +class Autoloader +{ + /** + * Load the given class + * + * @param string $class Class name + * + * @return void + */ + public function load($class) + { + $file = strtr($class, '_\\', '//') . '.php'; + if (stream_resolve_include_path($file)) { + include $file; + } + } + + /** + * Register this autoloader + * + * @return void + */ + public static function register() + { + set_include_path( + get_include_path() . PATH_SEPARATOR . __DIR__ . '/../' + ); + spl_autoload_register(array(new self(), 'load')); + } +} +?> \ No newline at end of file diff --git a/src/phpsqllint/Cli.php b/src/phpsqllint/Cli.php new file mode 100644 index 0000000..03ee710 --- /dev/null +++ b/src/phpsqllint/Cli.php @@ -0,0 +1,178 @@ + + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +namespace phpsqllint; +use SqlParser\Parser; + +require_once 'Console/CommandLine.php'; + +/** + * Command line interface + * + * @category Tools + * @package PHP-SQLlint + * @author Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://www.emacswiki.org/emacs/CreatingYourOwnCompileErrorRegexp + */ +class Cli +{ + protected $renderer; + + /** + * Start processing. + * + * @return void + */ + public function run() + { + try { + $parser = $this->loadParameters(); + $files = $this->parseParameters($parser); + + $allfine = true; + foreach ($files as $filename) { + $allfine &= $this->checkFile($filename); + } + + if ($allfine == true) { + exit(0); + } else { + exit(10); + } + } catch (\Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); + } + } + + /** + * Check a .sql file for syntax errors + * + * @param string $filename File path + * + * @return boolean True if there were no errors, false if there were some + */ + public function checkFile($filename) + { + $this->renderer->startRendering($filename); + + $sql = file_get_contents($filename); + if (trim($sql) == '') { + $this->renderer->displayError('SQL file empty', '', 0, 0); + return false; + } + + $parser = new \SqlParser\Parser($sql); + if (count($parser->errors) == 0) { + $this->renderer->finishOk(); + return true; + } + + $lines = array(1 => 0); + $pos = -1; + $line = 1; + while (false !== $pos = strpos($sql, "\n", ++$pos)) { + $lines[++$line] = $pos; + } + + foreach ($parser->errors as $error) { + /* @var SqlParser\Exceptions\ParserException $error) */ + reset($lines); + $line = 1; + while (next($lines) && $error->token->position >= current($lines)) { + ++$line; + } + $col = $error->token->position - $lines[$line]; + + $this->renderer->displayError( + $error->getMessage(), + //FIXME: ->token or ->value? + $error->token->token, + $line, + $col + ); + } + + return false; + } + + /** + * Load parameters for the CLI option parser. + * + * @return \Console_CommandLine CLI option parser + */ + protected function loadParameters() + { + $parser = new \Console_CommandLine(); + $parser->description = 'php-sqllint'; + $parser->version = '0.0.1'; + + $parser->addOption( + 'renderer', + array( + 'short_name' => '-r', + 'long_name' => '--renderer', + 'description' => 'Output mode', + 'action' => 'StoreString', + 'choices' => array( + 'emacs', + 'text', + ), + 'default' => 'text', + 'add_list_option' => true, + ) + ); + + $parser->addArgument( + 'sql_files', + array( + 'multiple' => true + ) + ); + + return $parser; + } + + /** + * Let the CLI option parser parse the options. + * + * @param object $parser Option parser + * + * @return array Array of file names + */ + protected function parseParameters(\Console_CommandLine $parser) + { + try { + $result = $parser->parse(); + + $rendClass = '\\phpsqllint\\Renderer_' + . ucfirst($result->options['renderer']); + $this->renderer = new $rendClass(); + + foreach ($result->args['sql_files'] as $filename) { + if (!file_exists($filename)) { + throw new \Exception('File does not exist: ' . $filename); + } + if (!is_file($filename)) { + throw new \Exception('Not a file: ' . $filename); + } + } + + return $result->args['sql_files']; + } catch (\Exception $exc) { + $parser->displayError($exc->getMessage()); + } + } + +} +?> diff --git a/src/phpsqllint/Renderer.php b/src/phpsqllint/Renderer.php new file mode 100644 index 0000000..5b68ee1 --- /dev/null +++ b/src/phpsqllint/Renderer.php @@ -0,0 +1,54 @@ + + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +namespace phpsqllint; + +/** + * What every renderer has to implement + * + * @category Tools + * @package PHP-SQLlint + * @author Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://www.emacswiki.org/emacs/CreatingYourOwnCompileErrorRegexp + */ +interface Renderer +{ + /** + * Begin syntax check output rendering + * + * @param string $filename Path to the SQL file + * + * @return void + */ + public function startRendering($filename); + + /** + * Output errors in GNU style; see emacs compilation.txt + * + * @param string $msg Error message + * @param string $token Character which caused the error + * @param integer $line Line at which the error occured + * @param integer $col Column at which the error occured + * + * @return void + */ + public function displayError($msg, $token, $line, $col); + + /** + * Finish syntax check output rendering; no syntax errors found + * + * @return void + */ + public function finishOk(); +} +?> diff --git a/src/phpsqllint/Renderer/Emacs.php b/src/phpsqllint/Renderer/Emacs.php new file mode 100644 index 0000000..3a667c7 --- /dev/null +++ b/src/phpsqllint/Renderer/Emacs.php @@ -0,0 +1,70 @@ + + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +namespace phpsqllint; + +/** + * Output for emacs' compilation mode + * + * @category Tools + * @package PHP-SQLlint + * @author Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://www.emacswiki.org/emacs/CreatingYourOwnCompileErrorRegexp + */ +class Renderer_Emacs implements Renderer +{ + protected $filename; + + /** + * Begin syntax check output rendering + * + * @param string $filename Path to the SQL file + * + * @return void + */ + public function startRendering($filename) + { + $this->filename = $filename; + } + + /** + * Output errors in GNU style; see emacs compilation.txt + * + * @param string $msg Error message + * @param string $token Character which caused the error + * @param integer $line Line at which the error occured + * @param integer $col Column at which the error occured + * + * @return void + */ + public function displayError($msg, $token, $line, $col) + { + echo $this->filename + . ':' . $line + . '.' . $col + . ':Error:' + . ' '. $msg + . "\n"; + } + + /** + * Finish syntax check output rendering; no syntax errors found + * + * @return void + */ + public function finishOk() + { + //do nothing + } +} +?> diff --git a/src/phpsqllint/Renderer/Text.php b/src/phpsqllint/Renderer/Text.php new file mode 100644 index 0000000..c36e768 --- /dev/null +++ b/src/phpsqllint/Renderer/Text.php @@ -0,0 +1,84 @@ + + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +namespace phpsqllint; + +/** + * Textual output, easily readable by humans. + * + * @category Tools + * @package PHP-SQLlint + * @author Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://www.emacswiki.org/emacs/CreatingYourOwnCompileErrorRegexp + */ +class Renderer_Text implements Renderer +{ + /** + * Begin syntax check output rendering + * + * @param string $filename Path to the SQL file + * + * @return void + */ + public function startRendering($filename) + { + echo "Checking SQL syntax of " . $filename . "\n"; + } + + /** + * Show the error to the user. + * + * @param string $msg Error message + * @param string $token Character which caused the error + * @param integer $line Line at which the error occured + * @param integer $col Column at which the error occured + * + * @return void + */ + public function displayError($msg, $token, $line, $col) + { + echo ' Line ' . $line + . ', col ' . $col + . ' at "' . $this->niceToken($token) . '":' + . ' ' . $msg + . "\n"; + } + + /** + * Finish syntax check output rendering; no syntax errors found + * + * @return void + */ + public function finishOk() + { + echo " OK\n"; + } + + /** + * Convert the token string to a readable one, especially special + * characters like newline and tabs + * + * @param string $str String with possibly special characters + * + * @return string Escaped string + */ + protected function niceToken($str) + { + return str_replace( + ["\n", "\r", "\t"], + ['\n', '\r', '\t'], + $str + ); + } +} +?> -- 2.30.2