Test Failed
Pull Request — master (#193)
by
unknown
03:54
created

CaseExpression   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 288
Duplicated Lines 5.56 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 99.04%

Importance

Changes 0
Metric Value
wmc 39
lcom 1
cbo 6
dl 16
loc 288
ccs 103
cts 104
cp 0.9904
rs 8.2857
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
D parse() 8 181 30
C build() 8 33 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * Parses a reference to a CASE expression.
5
 */
6
7
namespace PhpMyAdmin\SqlParser\Components;
8
9
use PhpMyAdmin\SqlParser\Component;
10
use PhpMyAdmin\SqlParser\Context;
11
use PhpMyAdmin\SqlParser\Parser;
12
use PhpMyAdmin\SqlParser\Token;
13
use PhpMyAdmin\SqlParser\TokensList;
14
15
/**
16
 * Parses a reference to a CASE expression.
17
 *
18
 * @category   Components
19
 *
20
 * @license    https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
21
 */
22
class CaseExpression extends Component
23
{
24
    /**
25
     * The value to be compared.
26
     *
27
     * @var Expression
28
     */
29
    public $value;
30
31
    /**
32
     * The conditions in WHEN clauses.
33
     *
34
     * @var array
35
     */
36
    public $conditions;
37
38
    /**
39
     * The results matching with the WHEN clauses.
40
     *
41
     * @var array
42
     */
43
    public $results;
44
45
    /**
46
     * The values to be compared against.
47
     *
48
     * @var array
49
     */
50
    public $compare_values;
51
52
    /**
53
     * The result in ELSE section of expr.
54
     *
55
     * @var Expression
56
     */
57
    public $else_result;
58
59
    /**
60
     * The alias of this expression.
61
     * Added by Sinri
62
     *
63
     * @var string
64
     */
65
    public $alias;
66
67
    /**
68
     * The sub-expression.
69
     *
70
     * @var string
71
     */
72
    public $expr = '';
73
74
    /**
75
     * Constructor.
76
     */
77 17
    public function __construct()
78
    {
79 17
    }
80
81
    /**
82
     * @param Parser $parser the parser that serves as context
83
     * @param TokensList $list the list of tokens that are being parsed
84
     *
85
     * @return CaseExpression
86
     * @throws \PhpMyAdmin\SqlParser\Exceptions\ParserException
87
     */
88 17
    public static function parse(Parser $parser, TokensList $list, array $options = array())
89
    {
90 17
        $ret = new self();
91
92
        /**
93
         * State of parser.
94
         *
95
         * @var int
96
         */
97 17
        $state = 0;
98
99
        /**
100
         * Syntax type (type 0 or type 1).
101
         *
102
         * @var int
103
         */
104 17
        $type = 0;
105
106 17
        ++$list->idx; // Skip 'CASE'
107
108 17
        for (; $list->idx < $list->count; ++$list->idx) {
109
            /**
110
             * Token parsed at this moment.
111
             *
112
             * @var Token
113
             */
114 17
            $token = $list->tokens[$list->idx];
115
116
            //echo __METHOD__ . '@' . __LINE__ . ' state ' . $state . ' see token: ' . $token . PHP_EOL;
117
            // state:
118
            // case[0] XXX[1]
119
            // when[2] XXX then[1]
120
            // else[0] XXX
121
            // end
122
123
            // Skipping whitespaces and comments.
124 17
            if (($token->type === Token::TYPE_WHITESPACE)
125 17
                || ($token->type === Token::TYPE_COMMENT)
126
            ) {
127 17
                continue;
128
            }
129
130 17
            if ($state === 0) {
131 17
                if ($token->type === Token::TYPE_KEYWORD
132 11
                    && $token->keyword === 'WHEN'
133
                ) {
134 7
                    ++$list->idx; // Skip 'WHEN'
135 7
                    $new_condition = Condition::parse($parser, $list);
136 7
                    $type = 1;
137 7
                    $state = 1;
138 7
                    $ret->conditions[] = $new_condition;
139 16
                } elseif ($token->type === Token::TYPE_KEYWORD
140 10
                    && $token->keyword === 'ELSE'
141
                ) {
142 4
                    ++$list->idx; // Skip 'ELSE'
143 4
                    $ret->else_result = Expression::parse($parser, $list);
144 4
                    $state = 0; // last clause of CASE expression
145 16
                } elseif ($token->type === Token::TYPE_KEYWORD
146 10
                    && $token->keyword === 'END'
147
                ) {
148 9
                    $state = 3; // end of CASE expression
149 9
                    ++$list->idx;
150 9
                    break;
151 10
                } elseif ($token->type === Token::TYPE_KEYWORD) {
152 1
                    $parser->error('Unexpected keyword.', $token);
153 1
                    break;
154
                } else {
155 9
                    $ret->value = Expression::parse($parser, $list);
156 9
                    $type = 0;
157 9
                    $state = 1;
158
                }
159 16
            } elseif ($state === 1) {
160 16
                if ($type === 0) {
161 9
                    if ($token->type === Token::TYPE_KEYWORD
162 9
                        && $token->keyword === 'WHEN'
163
                    ) {
164 7
                        ++$list->idx; // Skip 'WHEN'
165 7
                        $new_value = Expression::parse($parser, $list);
166 7
                        $state = 2;
167 7
                        $ret->compare_values[] = $new_value;
168 8
                    } elseif ($token->type === Token::TYPE_KEYWORD
169 8
                        && $token->keyword === 'ELSE'
170
                    ) {
171 3
                        ++$list->idx; // Skip 'ELSE'
172 3
                        $ret->else_result = Expression::parse($parser, $list);
173 3
                        $state = 0; // last clause of CASE expression
174 5
                    } elseif ($token->type === Token::TYPE_KEYWORD
175 5
                        && $token->keyword === 'END'
176
                    ) {
177 3
                        $state = 3; // end of CASE expression
178 3
                        ++$list->idx;
179 3
                        break;
180 2
                    } elseif ($token->type === Token::TYPE_KEYWORD) {
181 2
                        $parser->error('Unexpected keyword.', $token);
182 2
                        break;
183
                    }
184
                } else {
185 7
                    if ($token->type === Token::TYPE_KEYWORD
186 7
                        && $token->keyword === 'THEN'
187
                    ) {
188 6
                        ++$list->idx; // Skip 'THEN'
189 6
                        $new_result = Expression::parse($parser, $list);
190 6
                        $state = 0;
191 6
                        $ret->results[] = $new_result;
192 1
                    } elseif ($token->type === Token::TYPE_KEYWORD) {
193 1
                        $parser->error('Unexpected keyword.', $token);
194 1
                        break;
195
                    }
196
                }
197 7
            } elseif ($state === 2) {
198 7
                if ($type === 0) {
199 7
                    if ($token->type === Token::TYPE_KEYWORD
200 7
                        && $token->keyword === 'THEN'
201
                    ) {
202 7
                        ++$list->idx; // Skip 'THEN'
203 7
                        $new_result = Expression::parse($parser, $list);
204 7
                        $ret->results[] = $new_result;
205 7
                        $state = 1;
206 1
                    } elseif ($token->type === Token::TYPE_KEYWORD) {
207 1
                        $parser->error('Unexpected keyword.', $token);
208 1
                        break;
209
                    }
210
                }
211
            }
212
        }
213
214 17 View Code Duplication
        if ($state !== 3) {
215 5
            $parser->error(
216 5
                'Unexpected end of CASE expression',
217 5
                $list->tokens[$list->idx - 1]
218
            );
219
        } else {
220
            /*
221
                        // Seek Alias
222
                        // To fix https://github.com/phpmyadmin/sql-parser/issues/192
223
                        // (a) CASE...END [, or KEYWORD]
224
                        // (b) CASE...END AS XXX [, or KEYWORD]
225
                        // (c) CASE...END XXX [, or KEYWORD]
226
                        // (d) CASE...END + 1 AS XXX -> not support ... to complex
227
                        $aliasMode='a';
228
229
                        for($tmpIdx=$list->idx;$tmpIdx<$list->count;$tmpIdx++){
230
                            $token = $list->tokens[$tmpIdx];
231
232
                            echo __METHOD__.'@'.__LINE__.' debug seek alias token: '.$token.PHP_EOL;
233
234
                            if(
235
                                $token->type===Token::TYPE_WHITESPACE
236
                                || $token->type===Token::TYPE_COMMENT
237
                            ){
238
                                // whitespace
239
                                continue;
240
                            }
241
                            elseif($aliasMode==='a'){
242
                                if($token->keyword==='AS'){
243
                                    $aliasMode='b';
244
                                    continue;
245
                                }elseif($token->type===Token::TYPE_OPERATOR){
246
                                    $aliasMode='d';
247
                                    $list->idx=$tmpIdx+1;
248
                                    break;
249
                                }else{
250
                                    $aliasMode='c';
251
                                    $ret->alias=$token->value;
252
                                    $list->idx=$tmpIdx+1;
253
                                    break;
254
                                }
255
                            }elseif($aliasMode==='b'){
256
                                $ret->alias=$token->value;
257
                                $list->idx=$tmpIdx+1;
258
                                break;
259
                            }
260
                        }
261
            */
262 12
            $ret->expr = self::build($ret);
263
        }
264
265 17
        --$list->idx;
266
267 17
        return $ret;
268
    }
269
270
    /**
271
     * @param CaseExpression $component the component to be built
272
     * @param array          $options   parameters for building
273
     *
274
     * @return string
275
     */
276 12
    public static function build($component, array $options = array())
277
    {
278 12
        $ret = 'CASE ';
279 12
        if (isset($component->value)) {
280
            // Syntax type 0
281 6
            $ret .= $component->value . ' ';
282 6
            $val_cnt = count($component->compare_values);
283 6
            $res_cnt = count($component->results);
284 6 View Code Duplication
            for ($i = 0; $i < $val_cnt && $i < $res_cnt; ++$i) {
285 6
                $ret .= 'WHEN ' . $component->compare_values[$i] . ' ';
286 6
                $ret .= 'THEN ' . $component->results[$i] . ' ';
287
            }
288
        } else {
289
            // Syntax type 1
290 6
            $val_cnt = count($component->conditions);
291 6
            $res_cnt = count($component->results);
292 6 View Code Duplication
            for ($i = 0; $i < $val_cnt && $i < $res_cnt; ++$i) {
293 6
                $ret .= 'WHEN ' . Condition::build($component->conditions[$i]) . ' ';
294 6
                $ret .= 'THEN ' . $component->results[$i] . ' ';
295
            }
296
        }
297 12
        if (isset($component->else_result)) {
298 7
            $ret .= 'ELSE ' . $component->else_result . ' ';
299
        }
300 12
        $ret .= 'END';
301
302
        // a fix for https://github.com/phpmyadmin/sql-parser/issues/192
303 12
        if ($component->alias) {
304
            $ret .= ' AS ' . Context::escape($component->alias);
305
        }
306
307 12
        return $ret;
308
    }
309
}
310