Passed
Push — master ( 1265f7...db8617 )
by Alexander
01:41
created

SqlTokenizer   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Test Coverage

Coverage 91.84%

Importance

Changes 0
Metric Value
eloc 203
c 0
b 0
f 0
dl 0
loc 278
ccs 45
cts 49
cp 0.9184
rs 10
wmc 17

6 Methods

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