Passed
Push — master ( b16987...8b6d77 )
by Maurício
03:49 queued 13s
created

Keys::parse()   D

Complexity

Conditions 31
Paths 3

Size

Total Lines 131
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 60
CRAP Score 31

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 31
eloc 65
nc 3
nop 3
dl 0
loc 131
ccs 60
cts 60
cp 1
crap 31
rs 4.1666
c 1
b 0
f 0

How to fix   Long Method    Complexity   

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\Parsers;
6
7
use PhpMyAdmin\SqlParser\Components\Key;
8
use PhpMyAdmin\SqlParser\Parseable;
9
use PhpMyAdmin\SqlParser\Parser;
10
use PhpMyAdmin\SqlParser\TokensList;
11
use PhpMyAdmin\SqlParser\TokenType;
12
13
/**
14
 * Parses the definition of a key.
15
 *
16
 * Used for parsing `CREATE TABLE` statement.
17
 */
18
final class Keys implements Parseable
19
{
20
    /**
21
     * All key options.
22
     */
23
    private const KEY_OPTIONS = [
24
        'KEY_BLOCK_SIZE' => [
25
            1,
26
            'var=',
27
        ],
28
        'USING' => [
29
            2,
30
            'var',
31
        ],
32
        'WITH PARSER' => [
33
            3,
34
            'var',
35
        ],
36
        'COMMENT' => [
37
            4,
38
            'var',
39
        ],
40
        // MariaDB options
41
        'CLUSTERING' => [
42
            4,
43
            'var=',
44
        ],
45
        'ENGINE_ATTRIBUTE' => [
46
            5,
47
            'var=',
48
        ],
49
        'SECONDARY_ENGINE_ATTRIBUTE' => [
50
            5,
51
            'var=',
52
        ],
53
        // MariaDB & MySQL options
54
        'VISIBLE' => 6,
55
        'INVISIBLE' => 6,
56
        // MariaDB options
57
        'IGNORED' => 10,
58
        'NOT IGNORED' => 10,
59
    ];
60
61
    /**
62
     * @param Parser               $parser  the parser that serves as context
63
     * @param TokensList           $list    the list of tokens that are being parsed
64
     * @param array<string, mixed> $options parameters for parsing
65
     */
66 68
    public static function parse(Parser $parser, TokensList $list, array $options = []): Key
67
    {
68 68
        $ret = new Key();
69
70
        /**
71
         * Last parsed column.
72
         */
73 68
        $lastColumn = [];
74
75
        /**
76
         * The state of the parser.
77
         *
78
         * Below are the states of the parser.
79
         *
80
         *      0 ---------------------[ type ]---------------------------> 1
81
         *
82
         *      1 ---------------------[ name ]---------------------------> 1
83
         *      1 ---------------------[ columns ]------------------------> 2
84
         *      1 ---------------------[ expression ]---------------------> 5
85
         *
86
         *      2 ---------------------[ column length ]------------------> 3
87
         *      3 ---------------------[ column length ]------------------> 2
88
         *      2 ---------------------[ options ]------------------------> 4
89
         *      5 ---------------------[ expression ]---------------------> 4
90
         */
91 68
        $state = 0;
92
93 68
        for (; $list->idx < $list->count; ++$list->idx) {
94
            /**
95
             * Token parsed at this moment.
96
             */
97 68
            $token = $list->tokens[$list->idx];
98
99
            // End of statement.
100 68
            if ($token->type === TokenType::Delimiter) {
101 4
                break;
102
            }
103
104
            // Skipping whitespaces and comments.
105 66
            if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
106 66
                continue;
107
            }
108
109 66
            if ($state === 0) {
110 66
                $ret->type = $token->value;
111 66
                $state = 1;
112 66
            } elseif ($state === 1) {
113 66
                if (($token->type === TokenType::Operator) && ($token->value === '(')) {
114 66
                    $positionBeforeSearch = $list->idx;
115 66
                    $list->idx++;// Ignore the current token "(" or the search condition will always be true
116 66
                    $nextToken = $list->getNext();
117 66
                    $list->idx = $positionBeforeSearch;// Restore the position
118
119 66
                    if ($nextToken !== null && $nextToken->value === '(') {
120
                        // Switch to expression mode
121 12
                        $state = 5;
122
                    } else {
123 56
                        $state = 2;
124
                    }
125
                } else {
126 52
                    $ret->name = $token->value;
127
                }
128 66
            } elseif ($state === 2) {
129 56
                if ($token->type === TokenType::Operator) {
130 56
                    if ($token->value === '(') {
131 14
                        $state = 3;
132 56
                    } elseif (($token->value === ',') || ($token->value === ')')) {
133 56
                        $state = $token->value === ',' ? 2 : 4;
134 56
                        if ($lastColumn !== []) {
135 56
                            $ret->columns[] = $lastColumn;
136 56
                            $lastColumn = [];
137
                        }
138
                    }
139
                } elseif (
140
                    (
141 56
                        $token->type === TokenType::Keyword
142
                    )
143
                    &&
144
                    (
145 56
                        ($token->keyword === 'ASC') || ($token->keyword === 'DESC')
146
                    )
147
                ) {
148 10
                    $lastColumn['order'] = $token->keyword;
149
                } else {
150 56
                    $lastColumn['name'] = $token->value;
151
                }
152 64
            } elseif ($state === 3) {
153 14
                if (($token->type === TokenType::Operator) && ($token->value === ')')) {
154 14
                    $state = 2;
155
                } else {
156 14
                    $lastColumn['length'] = $token->value;
157
                }
158 64
            } elseif ($state === 4) {
159 64
                $ret->options = OptionsArrays::parse($parser, $list, self::KEY_OPTIONS);
160 64
                ++$list->idx;
161 64
                break;
162 12
            } elseif ($state === 5) {
163 12
                if ($token->type === TokenType::Operator) {
164
                    // This got back to here and we reached the end of the expression
165 12
                    if ($token->value === ')') {
166 12
                        $state = 4;// go back to state 4 to fetch options
167 12
                        continue;
168
                    }
169
170
                    // The expression is not finished, adding a separator for the next expression
171 12
                    if ($token->value === ',') {
172 6
                        $ret->expr .= ', ';
173 6
                        continue;
174
                    }
175
176
                    // Start of the expression
177 12
                    if ($token->value === '(') {
178
                        // This is the first expression, set to empty
179 12
                        if ($ret->expr === null) {
180 12
                            $ret->expr = '';
181
                        }
182
183 12
                        $ret->expr .= Expressions::parse($parser, $list, ['parenthesesDelimited' => true]);
184 12
                        continue;
185
                    }
186
                    // Another unexpected operator was found
187
                }
188
189
                // Something else than an operator was found
190 2
                $parser->error('Unexpected token.', $token);
191
            }
192
        }
193
194 68
        --$list->idx;
195
196 68
        return $ret;
197
    }
198
}
199