Passed
Push — master ( e55fad...9226b6 )
by Maurício
03:38
created

CLI::run()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
c 1
b 0
f 0
nc 5
nop 0
dl 0
loc 22
ccs 0
cts 13
cp 0
crap 30
rs 9.5555
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\SqlParser\Utils;
6
7
use PhpMyAdmin\SqlParser\Context;
8
use PhpMyAdmin\SqlParser\Lexer;
9
use PhpMyAdmin\SqlParser\Parser;
10
11
use function getopt;
12
use function implode;
13
use function in_array;
14
use function rtrim;
15
use function stream_get_contents;
16
use function stream_select;
17
use function var_export;
18
19
use const STDIN;
20
21
/**
22
 * CLI interface.
23
 */
24
class CLI
25
{
26 2
    public function __construct()
27
    {
28 2
        Context::load();
29
    }
30
31
    public function run(): int
32
    {
33
        $params = $this->getopt('', ['lint', 'highlight', 'tokenize']);
34
        if ($params !== false) {
35
            if (isset($params['lint'])) {
36
                return $this->runLint(false);
37
            }
38
39
            if (isset($params['highlight'])) {
40
                return $this->runHighlight(false);
41
            }
42
43
            if (isset($params['tokenize'])) {
44
                return $this->runTokenize(false);
45
            }
46
        }
47
48
        $this->usageLint(false);
49
        $this->usageHighlight(false);
50
        $this->usageTokenize(false);
51
52
        return 1;
53
    }
54
55
    /**
56
     * @param string[]|false[] $params
57
     * @param string[]         $longopts
58
     */
59 62
    public function mergeLongOpts(array &$params, array &$longopts): void
60
    {
61 62
        foreach ($longopts as $value) {
62 62
            $value = rtrim($value, ':');
63 62
            if (! isset($params[$value])) {
64 62
                continue;
65
            }
66
67 6
            $params[$value[0]] = $params[$value];
68
        }
69
    }
70
71 8
    public function usageHighlight(bool $isStandalone = true): void
72
    {
73 8
        $command = $isStandalone ? 'highlight-query' : 'sql-parser --highlight';
74
75 8
        echo 'Usage: ' . $command . ' --query SQL [--format html|cli|text] [--ansi]' . "\n";
76 8
        echo '       cat file.sql | ' . $command . "\n";
77
    }
78
79
    /**
80
     * @param string[] $long
81
     *
82
     * @return string[]|false[]|false
83
     */
84 2
    public function getopt(string $opt, array $long): array|false
85
    {
86 2
        return getopt($opt, $long);
87
    }
88
89
    /** @return string[]|false[]|false */
90 30
    public function parseHighlight(): array|false
91
    {
92 30
        $longopts = [
93 30
            'help',
94 30
            'query:',
95 30
            'format:',
96 30
            'ansi',
97 30
        ];
98 30
        $params = $this->getopt('hq:f:a', $longopts);
99 30
        if ($params === false) {
100 4
            return false;
101
        }
102
103 26
        $this->mergeLongOpts($params, $longopts);
104 26
        if (! isset($params['f'])) {
105 14
            $params['f'] = 'cli';
106
        }
107
108 26
        if (! in_array($params['f'], ['html', 'cli', 'text'])) {
109 4
            echo "ERROR: Invalid value for format!\n";
110
111 4
            return false;
112
        }
113
114 22
        return $params;
115
    }
116
117 30
    public function runHighlight(bool $isStandalone = true): int
118
    {
119 30
        $params = $this->parseHighlight();
120 30
        if ($params === false) {
0 ignored issues
show
introduced by
The condition $params === false is always true.
Loading history...
121 8
            return 1;
122
        }
123
124 22
        if (isset($params['h'])) {
125 4
            $this->usageHighlight($isStandalone);
126
127 4
            return 0;
128
        }
129
130 18
        if (! isset($params['q'])) {
131 10
            $stdIn = $this->readStdin();
132
133 10
            if ($stdIn) {
134 6
                $params['q'] = $stdIn;
135
            }
136
        }
137
138 18
        if (isset($params['a'])) {
139
            Context::setMode(Context::SQL_MODE_ANSI_QUOTES);
140
        }
141
142 18
        if (isset($params['q'])) {
143 14
            echo Formatter::format(
144 14
                $params['q'],
145 14
                ['type' => $params['f']],
146 14
            );
147 14
            echo "\n";
148
149 14
            return 0;
150
        }
151
152 4
        echo "ERROR: Missing parameters!\n";
153 4
        $this->usageHighlight($isStandalone);
154
155 4
        return 1;
156
    }
157
158 8
    public function usageLint(bool $isStandalone = true): void
159
    {
160 8
        $command = $isStandalone ? 'lint-query' : 'sql-parser --lint';
161
162 8
        echo 'Usage: ' . $command . ' --query SQL [--ansi]' . "\n";
163 8
        echo '       cat file.sql | ' . $command . "\n";
164
    }
165
166
    /** @return string[]|false[]|false */
167 26
    public function parseLint(): array|false
168
    {
169 26
        $longopts = [
170 26
            'help',
171 26
            'query:',
172 26
            'context:',
173 26
            'ansi',
174 26
        ];
175 26
        $params = $this->getopt('hq:c:a', $longopts);
176 26
        if ($params === false) {
177 4
            return false;
178
        }
179
180 22
        $this->mergeLongOpts($params, $longopts);
181
182 22
        return $params;
183
    }
184
185 26
    public function runLint(bool $isStandalone = true): int
186
    {
187 26
        $params = $this->parseLint();
188 26
        if ($params === false) {
0 ignored issues
show
introduced by
The condition $params === false is always true.
Loading history...
189 4
            return 1;
190
        }
191
192 22
        if (isset($params['h'])) {
193 4
            $this->usageLint($isStandalone);
194
195 4
            return 0;
196
        }
197
198 18
        if (isset($params['c'])) {
199 4
            Context::load($params['c']);
200
        }
201
202 18
        if (! isset($params['q'])) {
203 10
            $stdIn = $this->readStdin();
204
205 10
            if ($stdIn) {
206 6
                $params['q'] = $stdIn;
207
            }
208
        }
209
210 18
        if (isset($params['a'])) {
211
            Context::setMode(Context::SQL_MODE_ANSI_QUOTES);
212
        }
213
214 18
        if (isset($params['q'])) {
215 14
            $lexer = new Lexer($params['q'], false);
216 14
            $parser = new Parser($lexer->list);
217 14
            $errors = Error::get([$lexer, $parser]);
218 14
            if ($errors === []) {
219 6
                return 0;
220
            }
221
222 8
            $output = Error::format($errors);
223 8
            echo implode("\n", $output);
224 8
            echo "\n";
225
226 8
            return 10;
227
        }
228
229 4
        echo "ERROR: Missing parameters!\n";
230 4
        $this->usageLint($isStandalone);
231
232 4
        return 1;
233
    }
234
235 8
    public function usageTokenize(bool $isStandalone = true): void
236
    {
237 8
        $command = $isStandalone ? 'tokenize-query' : 'sql-parser --tokenize';
238
239 8
        echo 'Usage: ' . $command . ' --query SQL [--ansi]' . "\n";
240 8
        echo '       cat file.sql | ' . $command . "\n";
241
    }
242
243
    /** @return string[]|false[]|false */
244 18
    public function parseTokenize(): array|false
245
    {
246 18
        $longopts = [
247 18
            'help',
248 18
            'query:',
249 18
            'ansi',
250 18
        ];
251 18
        $params = $this->getopt('hq:a', $longopts);
252 18
        if ($params === false) {
253 4
            return false;
254
        }
255
256 14
        $this->mergeLongOpts($params, $longopts);
257
258 14
        return $params;
259
    }
260
261 18
    public function runTokenize(bool $isStandalone = true): int
262
    {
263 18
        $params = $this->parseTokenize();
264 18
        if ($params === false) {
0 ignored issues
show
introduced by
The condition $params === false is always true.
Loading history...
265 4
            return 1;
266
        }
267
268 14
        if (isset($params['h'])) {
269 4
            $this->usageTokenize($isStandalone);
270
271 4
            return 0;
272
        }
273
274 10
        if (! isset($params['q'])) {
275 6
            $stdIn = $this->readStdin();
276
277 6
            if ($stdIn) {
278 2
                $params['q'] = $stdIn;
279
            }
280
        }
281
282 10
        if (isset($params['a'])) {
283
            Context::setMode(Context::SQL_MODE_ANSI_QUOTES);
284
        }
285
286 10
        if (isset($params['q'])) {
287 6
            $lexer = new Lexer($params['q'], false);
288 6
            foreach ($lexer->list->tokens as $idx => $token) {
289 6
                echo '[TOKEN ', $idx, "]\n";
290 6
                echo 'Type = ', $token->type->value, "\n";
291 6
                echo 'Flags = ', $token->flags, "\n";
292 6
                echo 'Value = ';
293 6
                var_export($token->value);
294 6
                echo "\n";
295 6
                echo 'Token = ';
296 6
                var_export($token->token);
297 6
                echo "\n";
298 6
                echo "\n";
299
            }
300
301 6
            return 0;
302
        }
303
304 4
        echo "ERROR: Missing parameters!\n";
305 4
        $this->usageTokenize($isStandalone);
306
307 4
        return 1;
308
    }
309
310 6
    public function readStdin(): string|false|null
311
    {
312 6
        $read = [STDIN];
313 6
        $write = [];
314 6
        $except = [];
315
316
        // Assume there's nothing to be read from STDIN.
317 6
        $stdin = null;
318
319
        // Try to read from STDIN.  Wait 0.2 second before timing out.
320 6
        $result = stream_select($read, $write, $except, 0, 2000);
321
322 6
        if ($result > 0) {
323 6
            $stdin = stream_get_contents(STDIN);
324
        }
325
326 6
        return $stdin;
327
    }
328
}
329