rework everything; add emacs output mode
authorChristian Weiske <cweiske@cweiske.de>
Wed, 9 Dec 2015 19:59:29 +0000 (20:59 +0100)
committerChristian Weiske <cweiske@cweiske.de>
Wed, 9 Dec 2015 19:59:29 +0000 (20:59 +0100)
bin/php-sqllint
src/phpsqllint/Autoloader.php [new file with mode: 0644]
src/phpsqllint/Cli.php [new file with mode: 0644]
src/phpsqllint/Renderer.php [new file with mode: 0644]
src/phpsqllint/Renderer/Emacs.php [new file with mode: 0644]
src/phpsqllint/Renderer/Text.php [new file with mode: 0644]

index 69372a01727f87c78a6111aa69e74390381ee522..f746813650c7a37e9ad99896fc834b19b7220dbd 100755 (executable)
@@ -1,68 +1,26 @@
 #!/usr/bin/env php
 <?php
-use SqlParser\Parser;
-
-require_once __DIR__ . '/../vendor/autoload.php';
-
-if ($argc < 2) {
-    echo "SQL file name missing\n";
-    exit(1);
-}
-$file = $argv[1];
-if ($file == '') {
-    echo "SQL file name empty\n";
-    exit(1);
-}
-if (!file_exists($file)) {
-    echo "SQL file does not exist\n";
-    exit(1);
-}
-
-$sql = file_get_contents($file);
-if (trim($sql) == '') {
-    echo "SQL file empty\n";
-    exit(1);
-}
-
-$parser = new Parser($sql);
-
-if (count($parser->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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..6811b82
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Part of php-sqllint
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   PHP-SQLlint
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..03ee710
--- /dev/null
@@ -0,0 +1,178 @@
+<?php
+/**
+ * Part of php-sqllint
+ *
+ * PHP version 5
+ *
+ * @category Tools
+ * @package  PHP-SQLlint
+ * @author   Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..5b68ee1
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Part of php-sqllint
+ *
+ * PHP version 5
+ *
+ * @category Tools
+ * @package  PHP-SQLlint
+ * @author   Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..3a667c7
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Part of php-sqllint
+ *
+ * PHP version 5
+ *
+ * @category Tools
+ * @package  PHP-SQLlint
+ * @author   Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..c36e768
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Part of php-sqllint
+ *
+ * PHP version 5
+ *
+ * @category Tools
+ * @package  PHP-SQLlint
+ * @author   Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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
+        );
+    }
+}
+?>