Passed
Push — master ( 21771a...749afb )
by Wilmer
18:31 queued 03:34
created

SqlTokenizer::isKeyword()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 138
Code Lines 130

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 130
nc 2
nop 2
dl 0
loc 138
rs 8
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Sqlite;
6
7
use function mb_strtoupper;
8
use function strtr;
9
10
/**
11
 * SqlTokenizer splits SQLite query into individual SQL tokens.
12
 *
13
 * It's used to obtain a `CHECK` constraint information from a `CREATE TABLE` SQL code.
14
 *
15
 * {@see http://www.sqlite.org/draft/tokenreq.html}
16
 * {@see https://sqlite.org/lang.html}
17
 */
18
final class SqlTokenizer extends BaseTokenizer
19
{
20
    /**
21
     * Returns whether there's a whitespace at the current offset.
22
     *
23
     * If this method returns `true`, it has to set the `$length` parameter to the length of the matched string.
24
     *
25
     * @param int|null $length length of the matched string.
26
     *
27
     * @return bool whether there's a whitespace at the current offset.
28
     */
29
    protected function isWhitespace(?int &$length): bool
30
    {
31
        static $whitespaces = [
32
            "\f" => true,
33
            "\n" => true,
34
            "\r" => true,
35
            "\t" => true,
36
            ' ' => true,
37
        ];
38
39
        $length = 1;
40
41
        return isset($whitespaces[$this->substring($length)]);
42
    }
43
44
    /**
45
     * Returns whether there's a commentary at the current offset.
46
     *
47
     * If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.
48
     *
49
     * @param int $length length of the matched string.
50
     *
51
     * @return bool whether there's a commentary at the current offset.
52
     */
53
    protected function isComment(int &$length): bool
54
    {
55
        static $comments = [
56
            '--' => true,
57
            '/*' => true,
58
        ];
59
60
        $length = 2;
61
62
        if (!isset($comments[$this->substring($length)])) {
63
            return false;
64
        }
65
66
        if ($this->substring($length) === '--') {
67
            $length = $this->indexAfter("\n") - $this->offset;
68
        } else {
69
            $length = $this->indexAfter('*/') - $this->offset;
70
        }
71
72
        return true;
73
    }
74
75
    /**
76
     * Returns whether there's an operator at the current offset.
77
     *
78
     * If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string. It may
79
     * also set `$content` to a string that will be used as a token content.
80
     *
81
     * @param int $length  length of the matched string.
82
     * @param string|null $content optional content instead of the matched string.
83
     *
84
     * @return bool whether there's an operator at the current offset.
85
     */
86
    protected function isOperator(int &$length, ?string &$content): bool
87
    {
88
        static $operators = [
89
            '!=',
90
            '%',
91
            '&',
92
            '(',
93
            ')',
94
            '*',
95
            '+',
96
            ',',
97
            '-',
98
            '.',
99
            '/',
100
            ';',
101
            '<',
102
            '<<',
103
            '<=',
104
            '<>',
105
            '=',
106
            '==',
107
            '>',
108
            '>=',
109
            '>>',
110
            '|',
111
            '||',
112
            '~',
113
        ];
114
115
        return $this->startsWithAnyLongest($operators, true, $length);
116
    }
117
118
    /**
119
     * Returns whether there's an identifier at the current offset.
120
     *
121
     * If this method returns `true`, it has to set the `$length` parameter to the length of the matched string. It may
122
     * also set `$content` to a string that will be used as a token content.
123
     *
124
     * @param int $length length of the matched string.
125
     * @param string|null $content optional content instead of the matched string.
126
     *
127
     * @return bool whether there's an identifier at the current offset.
128
     */
129
    protected function isIdentifier(int &$length, ?string &$content): bool
130
    {
131
        static $identifierDelimiters = [
132
            '"' => '"',
133
            '[' => ']',
134
            '`' => '`',
135
        ];
136
137
        if (!isset($identifierDelimiters[$this->substring(1)])) {
138
            return false;
139
        }
140
141
        $delimiter = $identifierDelimiters[$this->substring(1)];
142
        $offset = $this->offset;
143
144
        while (true) {
145
            $offset = $this->indexAfter($delimiter, $offset + 1);
146
            if ($delimiter === ']' || $this->substring(1, true, $offset) !== $delimiter) {
147
                break;
148
            }
149
        }
150
        $length = $offset - $this->offset;
151
        $content = $this->substring($length - 2, true, $this->offset + 1);
152
153
        if ($delimiter !== ']') {
154
            $content = strtr($content, ["$delimiter$delimiter" => $delimiter]);
155
        }
156
157
        return true;
158
    }
159
160
    /**
161
     * Returns whether there's a string literal at the current offset.
162
     *
163
     * If this method returns `true`, it has to set the `$length` parameter to the length of the matched string. It may
164
     * also set `$content` to a string that will be used as a token content.
165
     *
166
     * @param int $length  length of the matched string.
167
     * @param string|null $content optional content instead of the matched string.
168
     *
169
     * @return bool whether there's a string literal at the current offset.
170
     */
171
    protected function isStringLiteral(int &$length, ?string &$content): bool
172
    {
173
        if ($this->substring(1) !== "'") {
174
            return false;
175
        }
176
177
        $offset = $this->offset;
178
179
        while (true) {
180
            $offset = $this->indexAfter("'", $offset + 1);
181
            if ($this->substring(1, true, $offset) !== "'") {
182
                break;
183
            }
184
        }
185
        $length = $offset - $this->offset;
186
        $content = strtr($this->substring($length - 2, true, $this->offset + 1), ["''" => "'"]);
187
188
        return true;
189
    }
190
191
    /**
192
     * Returns whether the given string is a keyword.
193
     *
194
     * The method may set `$content` to a string that will be used as a token content.
195
     *
196
     * @param string $string  string to be matched.
197
     * @param string|null $content optional content instead of the matched string.
198
     *
199
     * @return bool whether the given string is a keyword.
200
     */
201
    protected function isKeyword(string $string, ?string &$content): bool
202
    {
203
        static $keywords = [
204
            'ABORT' => true,
205
            'ACTION' => true,
206
            'ADD' => true,
207
            'AFTER' => true,
208
            'ALL' => true,
209
            'ALTER' => true,
210
            'ANALYZE' => true,
211
            'AND' => true,
212
            'AS' => true,
213
            'ASC' => true,
214
            'ATTACH' => true,
215
            'AUTOINCREMENT' => true,
216
            'BEFORE' => true,
217
            'BEGIN' => true,
218
            'BETWEEN' => true,
219
            'BY' => true,
220
            'CASCADE' => true,
221
            'CASE' => true,
222
            'CAST' => true,
223
            'CHECK' => true,
224
            'COLLATE' => true,
225
            'COLUMN' => true,
226
            'COMMIT' => true,
227
            'CONFLICT' => true,
228
            'CONSTRAINT' => true,
229
            'CREATE' => true,
230
            'CROSS' => true,
231
            'CURRENT_DATE' => true,
232
            'CURRENT_TIME' => true,
233
            'CURRENT_TIMESTAMP' => true,
234
            'DATABASE' => true,
235
            'DEFAULT' => true,
236
            'DEFERRABLE' => true,
237
            'DEFERRED' => true,
238
            'DELETE' => true,
239
            'DESC' => true,
240
            'DETACH' => true,
241
            'DISTINCT' => true,
242
            'DROP' => true,
243
            'EACH' => true,
244
            'ELSE' => true,
245
            'END' => true,
246
            'ESCAPE' => true,
247
            'EXCEPT' => true,
248
            'EXCLUSIVE' => true,
249
            'EXISTS' => true,
250
            'EXPLAIN' => true,
251
            'FAIL' => true,
252
            'FOR' => true,
253
            'FOREIGN' => true,
254
            'FROM' => true,
255
            'FULL' => true,
256
            'GLOB' => true,
257
            'GROUP' => true,
258
            'HAVING' => true,
259
            'IF' => true,
260
            'IGNORE' => true,
261
            'IMMEDIATE' => true,
262
            'IN' => true,
263
            'INDEX' => true,
264
            'INDEXED' => true,
265
            'INITIALLY' => true,
266
            'INNER' => true,
267
            'INSERT' => true,
268
            'INSTEAD' => true,
269
            'INTERSECT' => true,
270
            'INTO' => true,
271
            'IS' => true,
272
            'ISNULL' => true,
273
            'JOIN' => true,
274
            'KEY' => true,
275
            'LEFT' => true,
276
            'LIKE' => true,
277
            'LIMIT' => true,
278
            'MATCH' => true,
279
            'NATURAL' => true,
280
            'NO' => true,
281
            'NOT' => true,
282
            'NOTNULL' => true,
283
            'NULL' => true,
284
            'OF' => true,
285
            'OFFSET' => true,
286
            'ON' => true,
287
            'OR' => true,
288
            'ORDER' => true,
289
            'OUTER' => true,
290
            'PLAN' => true,
291
            'PRAGMA' => true,
292
            'PRIMARY' => true,
293
            'QUERY' => true,
294
            'RAISE' => true,
295
            'RECURSIVE' => true,
296
            'REFERENCES' => true,
297
            'REGEXP' => true,
298
            'REINDEX' => true,
299
            'RELEASE' => true,
300
            'RENAME' => true,
301
            'REPLACE' => true,
302
            'RESTRICT' => true,
303
            'RIGHT' => true,
304
            'ROLLBACK' => true,
305
            'ROW' => true,
306
            'SAVEPOINT' => true,
307
            'SELECT' => true,
308
            'SET' => true,
309
            'TABLE' => true,
310
            'TEMP' => true,
311
            'TEMPORARY' => true,
312
            'THEN' => true,
313
            'TO' => true,
314
            'TRANSACTION' => true,
315
            'TRIGGER' => true,
316
            'UNION' => true,
317
            'UNIQUE' => true,
318
            'UPDATE' => true,
319
            'USING' => true,
320
            'VACUUM' => true,
321
            'VALUES' => true,
322
            'VIEW' => true,
323
            'VIRTUAL' => true,
324
            'WHEN' => true,
325
            'WHERE' => true,
326
            'WITH' => true,
327
            'WITHOUT' => true,
328
        ];
329
330
        $string = mb_strtoupper($string, 'UTF-8');
331
332
        if (!isset($keywords[$string])) {
333
            return false;
334
        }
335
336
        $content = $string;
337
338
        return true;
339
    }
340
}
341