Update to SQL Parser 4.1.9
[php-sqllint.git] / src / phpsqllint / Cli.php
1 <?php
2 /**
3  * Part of php-sqllint
4  *
5  * PHP version 5
6  *
7  * @category Tools
8  * @package  PHP-SQLlint
9  * @author   Christian Weiske <cweiske@cweiske.de>
10  * @license  http://www.gnu.org/licenses/agpl.html GNU AGPL v3
11  * @link     http://cweiske.de/php-sqllint.htm
12  */
13 namespace phpsqllint;
14 use PhpMyAdmin\SqlParser\Parser;
15
16 require_once 'Console/CommandLine.php';
17
18 /**
19  * Command line interface
20  *
21  * @category Tools
22  * @package  PHP-SQLlint
23  * @author   Christian Weiske <cweiske@cweiske.de>
24  * @license  http://www.gnu.org/licenses/agpl.html GNU AGPL v3
25  * @link     http://www.emacswiki.org/emacs/CreatingYourOwnCompileErrorRegexp
26  */
27 class Cli
28 {
29     protected $renderer;
30
31     protected $format = false;
32
33     /**
34      * What syntax highlighting mode should be used
35      *
36      * none, ansi, html
37      */
38     protected $highlight = 'none';
39
40
41     /**
42      * Start processing.
43      *
44      * @return void
45      */
46     public function run()
47     {
48         try {
49             $parser = $this->loadOptionParser();
50             $files  = $this->parseParameters($parser);
51
52             $allfine = true;
53             foreach ($files as $filename) {
54                 if ($this->format) {
55                     $allfine &= $this->formatFile($filename);
56                 } else {
57                     $allfine &= $this->checkFile($filename);
58                 }
59             }
60
61             if ($allfine == true) {
62                 exit(0);
63             } else {
64                 exit(10);
65             }
66         } catch (\Exception $e) {
67             echo 'Error: ' . $e->getMessage() . "\n";
68             exit(1);
69         }
70     }
71
72     /**
73      * Check a .sql file for syntax errors
74      *
75      * @param string $filename File path
76      *
77      * @return boolean True if there were no errors, false if there were some
78      */
79     public function checkFile($filename)
80     {
81         $this->renderer->startRendering($filename);
82         $sql = $this->loadSql($filename);
83         if ($sql === false) {
84             return false;
85         }
86
87         $parser = new \PhpMyAdmin\SqlParser\Parser($sql);
88         if (count($parser->errors) == 0) {
89             $this->renderer->finishOk();
90             return true;
91         }
92
93         $lines = array(1 => 0);
94         $pos = -1;
95         $line = 1;
96         while (false !== $pos = strpos($sql, "\n", ++$pos)) {
97             $lines[++$line] = $pos;
98         }
99
100         foreach ($parser->errors as $error) {
101             /* @var PhpMyAdmin\SqlParser\Exceptions\ParserException $error) */
102             reset($lines);
103             $line = 1;
104             while (next($lines) && $error->token->position >= current($lines)) {
105                 ++$line;
106             }
107             $col = $error->token->position - $lines[$line];
108
109             $this->renderer->displayError(
110                 $error->getMessage(),
111                 //FIXME: ->token or ->value?
112                 $error->token->token,
113                 $line,
114                 $col
115             );
116         }
117
118         return false;
119     }
120
121     /**
122      * Reformat the given file
123      */
124     protected function formatFile($filename)
125     {
126         $this->renderer->startRendering($filename);
127         $sql = $this->loadSql($filename);
128         if ($sql === false) {
129             return false;
130         }
131
132         $typeMap = array(
133             'none' => 'text',
134             'ansi' => 'cli',
135             'html' => 'html',
136         );
137         $options = array(
138             'type' => $typeMap[$this->highlight],
139         );
140         echo \PhpMyAdmin\SqlParser\Utils\Formatter::format($sql, $options) . "\n";
141     }
142
143     protected function loadSql($filename)
144     {
145         if ($filename == '-') {
146             $sql = file_get_contents('php://stdin');
147         } else {
148             $sql = file_get_contents($filename);
149         }
150         if (trim($sql) == '') {
151             $this->renderer->displayError('SQL file empty', '', 0, 0);
152             return false;
153         }
154         return $sql;
155     }
156
157     /**
158      * Load parameters for the CLI option parser.
159      *
160      * @return \Console_CommandLine CLI option parser
161      */
162     protected function loadOptionParser()
163     {
164         $parser = new \Console_CommandLine();
165         $parser->description = 'php-sqllint';
166         $parser->version = 'dev';
167         $parser->avoid_reading_stdin = true;
168
169         $versionFile = __DIR__ . '/../../VERSION';
170         if (file_exists($versionFile)) {
171             $parser->version = trim(file_get_contents($versionFile));
172         }
173
174         $parser->addOption(
175             'format',
176             array(
177                 'short_name'  => '-f',
178                 'long_name'   => '--format',
179                 'description' => 'Reformat SQL instead of checking',
180                 'action'      => 'StoreTrue',
181                 'default'     => false,
182             )
183         );
184         $parser->addOption(
185             'highlight',
186             array(
187                 'short_name'  => '-h',
188                 'long_name'   => '--highlight',
189                 'description' => 'Highlighting mode (when using --format)',
190                 'action'      => 'StoreString',
191                 'choices'     => array(
192                     'none',
193                     'ansi',
194                     'html',
195                     'auto',
196                 ),
197                 'default' => 'auto',
198                 'add_list_option' => true,
199             )
200         );
201         $parser->addOption(
202             'renderer',
203             array(
204                 'short_name'  => '-r',
205                 'long_name'   => '--renderer',
206                 'description' => 'Output mode',
207                 'action'      => 'StoreString',
208                 'choices'     => array(
209                     'emacs',
210                     'text',
211                 ),
212                 'default'     => 'text',
213                 'add_list_option' => true,
214             )
215         );
216
217         $parser->addArgument(
218             'sql_files',
219             array(
220                 'description' => 'SQL files, "-" for stdin',
221                 'multiple'    => true
222             )
223         );
224
225         return $parser;
226     }
227
228     /**
229      * Let the CLI option parser parse the options.
230      *
231      * @param object $parser Option parser
232      *
233      * @return array Array of file names
234      */
235     protected function parseParameters(\Console_CommandLine $parser)
236     {
237         try {
238             $result = $parser->parse();
239
240             $rendClass = '\\phpsqllint\\Renderer_'
241                 . ucfirst($result->options['renderer']);
242             $this->renderer = new $rendClass();
243
244             $this->format = $result->options['format'];
245
246             $this->highlight = $result->options['highlight'];
247             if ($this->highlight == 'auto') {
248                 if (php_sapi_name() == 'cli') {
249                     //default coloring to enabled, except
250                     // when piping | to another tool
251                     $this->highlight = 'ansi';
252                     if (function_exists('posix_isatty')
253                         && !posix_isatty(STDOUT)
254                     ) {
255                         $this->highlight = 'none';
256                     }
257                 } else {
258                     //no idea where we are, so do not highlight
259                     $this->highlight = 'none';
260                 }
261             }
262
263             foreach ($result->args['sql_files'] as $filename) {
264                 if ($filename == '-') {
265                     continue;
266                 }
267                 if (!file_exists($filename)) {
268                     throw new \Exception('File does not exist: ' . $filename);
269                 }
270                 if (!is_file($filename)) {
271                     throw new \Exception('Not a file: ' . $filename);
272                 }
273             }
274
275             return $result->args['sql_files'];
276         } catch (\Exception $exc) {
277             $parser->displayError($exc->getMessage());
278         }
279     }
280
281 }
282 ?>