Passed
Pull Request — master (#386)
by William
03:14
created

Formatter::getDefaultFormats()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 58
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 41
nc 1
nop 0
dl 0
loc 58
ccs 0
cts 0
cp 0
crap 2
rs 9.264
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 PhpMyAdmin\SqlParser\Utils;
6
7
use PhpMyAdmin\SqlParser\Components\JoinKeyword;
8
use PhpMyAdmin\SqlParser\Lexer;
9
use PhpMyAdmin\SqlParser\Parser;
10
use PhpMyAdmin\SqlParser\Token;
11
use PhpMyAdmin\SqlParser\TokensList;
12
13
use function array_merge;
14
use function array_pop;
15
use function end;
16
use function htmlspecialchars;
17
use function in_array;
18
use function mb_strlen;
19
use function str_contains;
20
use function str_repeat;
21
use function str_replace;
22
use function strtoupper;
23
24
use const ENT_NOQUOTES;
25
use const PHP_SAPI;
26
27
/**
28
 * Utilities that are used for formatting queries.
29
 */
30
class Formatter
31
{
32
    /**
33
     * The formatting options.
34
     *
35
     * @var array<string, bool|string|array<int, array<string, int|string>>>
36
     */
37
    public $options;
38
39
    /**
40
     * Clauses that are usually short.
41
     *
42
     * These clauses share the line with the next clause.
43
     *
44
     * E.g. if INSERT was not here, the formatter would produce:
45
     *
46
     *      INSERT
47
     *      INTO foo
48
     *      VALUES(0, 0, 0),(1, 1, 1);
49
     *
50
     * Instead of:
51
     *
52
     *      INSERT INTO foo
53
     *      VALUES(0, 0, 0),(1, 1, 1)
54
     *
55
     * @var array<string, bool>
56
     */
57
    public static $SHORT_CLAUSES = [
58
        'CREATE' => true,
59
        'INSERT' => true,
60
    ];
61
62
    /**
63
     * Clauses that must be inlined.
64
     *
65
     * These clauses usually are short and it's nicer to have them inline.
66
     *
67
     * @var array<string, bool>
68
     */
69
    public static $INLINE_CLAUSES = [
70
        'CREATE' => true,
71
        'INTO' => true,
72
        'LIMIT' => true,
73
        'PARTITION BY' => true,
74
        'PARTITION' => true,
75
        'PROCEDURE' => true,
76
        'SUBPARTITION BY' => true,
77
        'VALUES' => true,
78
    ];
79
80
    /**
81
     * @param array<string, bool|string|array<int, array<string, int|string>>> $options the formatting options
82
     */
83 96
    public function __construct(array $options = [])
84
    {
85 96
        $this->options = $this->getMergedOptions($options);
86
    }
87
88
    /**
89
     * The specified formatting options are merged with the default values.
90
     *
91
     * @param array<string, bool|string|array<int, array<string, int|string>>> $options
92
     *
93
     * @return array<string, bool|string|array<int, array<string, int|string>>>
94
     */
95 112
    private function getMergedOptions(array $options)
96
    {
97 112
        $options = array_merge(
98 112
            $this->getDefaultOptions(),
99
            $options
100
        );
101
102 112
        if (isset($options['formats'])) {
103 16
            $options['formats'] = self::mergeFormats($this->getDefaultFormats(), $options['formats']);
104
        } else {
105 96
            $options['formats'] = $this->getDefaultFormats();
106
        }
107
108 112
        if ($options['line_ending'] === null) {
109 96
            $options['line_ending'] = $options['type'] === 'html' ? '<br/>' : "\n";
110
        }
111
112 112
        if ($options['indentation'] === null) {
113 112
            $options['indentation'] = $options['type'] === 'html' ? '&nbsp;&nbsp;&nbsp;&nbsp;' : '    ';
114
        }
115
116
        // `parts_newline` requires `clause_newline`
117 112
        $options['parts_newline'] &= $options['clause_newline'];
118
119 112
        return $options;
120
    }
121
122
    /**
123
     * The default formatting options.
124
     *
125
     * @return array<string, bool|string|null>
126
     * @psalm-return array{
127
     *   type: ('cli'|'text'),
128
     *   line_ending: null,
129
     *   indentation: null,
130
     *   remove_comments: false,
131
     *   clause_newline: true,
132
     *   parts_newline: true,
133
     *   indent_parts: true
134
     * }
135
     */
136 96
    protected function getDefaultOptions()
137
    {
138
        return [
139
            /*
140
             * The format of the result.
141
             *
142
             * @var string The type ('text', 'cli' or 'html')
143
             */
144 96
            'type' => PHP_SAPI === 'cli' ? 'cli' : 'text',
145
146
            /*
147
             * The line ending used.
148
             * By default, for text this is "\n" and for HTML this is "<br/>".
149
             *
150
             * @var string
151
             */
152
            'line_ending' => null,
153
154
            /*
155
             * The string used for indentation.
156
             *
157
             * @var string
158
             */
159
            'indentation' => null,
160
161
            /*
162
             * Whether comments should be removed or not.
163
             *
164
             * @var bool
165
             */
166
            'remove_comments' => false,
167
168
            /*
169
             * Whether each clause should be on a new line.
170
             *
171
             * @var bool
172
             */
173
            'clause_newline' => true,
174
175
            /*
176
             * Whether each part should be on a new line.
177
             * Parts are delimited by brackets and commas.
178
             *
179
             * @var bool
180
             */
181
            'parts_newline' => true,
182
183
            /*
184
             * Whether each part of each clause should be indented.
185
             *
186
             * @var bool
187
             */
188
            'indent_parts' => true,
189
        ];
190
    }
191
192
    /**
193
     * The styles used for HTML formatting.
194
     * [$type, $flags, $span, $callback].
195
     *
196
     * @return array<int, array<string, int|string>>
197
     * @psalm-return list<array{type: int, flags: int, html: string, cli: string, function: string}>
198
     */
199
    protected function getDefaultFormats()
200
    {
201
        return [
202
            [
203
                'type' => Token::TYPE_KEYWORD,
204
                'flags' => Token::FLAG_KEYWORD_RESERVED,
205
                'html' => 'class="sql-reserved"',
206
                'cli' => "\x1b[35m",
207
                'function' => 'strtoupper',
208
            ],
209
            [
210
                'type' => Token::TYPE_KEYWORD,
211
                'flags' => 0,
212
                'html' => 'class="sql-keyword"',
213
                'cli' => "\x1b[95m",
214
                'function' => 'strtoupper',
215
            ],
216
            [
217
                'type' => Token::TYPE_COMMENT,
218
                'flags' => 0,
219
                'html' => 'class="sql-comment"',
220
                'cli' => "\x1b[37m",
221
                'function' => '',
222
            ],
223
            [
224
                'type' => Token::TYPE_BOOL,
225
                'flags' => 0,
226
                'html' => 'class="sql-atom"',
227
                'cli' => "\x1b[36m",
228
                'function' => 'strtoupper',
229
            ],
230
            [
231
                'type' => Token::TYPE_NUMBER,
232
                'flags' => 0,
233
                'html' => 'class="sql-number"',
234
                'cli' => "\x1b[92m",
235
                'function' => 'strtolower',
236
            ],
237
            [
238
                'type' => Token::TYPE_STRING,
239
                'flags' => 0,
240
                'html' => 'class="sql-string"',
241
                'cli' => "\x1b[91m",
242
                'function' => '',
243
            ],
244
            [
245
                'type' => Token::TYPE_SYMBOL,
246
                'flags' => Token::FLAG_SYMBOL_PARAMETER,
247
                'html' => 'class="sql-parameter"',
248
                'cli' => "\x1b[31m",
249
                'function' => '',
250
            ],
251
            [
252
                'type' => Token::TYPE_SYMBOL,
253
                'flags' => 0,
254
                'html' => 'class="sql-variable"',
255
                'cli' => "\x1b[36m",
256
                'function' => '',
257
            ],
258
        ];
259
    }
260
261
    /**
262
     * @param array<int, array<string, int|string>> $formats
263
     * @param array<int, array<string, int|string>> $newFormats
264
     *
265
     * @return array<int, array<string, int|string>>
266
     */
267 16
    private static function mergeFormats(array $formats, array $newFormats): array
268
    {
269 16
        $added = [];
270 8
        $integers = [
271
            'flags',
272
            'type',
273
        ];
274 8
        $strings = [
275
            'html',
276
            'cli',
277
            'function',
278
        ];
279
280
        /* Sanitize the array so that we do not have to care later */
281 16
        foreach ($newFormats as $j => $new) {
282 16
            foreach ($integers as $name) {
283 16
                if (isset($new[$name])) {
284 12
                    continue;
285
                }
286
287 12
                $newFormats[$j][$name] = 0;
288
            }
289
290 16
            foreach ($strings as $name) {
291 16
                if (isset($new[$name])) {
292 12
                    continue;
293
                }
294
295 16
                $newFormats[$j][$name] = '';
296
            }
297
        }
298
299
        /* Process changes to existing formats */
300 16
        foreach ($formats as $i => $original) {
301 16
            foreach ($newFormats as $j => $new) {
302 16
                if ($new['type'] !== $original['type'] || $original['flags'] !== $new['flags']) {
303 12
                    continue;
304
                }
305
306 12
                $formats[$i] = $new;
307 12
                $added[] = $j;
308
            }
309
        }
310
311
        /* Add not already handled formats */
312 16
        foreach ($newFormats as $j => $new) {
313 16
            if (in_array($j, $added)) {
314 12
                continue;
315
            }
316
317 4
            $formats[] = $new;
318
        }
319
320 16
        return $formats;
321
    }
322
323
    /**
324
     * Formats the given list of tokens.
325
     *
326
     * @param TokensList $list the list of tokens
327
     *
328
     * @return string
329
     */
330 96
    public function formatList($list)
331
    {
332
        /**
333
         * The query to be returned.
334
         *
335
         * @var string
336
         */
337 96
        $ret = '';
338
339
        /**
340
         * The indentation level.
341
         *
342
         * @var int
343
         */
344 96
        $indent = 0;
345
346
        /**
347
         * Whether the line ended.
348
         *
349
         * @var bool
350
         */
351 96
        $lineEnded = false;
352
353
        /**
354
         * Whether current group is short (no linebreaks).
355
         *
356
         * @var bool
357
         */
358 96
        $shortGroup = false;
359
360
        /**
361
         * The name of the last clause.
362
         *
363
         * @var string
364
         */
365 96
        $lastClause = '';
366
367
        /**
368
         * A stack that keeps track of the indentation level every time a new
369
         * block is found.
370
         */
371 96
        $blocksIndentation = [];
372
373
        /**
374
         * A stack that keeps track of the line endings every time a new block
375
         * is found.
376
         */
377 96
        $blocksLineEndings = [];
378
379
        /**
380
         * Whether clause's options were formatted.
381
         *
382
         * @var bool
383
         */
384 96
        $formattedOptions = false;
385
386
        /**
387
         * Previously parsed token.
388
         *
389
         * @var Token|null
390
         */
391 96
        $prev = null;
392
393
        // In order to be able to format the queries correctly, the next token
394
        // must be taken into consideration. The loop below uses two pointers,
395
        // `$prev` and `$curr` which store two consecutive tokens.
396
        // Actually, at every iteration the previous token is being used.
397 96
        for ($list->idx = 0; $list->idx < $list->count; ++$list->idx) {
398
            /**
399
             * Token parsed at this moment.
400
             */
401 96
            $curr = $list->tokens[$list->idx];
402 96
            if ($list->idx + 1 < $list->count) {
403 92
                $next = $list->tokens[$list->idx + 1];
404
            } else {
405 96
                $next = null;
406
            }
407
408 96
            if ($curr->type === Token::TYPE_WHITESPACE) {
409
                // Keep linebreaks before and after comments
410
                if (
411 92
                    str_contains($curr->token, "\n") && (
412 12
                        ($prev !== null && $prev->type === Token::TYPE_COMMENT) ||
413 92
                        ($next !== null && $next->type === Token::TYPE_COMMENT)
414
                    )
415
                ) {
416 4
                    $lineEnded = true;
417
                }
418
419
                // Whitespaces are skipped because the formatter adds its own.
420 92
                continue;
421
            }
422
423 96
            if ($curr->type === Token::TYPE_COMMENT && $this->options['remove_comments']) {
424
                // Skip Comments if option `remove_comments` is enabled
425 4
                continue;
426
            }
427
428
            // Checking if pointers were initialized.
429 96
            if ($prev !== null) {
430
                // Checking if a new clause started.
431 92
                if (static::isClause($prev) !== false) {
432 92
                    $lastClause = $prev->value;
433 92
                    $formattedOptions = false;
434
                }
435
436
                // The options of a clause should stay on the same line and everything that follows.
437
                if (
438 92
                    $this->options['parts_newline']
439
                    && ! $formattedOptions
440 92
                    && empty(self::$INLINE_CLAUSES[$lastClause])
441
                    && (
442 88
                        $curr->type !== Token::TYPE_KEYWORD
443
                        || (
444 20
                            $curr->type === Token::TYPE_KEYWORD
445 92
                            && $curr->flags & Token::FLAG_KEYWORD_FUNCTION
446
                        )
447
                    )
448
                ) {
449 84
                    $formattedOptions = true;
450 84
                    $lineEnded = true;
451 84
                    ++$indent;
452
                }
453
454
                // Checking if this clause ended.
455 92
                $isClause = static::isClause($curr);
456
457 92
                if ($isClause) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $isClause of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
458
                    if (
459 40
                        ($isClause === 2 || $this->options['clause_newline'])
460 40
                        && empty(self::$SHORT_CLAUSES[$lastClause])
461
                    ) {
462 40
                        $lineEnded = true;
463 40
                        if ($this->options['parts_newline'] && $indent > 0) {
464 36
                            --$indent;
465
                        }
466
                    }
467
                }
468
469
                // Inline JOINs
470
                if (
471 92
                    ($prev->type === Token::TYPE_KEYWORD && isset(JoinKeyword::$JOINS[$prev->value]))
472 92
                    || (in_array($curr->value, ['ON', 'USING'], true)
473 92
                        && isset(JoinKeyword::$JOINS[$list->tokens[$list->idx - 2]->value]))
474 92
                    || isset($list->tokens[$list->idx - 4], JoinKeyword::$JOINS[$list->tokens[$list->idx - 4]->value])
475 92
                    || isset($list->tokens[$list->idx - 6], JoinKeyword::$JOINS[$list->tokens[$list->idx - 6]->value])
476
                ) {
477 4
                    $lineEnded = false;
478
                }
479
480
                // Indenting BEGIN ... END blocks.
481 92
                if ($prev->type === Token::TYPE_KEYWORD && $prev->keyword === 'BEGIN') {
482 4
                    $lineEnded = true;
483 4
                    $blocksIndentation[] = $indent;
484 4
                    ++$indent;
485 92
                } elseif ($curr->type === Token::TYPE_KEYWORD && $curr->keyword === 'END') {
486 4
                    $lineEnded = true;
487 4
                    $indent = array_pop($blocksIndentation);
488
                }
489
490
                // Formatting fragments delimited by comma.
491 92
                if ($prev->type === Token::TYPE_OPERATOR && $prev->value === ',') {
492
                    // Fragments delimited by a comma are broken into multiple
493
                    // pieces only if the clause is not inlined or this fragment
494
                    // is between brackets that are on new line.
495
                    if (
496 16
                        end($blocksLineEndings) === true
497
                        || (
498 12
                            empty(self::$INLINE_CLAUSES[$lastClause])
499
                            && ! $shortGroup
500 16
                            && $this->options['parts_newline']
501
                        )
502
                    ) {
503 12
                        $lineEnded = true;
504
                    }
505
                }
506
507
                // Handling brackets.
508
                // Brackets are indented only if the length of the fragment between
509
                // them is longer than 30 characters.
510 92
                if ($prev->type === Token::TYPE_OPERATOR && $prev->value === '(') {
511 24
                    $blocksIndentation[] = $indent;
512 24
                    $shortGroup = true;
513 24
                    if (static::getGroupLength($list) > 30) {
514 4
                        ++$indent;
515 4
                        $lineEnded = true;
516 4
                        $shortGroup = false;
517
                    }
518
519 24
                    $blocksLineEndings[] = $lineEnded;
520 92
                } elseif ($curr->type === Token::TYPE_OPERATOR && $curr->value === ')') {
521 20
                    $indent = array_pop($blocksIndentation);
522 20
                    $lineEnded |= array_pop($blocksLineEndings);
523 20
                    $shortGroup = false;
524
                }
525
526
                // Adding the token.
527 92
                $ret .= $this->toString($prev);
528
529
                // Finishing the line.
530 92
                if ($lineEnded) {
531 88
                    $ret .= $this->options['line_ending']
532 88
                        . str_repeat($this->options['indentation'], (int) $indent);
533
534 88
                    $lineEnded = false;
535
                } else {
536
                    // If the line ended there is no point in adding whitespaces.
537
                    // Also, some tokens do not have spaces before or after them.
538
                    if (
539
                        // A space after delimiters that are longer than 2 characters.
540 92
                        $prev->keyword === 'DELIMITER'
541
                        || ! (
542 92
                            ($prev->type === Token::TYPE_OPERATOR && ($prev->value === '.' || $prev->value === '('))
543
                            // No space after . (
544 92
                            || ($curr->type === Token::TYPE_OPERATOR
545 44
                                && ($curr->value === '.' || $curr->value === ','
546 92
                                    || $curr->value === '(' || $curr->value === ')'))
547
                            // No space before . , ( )
548 92
                            || $curr->type === Token::TYPE_DELIMITER && mb_strlen((string) $curr->value, 'UTF-8') < 2
549
                        )
550
                    ) {
551 52
                        $ret .= ' ';
552
                    }
553
                }
554
            }
555
556
            // Iteration finished, consider current token as previous.
557 96
            $prev = $curr;
558
        }
559
560 96
        if ($this->options['type'] === 'cli') {
561 80
            return $ret . "\x1b[0m";
562
        }
563
564 84
        return $ret;
565
    }
566
567 76
    public function escapeConsole(string $string): string
568
    {
569 76
        return str_replace(
570
            [
571 76
                "\x00",
572
                "\x01",
573
                "\x02",
574
                "\x03",
575
                "\x04",
576
                "\x05",
577
                "\x06",
578
                "\x07",
579
                "\x08",
580
                "\x09",
581
                "\x0A",
582
                "\x0B",
583
                "\x0C",
584
                "\x0D",
585
                "\x0E",
586
                "\x0F",
587
                "\x10",
588
                "\x11",
589
                "\x12",
590
                "\x13",
591
                "\x14",
592
                "\x15",
593
                "\x16",
594
                "\x17",
595
                "\x18",
596
                "\x19",
597
                "\x1A",
598
                "\x1B",
599
                "\x1C",
600
                "\x1D",
601
                "\x1E",
602
                "\x1F",
603
            ],
604
            [
605 76
                '\x00',
606
                '\x01',
607
                '\x02',
608
                '\x03',
609
                '\x04',
610
                '\x05',
611
                '\x06',
612
                '\x07',
613
                '\x08',
614
                '\x09',
615
                '\x0A',
616
                '\x0B',
617
                '\x0C',
618
                '\x0D',
619
                '\x0E',
620
                '\x0F',
621
                '\x10',
622
                '\x11',
623
                '\x12',
624
                '\x13',
625
                '\x14',
626
                '\x15',
627
                '\x16',
628
                '\x17',
629
                '\x18',
630
                '\x19',
631
                '\x1A',
632
                '\x1B',
633
                '\x1C',
634
                '\x1D',
635
                '\x1E',
636
                '\x1F',
637
            ],
638
            $string
639
        );
640
    }
641
642
    /**
643
     * Tries to print the query and returns the result.
644
     *
645
     * @param Token $token the token to be printed
646
     *
647
     * @return string
648
     */
649 92
    public function toString($token)
650
    {
651 92
        $text = $token->token;
652
        static $prev;
653
654 92
        foreach ($this->options['formats'] as $format) {
655 92
            if ($token->type !== $format['type'] || ! (($token->flags & $format['flags']) === $format['flags'])) {
656 92
                continue;
657
            }
658
659
            // Running transformation function.
660 92
            if (! empty($format['function'])) {
661 92
                $func = $format['function'];
662 92
                $text = $func($text);
663
            }
664
665
            // Formatting HTML.
666 92
            if ($this->options['type'] === 'html') {
667 72
                return '<span ' . $format['html'] . '>' . htmlspecialchars($text, ENT_NOQUOTES) . '</span>';
668
            }
669
670 84
            if ($this->options['type'] === 'cli') {
671 76
                if ($prev !== $format['cli']) {
672 76
                    $prev = $format['cli'];
673
674 76
                    return $format['cli'] . $this->escapeConsole($text);
675
                }
676
677 20
                return $this->escapeConsole($text);
678
            }
679
680 72
            break;
681
        }
682
683 72
        if ($this->options['type'] === 'cli') {
684 56
            if ($prev !== "\x1b[39m") {
685 56
                $prev = "\x1b[39m";
686
687 56
                return "\x1b[39m" . $this->escapeConsole($text);
688
            }
689
690 32
            return $this->escapeConsole($text);
691
        }
692
693 72
        if ($this->options['type'] === 'html') {
694 56
            return htmlspecialchars($text, ENT_NOQUOTES);
695
        }
696
697 72
        return $text;
698
    }
699
700
    /**
701
     * Formats a query.
702
     *
703
     * @param string                                                           $query   The query to be formatted
704
     * @param array<string, bool|string|array<int, array<string, int|string>>> $options the formatting options
705
     *
706
     * @return string the formatted string
707
     */
708 96
    public static function format($query, array $options = [])
709
    {
710 96
        $lexer = new Lexer($query);
711 96
        $formatter = new self($options);
712
713 96
        return $formatter->formatList($lexer->list);
714
    }
715
716
    /**
717
     * Computes the length of a group.
718
     *
719
     * A group is delimited by a pair of brackets.
720
     *
721
     * @param TokensList $list the list of tokens
722
     *
723
     * @return int
724
     */
725 24
    public static function getGroupLength($list)
726
    {
727
        /**
728
         * The number of opening brackets found.
729
         * This counter starts at one because by the time this function called,
730
         * the list already advanced one position and the opening bracket was
731
         * already parsed.
732
         *
733
         * @var int
734
         */
735 24
        $count = 1;
736
737
        /**
738
         * The length of this group.
739
         *
740
         * @var int
741
         */
742 24
        $length = 0;
743
744 24
        for ($idx = $list->idx; $idx < $list->count; ++$idx) {
745
            // Counting the brackets.
746 24
            if ($list->tokens[$idx]->type === Token::TYPE_OPERATOR) {
747 24
                if ($list->tokens[$idx]->value === '(') {
748 4
                    ++$count;
749 24
                } elseif ($list->tokens[$idx]->value === ')') {
750 24
                    --$count;
751 24
                    if ($count === 0) {
752 24
                        break;
753
                    }
754
                }
755
            }
756
757
            // Keeping track of this group's length.
758 20
            $length += mb_strlen((string) $list->tokens[$idx]->value, 'UTF-8');
759
        }
760
761 24
        return $length;
762
    }
763
764
    /**
765
     * Checks if a token is a statement or a clause inside a statement.
766
     *
767
     * @param Token $token the token to be checked
768
     *
769
     * @return int|bool
770
     */
771 92
    public static function isClause($token)
772
    {
773
        if (
774 92
            ($token->type === Token::TYPE_KEYWORD && isset(Parser::$STATEMENT_PARSERS[$token->keyword]))
775 92
            || ($token->type === Token::TYPE_NONE && strtoupper($token->token) === 'DELIMITER')
776
        ) {
777 88
            return 2;
778
        }
779
780 92
        if ($token->type === Token::TYPE_KEYWORD && isset(Parser::$KEYWORD_PARSERS[$token->keyword])) {
781 40
            return 1;
782
        }
783
784 92
        return false;
785
    }
786
}
787