From 05ea79929aa099d25b543f4a86db721f5f7e700e Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Mon, 18 Nov 2019 12:11:56 +0300 Subject: Add phpsqllint for check sql queries --- tools/php-sqllint/bin/php-sqllint | 31 +++ tools/php-sqllint/build.xml | 103 ++++++++ tools/php-sqllint/composer.json | 22 ++ tools/php-sqllint/composer.lock | 186 ++++++++++++++ tools/php-sqllint/src/phpsqllint/Autoloader.php | 57 +++++ tools/php-sqllint/src/phpsqllint/Cli.php | 280 +++++++++++++++++++++ tools/php-sqllint/src/phpsqllint/Renderer.php | 54 ++++ .../php-sqllint/src/phpsqllint/Renderer/Emacs.php | 70 ++++++ tools/php-sqllint/src/phpsqllint/Renderer/Text.php | 102 ++++++++ tools/php-sqllint/src/stub-phar.php | 18 ++ 10 files changed, 923 insertions(+) create mode 100755 tools/php-sqllint/bin/php-sqllint create mode 100644 tools/php-sqllint/build.xml create mode 100644 tools/php-sqllint/composer.json create mode 100644 tools/php-sqllint/composer.lock create mode 100644 tools/php-sqllint/src/phpsqllint/Autoloader.php create mode 100644 tools/php-sqllint/src/phpsqllint/Cli.php create mode 100644 tools/php-sqllint/src/phpsqllint/Renderer.php create mode 100644 tools/php-sqllint/src/phpsqllint/Renderer/Emacs.php create mode 100644 tools/php-sqllint/src/phpsqllint/Renderer/Text.php create mode 100644 tools/php-sqllint/src/stub-phar.php (limited to 'tools/php-sqllint') diff --git a/tools/php-sqllint/bin/php-sqllint b/tools/php-sqllint/bin/php-sqllint new file mode 100755 index 000000000..fd766bc07 --- /dev/null +++ b/tools/php-sqllint/bin/php-sqllint @@ -0,0 +1,31 @@ +#!/usr/bin/env php + + * @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')) { + //local git checkout + include_once __DIR__ . '/../vendor/autoload.php'; +} else if (file_exists('vendor/autoload.php')) { + //dependency composer installation + include_once '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/tools/php-sqllint/build.xml b/tools/php-sqllint/build.xml new file mode 100644 index 000000000..e1f45f0a4 --- /dev/null +++ b/tools/php-sqllint/build.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/php-sqllint/composer.json b/tools/php-sqllint/composer.json new file mode 100644 index 000000000..bde2da14d --- /dev/null +++ b/tools/php-sqllint/composer.json @@ -0,0 +1,22 @@ +{ + "name": "cweiske/php-sqllint", + "description": "Command line tool to validate (syntax check) SQL files", + "type": "project", + "bin": ["bin/php-sqllint"], + "require": { + "phpmyadmin/sql-parser": "^4.1.1", + "pear/console_commandline": "^1.2" + }, + "homepage": "http://cweiske.de/php-sqllint.htm", + "license": "AGPL-3.0", + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://cweiske.de/" + } + ], + "support": { + "issues": "https://github.com/cweiske/php-sqllint/issues" + } +} diff --git a/tools/php-sqllint/composer.lock b/tools/php-sqllint/composer.lock new file mode 100644 index 000000000..a30dda3bf --- /dev/null +++ b/tools/php-sqllint/composer.lock @@ -0,0 +1,186 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "642c0759e68495342f0e4ec0a8b45575", + "content-hash": "e52fa298a0c5ce2ff75067ab99b00ce7", + "packages": [ + { + "name": "pear/console_commandline", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/pear/Console_CommandLine.git", + "reference": "7a8afa50bdc8dbfdc0cf394f1101106e8b8f8e67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/Console_CommandLine/zipball/7a8afa50bdc8dbfdc0cf394f1101106e8b8f8e67", + "reference": "7a8afa50bdc8dbfdc0cf394f1101106e8b8f8e67", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "pear/pear_exception": "^1.0.0", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Console": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com" + }, + { + "name": "David Jean Louis", + "email": "izimobil@gmail.com" + } + ], + "description": "A full featured command line options and arguments parser.", + "homepage": "https://github.com/pear/Console_CommandLine", + "keywords": [ + "console" + ], + "time": "2016-07-14 06:00:57" + }, + { + "name": "pear/pear_exception", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/pear/PEAR_Exception.git", + "reference": "8c18719fdae000b690e3912be401c76e406dd13b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/8c18719fdae000b690e3912be401c76e406dd13b", + "reference": "8c18719fdae000b690e3912be401c76e406dd13b", + "shasum": "" + }, + "require": { + "php": ">=4.4.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "type": "class", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "PEAR": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "." + ], + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Helgi Thormar", + "email": "dufuz@php.net" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net" + } + ], + "description": "The PEAR Exception base class.", + "homepage": "https://github.com/pear/PEAR_Exception", + "keywords": [ + "exception" + ], + "time": "2015-02-10 20:07:52" + }, + { + "name": "phpmyadmin/sql-parser", + "version": "v4.1.9", + "source": { + "type": "git", + "url": "https://github.com/phpmyadmin/sql-parser.git", + "reference": "deac47217144b21056271674533b289239e1b279" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/deac47217144b21056271674533b289239e1b279", + "reference": "deac47217144b21056271674533b289239e1b279", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "conflict": { + "phpmyadmin/motranslator": "<3.0" + }, + "require-dev": { + "phpunit/php-code-coverage": "*", + "phpunit/phpunit": "~4.8 || ~5.7" + }, + "suggest": { + "phpmyadmin/motranslator": "Translate messages to your favorite locale" + }, + "bin": [ + "bin/highlight-query", + "bin/lint-query" + ], + "type": "library", + "autoload": { + "psr-4": { + "PhpMyAdmin\\SqlParser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "The phpMyAdmin Team", + "email": "developers@phpmyadmin.net", + "homepage": "https://www.phpmyadmin.net/team/" + } + ], + "description": "A validating SQL lexer and parser with a focus on MySQL dialect.", + "homepage": "https://github.com/phpmyadmin/sql-parser", + "keywords": [ + "analysis", + "lexer", + "parser", + "sql" + ], + "time": "2017-07-12 14:54:33" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/tools/php-sqllint/src/phpsqllint/Autoloader.php b/tools/php-sqllint/src/phpsqllint/Autoloader.php new file mode 100644 index 000000000..6811b82f1 --- /dev/null +++ b/tools/php-sqllint/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/tools/php-sqllint/src/phpsqllint/Cli.php b/tools/php-sqllint/src/phpsqllint/Cli.php new file mode 100644 index 000000000..1501815eb --- /dev/null +++ b/tools/php-sqllint/src/phpsqllint/Cli.php @@ -0,0 +1,280 @@ + + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +namespace phpsqllint; +use PhpMyAdmin\SqlParser\Parser; + +/** + * 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; + + protected $format = false; + + /** + * What syntax highlighting mode should be used + * + * none, ansi, html + */ + protected $highlight = 'none'; + + + /** + * Start processing. + * + * @return void + */ + public function run() + { + try { + $parser = $this->loadOptionParser(); + $files = $this->parseParameters($parser); + + $allfine = true; + foreach ($files as $filename) { + if ($this->format) { + $allfine &= $this->formatFile($filename); + } else { + $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 = $this->loadSql($filename); + if ($sql === false) { + return false; + } + + $parser = new \PhpMyAdmin\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 PhpMyAdmin\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; + } + + /** + * Reformat the given file + */ + protected function formatFile($filename) + { + $this->renderer->startRendering($filename); + $sql = $this->loadSql($filename); + if ($sql === false) { + return false; + } + + $typeMap = array( + 'none' => 'text', + 'ansi' => 'cli', + 'html' => 'html', + ); + $options = array( + 'type' => $typeMap[$this->highlight], + ); + echo \PhpMyAdmin\SqlParser\Utils\Formatter::format($sql, $options) . "\n"; + } + + protected function loadSql($filename) + { + if ($filename == '-') { + $sql = file_get_contents('php://stdin'); + } else { + $sql = file_get_contents($filename); + } + if (trim($sql) == '') { + $this->renderer->displayError('SQL file empty', '', 0, 0); + return false; + } + return $sql; + } + + /** + * Load parameters for the CLI option parser. + * + * @return \Console_CommandLine CLI option parser + */ + protected function loadOptionParser() + { + $parser = new \Console_CommandLine(); + $parser->description = 'php-sqllint'; + $parser->version = 'dev'; + $parser->avoid_reading_stdin = true; + + $versionFile = __DIR__ . '/../../VERSION'; + if (file_exists($versionFile)) { + $parser->version = trim(file_get_contents($versionFile)); + } + + $parser->addOption( + 'format', + array( + 'short_name' => '-f', + 'long_name' => '--format', + 'description' => 'Reformat SQL instead of checking', + 'action' => 'StoreTrue', + 'default' => false, + ) + ); + $parser->addOption( + 'highlight', + array( + 'short_name' => '-h', + 'long_name' => '--highlight', + 'description' => 'Highlighting mode (when using --format)', + 'action' => 'StoreString', + 'choices' => array( + 'none', + 'ansi', + 'html', + 'auto', + ), + 'default' => 'auto', + 'add_list_option' => true, + ) + ); + $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( + 'description' => 'SQL files, "-" for stdin', + '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(); + + $this->format = $result->options['format']; + + $this->highlight = $result->options['highlight']; + if ($this->highlight == 'auto') { + if (php_sapi_name() == 'cli') { + //default coloring to enabled, except + // when piping | to another tool + $this->highlight = 'ansi'; + if (function_exists('posix_isatty') + && !posix_isatty(STDOUT) + ) { + $this->highlight = 'none'; + } + } else { + //no idea where we are, so do not highlight + $this->highlight = 'none'; + } + } + + foreach ($result->args['sql_files'] as $filename) { + if ($filename == '-') { + continue; + } + 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/tools/php-sqllint/src/phpsqllint/Renderer.php b/tools/php-sqllint/src/phpsqllint/Renderer.php new file mode 100644 index 000000000..5b68ee11a --- /dev/null +++ b/tools/php-sqllint/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/tools/php-sqllint/src/phpsqllint/Renderer/Emacs.php b/tools/php-sqllint/src/phpsqllint/Renderer/Emacs.php new file mode 100644 index 000000000..3a667c7f6 --- /dev/null +++ b/tools/php-sqllint/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/tools/php-sqllint/src/phpsqllint/Renderer/Text.php b/tools/php-sqllint/src/phpsqllint/Renderer/Text.php new file mode 100644 index 000000000..44e7ecbd4 --- /dev/null +++ b/tools/php-sqllint/src/phpsqllint/Renderer/Text.php @@ -0,0 +1,102 @@ + + * @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 +{ + protected $fileshown = false; + protected $filename = null; + + /** + * Begin syntax check output rendering + * + * @param string $filename Path to the SQL file + * + * @return void + */ + public function startRendering($filename) + { + $this->filename = $filename; + $this->fileshown = false; + } + + + protected function showFile() + { + if ($this->fileshown) { + return; + } + + echo "Checking SQL syntax of " . $this->filename . "\n"; + $this->fileshown = true; + } + + /** + * 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) + { + $this->showFile(); + 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() + { + if ($this->fileshown) { + 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 + ); + } +} +?> diff --git a/tools/php-sqllint/src/stub-phar.php b/tools/php-sqllint/src/stub-phar.php new file mode 100644 index 000000000..5c8629e23 --- /dev/null +++ b/tools/php-sqllint/src/stub-phar.php @@ -0,0 +1,18 @@ +#!/usr/bin/env php + + * @copyright 2015 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/php-sqllint.htm + */ +Phar::mapPhar('php-sqllint.phar'); +require 'phar://php-sqllint.phar/bin/phar-php-sqllint.php'; +__HALT_COMPILER(); +?> -- cgit v1.2.3-70-g09d2