SqlTokenizer::isKeyword()   B
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 138
Code Lines 130

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 132
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 130
c 1
b 0
f 0
dl 0
loc 138
ccs 132
cts 132
cp 1
rs 8
cc 2
nc 2
nop 2
crap 2

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