Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
tokenizer.php
1<?php
2namespace Bitrix\Perfmon\Sql;
3
4class Token
5{
6 const T_WHITESPACE = 0;
7 const T_STRING = 1;
8 const T_CHAR = 2;
9
10 const T_SINGLE_QUOTE = 3;
11 const T_DOUBLE_QUOTE = 4;
12 const T_BACK_QUOTE = 5;
13 const T_SQUARE_QUOTE = 6;
14
15 const T_COMMENT = 7;
16
17 public $type;
18 public $text;
19 public $upper;
20 public $line;
21 public $level;
22
27 public function __construct($type, $text)
28 {
29 $this->type = $type;
30 $this->text = $text;
31 $this->upper = mb_strtoupper($this->text);
32 }
33
43 public function setText($text)
44 {
45 $this->text = $text;
46 $this->upper = mb_strtoupper($this->text);
47 }
48
58 public function appendText($text)
59 {
60 $this->text .= $text;
61 $this->upper = mb_strtoupper($this->text);
62 }
63}
64
66{
67 protected $index = 0;
68 protected $bookmark = 0;
70 protected $tokens = [];
71
79 public static function createFromString($sql)
80 {
81 $tokenizer = new self;
82 $tokenizer->_tokenize($sql);
83 $tokenizer->makeLines();
84 $tokenizer->makeParenthesis();
85 return $tokenizer;
86 }
87
95 public static function createFromTokens(array $tokens)
96 {
97 $tokenizer = new self;
98 $tokenizer->tokens = $tokens;
99 return $tokenizer;
100 }
101
107 public function getTokens()
108 {
109 return $this->tokens;
110 }
111
117 public function resetState()
118 {
119 $this->index = 0;
120 }
121
128 public function setBookmark()
129 {
130 $this->bookmark = $this->index;
131 }
132
139 public function restoreBookmark()
140 {
141 $this->index = $this->bookmark;
142 }
143
149 public function putBack()
150 {
151 $this->index--;
152 }
153
159 public function endOfInput()
160 {
161 return !isset($this->tokens[$this->index]);
162 }
163
171 public function getPrevToken()
172 {
174 $token = $this->tokens[$this->index - 1] ?? null;
175 return $token;
176 }
177
185 public function getCurrentToken()
186 {
188 $token = $this->tokens[$this->index] ?? null;
189 return $token;
190 }
191
199 public function nextToken()
200 {
201 $this->index++;
203 $token = $this->tokens[$this->index] ?? null;
204 return $token;
205 }
206
212 public function nextNotWhiteSpaceToken()
213 {
214 $i = $this->index + 1;
215 while (isset($this->tokens[$i]))
216 {
218 $token = $this->tokens[$i];
219 if ($token->type == Token::T_WHITESPACE || $token->type == Token::T_COMMENT)
220 {
221 $i++;
222 }
223 else
224 {
225 break;
226 }
227 }
228 return $this->tokens[$i] ?? null;
229 }
230
236 public function skipWhiteSpace()
237 {
238 while (isset($this->tokens[$this->index]))
239 {
241 $token = $this->tokens[$this->index];
242 if ($token->type == Token::T_WHITESPACE || $token->type == Token::T_COMMENT)
243 {
244 $this->index++;
245 }
246 else
247 {
248 break;
249 }
250 }
251 }
252
262 public function testUpperText($text)
263 {
264 if (isset($this->tokens[$this->index]))
265 {
267 $token = $this->tokens[$this->index];
268 if ($token->upper === $text)
269 {
270 $this->index++;
271 return true;
272 }
273 }
274 return false;
275 }
276
286 public function testText($text)
287 {
288 if (isset($this->tokens[$this->index]))
289 {
291 $token = $this->tokens[$this->index];
292 if ($token->upper === $text)
293 {
294 $this->index++;
295 return true;
296 }
297 }
298 return false;
299 }
300
308 private function _tokenize($sql)
309 {
310 $this->tokens = [];
311 $tokenCount = 0;
312 $chars = '(),.:=;/';
313 $rawTokens = preg_split("/(
314 [ \\t\\n\\r]+ # WHITESPACE
315 |\\\\+ # BACKSLASHES
316 |\" # DOUBLE QUOTE
317 |' # SINGLE QUOTE
318 |`[^`]+` # BACK QUOTE
319 |\\[[^\\]]+\\] # SQUARE QUOTE
320 |\\/\\*.*?\\*\\/ # COMMENTARY
321 |--.*?\\n # COMMENTARY
322 |\\#[^']*?\\n # COMMENTARY
323 |[" . preg_quote($chars, '/') . '] # CHARACTER
324 )/xs', $sql, -1, PREG_SPLIT_DELIM_CAPTURE);
325 $isInSingleQuote = false;
326 $isInDoubleQuote = false;
327 foreach ($rawTokens as $rawToken)
328 {
329 if ($rawToken === '')
330 {
331 continue;
332 }
333
335 $prevToken = $this->tokens[$tokenCount - 1] ?? null;
336
337 if ($isInSingleQuote)
338 {
339 $prevToken->appendText($rawToken);
340 if (
341 $rawToken === "'"
342 && preg_match("/(\\\\)*'\$/", $prevToken->text, $match)
343 && (
344 mb_strlen($match[0]) === 0
345 || (mb_strlen($match[0]) % 2) === 1
346 )
347 )
348 {
349 $isInSingleQuote = false;
350 }
351 }
352 elseif ($isInDoubleQuote)
353 {
354 $prevToken->appendText($rawToken);
355 if (
356 $rawToken === '"'
357 && preg_match('/(\\\\)*"$/', $prevToken->text, $match)
358 && (mb_strlen($match[0]) % 2) === 1
359 )
360 {
361 $isInDoubleQuote = false;
362 }
363 }
364 elseif ($rawToken[0] === '`')
365 {
366 $this->tokens[$tokenCount++] = new Token(Token::T_BACK_QUOTE, $rawToken);
367 }
368 elseif ($rawToken[0] === '[')
369 {
370 $this->tokens[$tokenCount++] = new Token(Token::T_SQUARE_QUOTE, $rawToken);
371 }
372 elseif (
373 ($rawToken[0] === '/' && $rawToken[1] === '*')
374 || ($rawToken[0] === '-' && $rawToken[1] === '-')
375 || ($rawToken[0] === '#')
376 )
377 {
378 $this->tokens[$tokenCount++] = new Token(Token::T_COMMENT, $rawToken);
379 }
380 elseif (mb_strlen($rawToken) == 1 && mb_strpos($chars, $rawToken) !== false)
381 {
382 $this->tokens[$tokenCount++] = new Token(Token::T_CHAR, $rawToken);
383 }
384 elseif ($rawToken === '"')
385 {
386 $this->tokens[$tokenCount++] = new Token(Token::T_DOUBLE_QUOTE, $rawToken);
387 $isInDoubleQuote = true;
388 }
389 elseif ($rawToken === "'")
390 {
391 $this->tokens[$tokenCount++] = new Token(Token::T_SINGLE_QUOTE, $rawToken);
392 $isInSingleQuote = true;
393 }
394 elseif (preg_match("/^[ \\t\\n\\r]+\$/", $rawToken))
395 {
396 $this->tokens[$tokenCount++] = new Token(Token::T_WHITESPACE, $rawToken);
397 }
398 else
399 {
400 if ($tokenCount > 0 && $prevToken->type === Token::T_STRING)
401 {
402 $prevToken->appendText($rawToken);
403 }
404 else
405 {
406 $this->tokens[$tokenCount++] = new Token(Token::T_STRING, $rawToken);
407 }
408 }
409 }
410 }
411
417 private function makeLines()
418 {
419 $line = 1;
421 foreach ($this->tokens as $token)
422 {
423 $token->line = $line;
424 if (preg_match_all("/\\n/", $token->text, $m))
425 {
426 $line += count($m[0]);
427 }
428 }
429 }
430
436 private function makeParenthesis()
437 {
438 $level = 0;
440 foreach ($this->tokens as $token)
441 {
442 if ($token->text === ')')
443 {
444 $level--;
445 }
446 $token->level = $level;
447 if ($token->text === '(')
448 {
449 $level++;
450 }
451 }
452 }
453}
__construct($type, $text)
Definition tokenizer.php:27
static createFromTokens(array $tokens)
Definition tokenizer.php:95