CaseExpressions::parse()   F
last analyzed

Complexity

Conditions 43
Paths 260

Size

Total Lines 173
Code Lines 114

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 109
CRAP Score 43

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 43
eloc 114
nc 260
nop 3
dl 0
loc 173
ccs 109
cts 109
cp 1
crap 43
rs 2.0666
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\CaseExpression;
8
use PhpMyAdmin\SqlParser\Parseable;
9
use PhpMyAdmin\SqlParser\Parser;
10
use PhpMyAdmin\SqlParser\Token;
11
use PhpMyAdmin\SqlParser\TokensList;
12
use PhpMyAdmin\SqlParser\TokenType;
13
14
/**
15
 * Parses a reference to a CASE expression.
16
 */
17
final class CaseExpressions implements Parseable
18
{
19
    /**
20
     * @param Parser               $parser  the parser that serves as context
21
     * @param TokensList           $list    the list of tokens that are being parsed
22
     * @param array<string, mixed> $options parameters for parsing
23
     */
24 64
    public static function parse(Parser $parser, TokensList $list, array $options = []): CaseExpression
25
    {
26 64
        $ret = new CaseExpression();
27
28
        /**
29
         * State of parser.
30
         */
31 64
        $state = 0;
32
33
        /**
34
         * Syntax type (type 0 or type 1).
35
         */
36 64
        $type = 0;
37
38 64
        ++$list->idx; // Skip 'CASE'
39
40 64
        for (; $list->idx < $list->count; ++$list->idx) {
41
            /**
42
             * Token parsed at this moment.
43
             */
44 64
            $token = $list->tokens[$list->idx];
45
46
            // Skipping whitespaces and comments.
47 64
            if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
48 64
                continue;
49
            }
50
51 64
            if ($state === 0) {
52 64
                if ($token->type === TokenType::Keyword) {
53 48
                    switch ($token->keyword) {
54 48
                        case 'WHEN':
55 30
                            ++$list->idx; // Skip 'WHEN'
56 30
                            $newCondition = Conditions::parse($parser, $list);
57 30
                            $type = 1;
58 30
                            $state = 1;
59 30
                            $ret->conditions[] = $newCondition;
60 30
                            break;
61 46
                        case 'ELSE':
62 16
                            ++$list->idx; // Skip 'ELSE'
63 16
                            $ret->elseResult = Expressions::parse($parser, $list);
64 16
                            $state = 0; // last clause of CASE expression
65 16
                            break;
66 46
                        case 'END':
67 44
                            $state = 3; // end of CASE expression
68 44
                            ++$list->idx;
69 44
                            break 2;
70
                        default:
71 2
                            $parser->error('Unexpected keyword.', $token);
72 2
                            break 2;
73
                    }
74
                } else {
75 32
                    $ret->value = Expressions::parse($parser, $list);
76 32
                    $type = 0;
77 32
                    $state = 1;
78
                }
79 62
            } elseif ($state === 1) {
80 62
                if ($type === 0) {
81 32
                    if ($token->type === TokenType::Keyword) {
82 32
                        switch ($token->keyword) {
83 32
                            case 'WHEN':
84 28
                                ++$list->idx; // Skip 'WHEN'
85 28
                                $newValue = Expressions::parse($parser, $list);
86 28
                                $state = 2;
87 28
                                $ret->compareValues[] = $newValue;
88 28
                                break;
89 30
                            case 'ELSE':
90 16
                                ++$list->idx; // Skip 'ELSE'
91 16
                                $ret->elseResult = Expressions::parse($parser, $list);
92 16
                                $state = 0; // last clause of CASE expression
93 16
                                break;
94 14
                            case 'END':
95 10
                                $state = 3; // end of CASE expression
96 10
                                ++$list->idx;
97 10
                                break 2;
98
                            default:
99 4
                                $parser->error('Unexpected keyword.', $token);
100 4
                                break 2;
101
                        }
102
                    }
103 30
                } elseif ($token->type === TokenType::Keyword && $token->keyword === 'THEN') {
104 28
                    ++$list->idx; // Skip 'THEN'
105 28
                    $newResult = Expressions::parse($parser, $list);
106 28
                    $state = 0;
107 28
                    $ret->results[] = $newResult;
108 2
                } elseif ($token->type === TokenType::Keyword) {
109 2
                    $parser->error('Unexpected keyword.', $token);
110 2
                    break;
111
                }
112 28
            } elseif ($state === 2) {
113 28
                if ($type === 0) {
114 28
                    if ($token->type === TokenType::Keyword && $token->keyword === 'THEN') {
115 28
                        ++$list->idx; // Skip 'THEN'
116 28
                        $newResult = Expressions::parse($parser, $list);
117 28
                        $ret->results[] = $newResult;
118 28
                        $state = 1;
119 2
                    } elseif ($token->type === TokenType::Keyword) {
120 2
                        $parser->error('Unexpected keyword.', $token);
121 2
                        break;
122
                    }
123
                }
124
            }
125
        }
126
127 64
        if ($state !== 3) {
128 10
            $parser->error('Unexpected end of CASE expression', $list->tokens[$list->idx - 1]);
129
        } else {
130
            // Parse for alias of CASE expression
131 54
            $asFound = false;
132 54
            for (; $list->idx < $list->count; ++$list->idx) {
133 54
                $token = $list->tokens[$list->idx];
134
135
                // End of statement.
136 54
                if ($token->type === TokenType::Delimiter) {
137 20
                    break;
138
                }
139
140
                // Skipping whitespaces and comments.
141 40
                if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
142 40
                    continue;
143
                }
144
145
                // Handle optional AS keyword before alias
146 40
                if ($token->type === TokenType::Keyword && $token->keyword === 'AS') {
147 20
                    if ($asFound || ! empty($ret->alias)) {
148 2
                        $parser->error('Potential duplicate alias of CASE expression.', $token);
149 2
                        break;
150
                    }
151
152 20
                    $asFound = true;
153 20
                    continue;
154
                }
155
156
                if (
157 36
                    $asFound
158 36
                    && $token->type === TokenType::Keyword
159 36
                    && ($token->flags & Token::FLAG_KEYWORD_RESERVED || $token->flags & Token::FLAG_KEYWORD_FUNCTION)
160
                ) {
161 2
                    $parser->error('An alias expected after AS but got ' . $token->value, $token);
162 2
                    $asFound = false;
163 2
                    break;
164
                }
165
166
                if (
167 34
                    $asFound
168 32
                    || $token->type === TokenType::String
169 32
                    || ($token->type === TokenType::Symbol && ! $token->flags & Token::FLAG_SYMBOL_VARIABLE)
170 34
                    || $token->type === TokenType::None
171
                ) {
172
                    // An alias is expected (the keyword `AS` was previously found).
173 20
                    if (! empty($ret->alias)) {
174 2
                        $parser->error('An alias was previously found.', $token);
175 2
                        break;
176
                    }
177
178 20
                    $ret->alias = $token->value;
179 20
                    $asFound = false;
180
181 20
                    continue;
182
                }
183
184 28
                break;
185
            }
186
187 54
            if ($asFound) {
188 4
                $parser->error('An alias was expected after AS.', $list->tokens[$list->idx - 1]);
189
            }
190
191 54
            $ret->expr = $ret->build();
192
        }
193
194 64
        --$list->idx;
195
196 64
        return $ret;
197
    }
198
}
199