Passed
Pull Request — master (#145)
by Deven
04:53
created

DeleteStatement::parse()   D

Complexity

Conditions 43
Paths 22

Size

Total Lines 186
Code Lines 115

Duplication

Lines 32
Ratio 17.2 %

Code Coverage

Tests 110
CRAP Score 43

Importance

Changes 0
Metric Value
dl 32
loc 186
ccs 110
cts 110
cp 1
rs 4.1818
c 0
b 0
f 0
cc 43
eloc 115
nc 22
nop 2
crap 43

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
/**
4
 * `DELETE` statement.
5
 */
6
7
namespace PhpMyAdmin\SqlParser\Statements;
8
9
use PhpMyAdmin\SqlParser\Components\ArrayObj;
10
use PhpMyAdmin\SqlParser\Components\Condition;
11
use PhpMyAdmin\SqlParser\Components\Expression;
12
use PhpMyAdmin\SqlParser\Components\ExpressionArray;
13
use PhpMyAdmin\SqlParser\Components\JoinKeyword;
14
use PhpMyAdmin\SqlParser\Components\Limit;
15
use PhpMyAdmin\SqlParser\Components\OptionsArray;
16
use PhpMyAdmin\SqlParser\Components\OrderKeyword;
17
use PhpMyAdmin\SqlParser\Parser;
18
use PhpMyAdmin\SqlParser\Statement;
19
use PhpMyAdmin\SqlParser\Token;
20
use PhpMyAdmin\SqlParser\TokensList;
21
22
/**
23
 * `DELETE` statement.
24
 *
25
 * DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name
26
 *     [PARTITION (partition_name,...)]
27
 *     [WHERE where_condition]
28
 *     [ORDER BY ...]
29
 *     [LIMIT row_count]
30
 *
31
 * Multi-table syntax
32
 *
33
 * DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
34
 *   tbl_name[.*] [, tbl_name[.*]] ...
35
 *   FROM table_references
36
 *   [WHERE where_condition]
37
 *
38
 * OR
39
 *
40
 * DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
41
 *   FROM tbl_name[.*] [, tbl_name[.*]] ...
42
 *   USING table_references
43
 *   [WHERE where_condition]
44
 *
45
 *
46
 * @category   Statements
47
 *
48
 * @license    https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
49
 */
50
class DeleteStatement extends Statement
51
{
52
    /**
53
     * Options for `DELETE` statements.
54
     *
55
     * @var array
56
     */
57
    public static $OPTIONS = array(
58
        'LOW_PRIORITY' => 1,
59
        'QUICK' => 2,
60
        'IGNORE' => 3,
61
    );
62
63
    /**
64
     * The clauses of this statement, in order.
65
     *
66
     * @see Statement::$CLAUSES
67
     *
68
     * @var array
69
     */
70
    public static $CLAUSES = array(
71
        'DELETE' => array('DELETE', 2),
72
        // Used for options.
73
        '_OPTIONS' => array('_OPTIONS', 1),
74
        'FROM' => array('FROM', 3),
75
        'PARTITION' => array('PARTITION', 3),
76
        'USING' => array('USING', 3),
77
        'WHERE' => array('WHERE', 3),
78
        'ORDER BY' => array('ORDER BY', 3),
79
        'LIMIT' => array('LIMIT', 3),
80
    );
81
82
    /**
83
     * Table(s) used as sources for this statement.
84
     *
85
     * @var Expression[]
86
     */
87
    public $from;
88
89
    /**
90
     * Joins.
91
     *
92
     * @var JoinKeyword[]
93
     */
94
    public $join;
95
96
97
    /**
98
     * Tables used as sources for this statement.
99
     *
100
     * @var Expression[]
101
     */
102
    public $using;
103
104
    /**
105
     * Columns used in this statement.
106
     *
107
     * @var Expression[]
108
     */
109
    public $columns;
110
111
    /**
112
     * Partitions used as source for this statement.
113
     *
114
     * @var ArrayObj
115
     */
116
    public $partition;
117
118
    /**
119
     * Conditions used for filtering each row of the result set.
120
     *
121
     * @var Condition[]
122
     */
123
    public $where;
124
125
    /**
126
     * Specifies the order of the rows in the result set.
127
     *
128
     * @var OrderKeyword[]
129
     */
130
    public $order;
131
132
    /**
133
     * Conditions used for limiting the size of the result set.
134
     *
135
     * @var Limit
136
     */
137
    public $limit;
138
139
    /**
140
     * @return string
141
     */
142 2
    public function build()
143
    {
144 2
        $ret = 'DELETE ' . OptionsArray::build($this->options);
145
146 2
        if ($this->columns != null && count($this->columns) > 0) {
147 1
            $ret .= ' ' . ExpressionArray::build($this->columns);
0 ignored issues
show
Documentation introduced by
$this->columns is of type array<integer,object<Php...Components\Expression>>, but the function expects a array<integer,object<Php...nents\ExpressionArray>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
148
        }
149 2 View Code Duplication
        if ($this->from != null && count($this->from) > 0) {
150 2
            $ret .= ' FROM ' . ExpressionArray::build($this->from);
0 ignored issues
show
Documentation introduced by
$this->from is of type array<integer,object<Php...Components\Expression>>, but the function expects a array<integer,object<Php...nents\ExpressionArray>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
151
        }
152 2
        if ($this->join != null && count($this->join) > 0) {
153 1
            $ret .= ' ' . JoinKeyword::build($this->join);
154
        }
155 2 View Code Duplication
        if ($this->using != null && count($this->using) > 0) {
156 1
            $ret .= ' USING ' . ExpressionArray::build($this->using);
0 ignored issues
show
Documentation introduced by
$this->using is of type array<integer,object<Php...Components\Expression>>, but the function expects a array<integer,object<Php...nents\ExpressionArray>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
157
        }
158 2
        if ($this->where != null && count($this->where) > 0) {
159 2
            $ret .= ' WHERE ' . Condition::build($this->where);
160
        }
161 2 View Code Duplication
        if ($this->order != null && count($this->order) > 0) {
162 1
            $ret .= ' ORDER BY ' . ExpressionArray::build($this->order);
0 ignored issues
show
Documentation introduced by
$this->order is of type array<integer,object<Php...mponents\OrderKeyword>>, but the function expects a array<integer,object<Php...nents\ExpressionArray>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
163
        }
164 2
        if ($this->limit != null && strlen($this->limit) > 0) {
165 1
            $ret .= ' LIMIT ' . Limit::build($this->limit);
166
        }
167
168 2
        return $ret;
169
    }
170
171
    /**
172
     * @param Parser     $parser the instance that requests parsing
173
     * @param TokensList $list   the list of tokens to be parsed
174
     */
175 29
    public function parse(Parser $parser, TokensList $list)
176
    {
177 29
        ++$list->idx; // Skipping `DELETE`.
178
179
        // parse any options if provided
180 29
        $this->options = OptionsArray::parse(
181
            $parser,
182
            $list,
183 29
            static::$OPTIONS
184
        );
185 29
        ++$list->idx;
186
187
        /**
188
         * The state of the parser.
189
         *
190
         * Below are the states of the parser.
191
         *
192
         *      0 ---------------------------------[ FROM ]----------------------------------> 2
193
         *      0 ------------------------------[ table[.*] ]--------------------------------> 1
194
         *      1 ---------------------------------[ FROM ]----------------------------------> 2
195
         *      2 --------------------------------[ USING ]----------------------------------> 3
196
         *      2 --------------------------------[ WHERE ]----------------------------------> 4
197
         *      2 --------------------------------[ ORDER ]----------------------------------> 5
198
         *      2 --------------------------------[ LIMIT ]----------------------------------> 6
199
         *
200
         * @var int
201
         */
202 29
        $state = 0;
203
204
        /**
205
         * If the query is multi-table or not.
206
         *
207
         * @var bool
208
         */
209 29
        $multiTable = false;
210
211 29
        for (; $list->idx < $list->count; ++$list->idx) {
212
            /**
213
             * Token parsed at this moment.
214
             *
215
             * @var Token
216
             */
217 29
            $token = $list->tokens[$list->idx];
218
219
            // End of statement.
220 29
            if ($token->type === Token::TYPE_DELIMITER) {
221 17
                break;
222
            }
223
224 29
            if ($state === 0) {
225 29
                if ($token->type === Token::TYPE_KEYWORD
226 29
                    && $token->keyword !== 'FROM'
227
                ) {
228 1
                    $parser->error('Unexpected keyword.', $token);
229 1
                    break;
230 28
                } elseif ($token->type === Token::TYPE_KEYWORD
231 28
                    && $token->keyword === 'FROM'
232
                ) {
233 23
                    ++$list->idx; // Skip 'FROM'
234 23
                    $this->from = ExpressionArray::parse($parser, $list);
235
236 23
                    $state = 2;
237
                } else {
238 6
                    $this->columns = ExpressionArray::parse($parser, $list);
239 28
                    $state = 1;
240
                }
241 26 View Code Duplication
            } elseif ($state === 1) {
242 6
                if ($token->type === Token::TYPE_KEYWORD
243 6
                    && $token->keyword !== 'FROM'
244
                ) {
245 1
                    $parser->error('Unexpected keyword.', $token);
246 1
                    break;
247 5
                } elseif ($token->type === Token::TYPE_KEYWORD
248 5
                    && $token->keyword === 'FROM'
249
                ) {
250 4
                    ++$list->idx; // Skip 'FROM'
251 4
                    $this->from = ExpressionArray::parse($parser, $list);
252
253 4
                    $state = 2;
254
                } else {
255 1
                    $parser->error('Unexpected token.', $token);
256 5
                    break;
257
                }
258 23
            } elseif ($state === 2) {
259 23
                if ($token->type === Token::TYPE_KEYWORD
260 23
                    && stripos($token->keyword, 'JOIN') !== false
261
                ) {
262 2
                    ++$list->idx;
263 2
                    $this->join = JoinKeyword::parse($parser, $list);
264
265
                    // remain in state = 2
266 23
                } elseif ($token->type === Token::TYPE_KEYWORD
267 23
                    && $token->keyword === 'USING'
268
                ) {
269 7
                    ++$list->idx; // Skip 'USING'
270 7
                    $this->using = ExpressionArray::parse($parser, $list);
271 7
                    $state = 3;
272
273 7
                    $multiTable = true;
274 17
                } elseif ($token->type === Token::TYPE_KEYWORD
275 17
                    && $token->keyword === 'WHERE'
276
                ) {
277 13
                    ++$list->idx; // Skip 'WHERE'
278 13
                    $this->where = Condition::parse($parser, $list);
279 13
                    $state = 4;
280 4
                } elseif ($token->type === Token::TYPE_KEYWORD
281 4
                    && $token->keyword === 'ORDER BY'
282
                ) {
283 2
                    ++$list->idx; // Skip 'ORDER BY'
284 2
                    $this->order = OrderKeyword::parse($parser, $list);
285 2
                    $state = 5;
286 2
                } elseif ($token->type === Token::TYPE_KEYWORD
287 2
                    && $token->keyword === 'LIMIT'
288
                ) {
289 1
                    ++$list->idx; // Skip 'LIMIT'
290 1
                    $this->limit = Limit::parse($parser, $list);
291 1
                    $state = 6;
292 1
                } elseif ($token->type === Token::TYPE_KEYWORD) {
293 1
                    $parser->error('Unexpected keyword.', $token);
294 23
                    break;
295
                }
296 16 View Code Duplication
            } elseif ($state === 3) {
297 7
                if ($token->type === Token::TYPE_KEYWORD
298 7
                    && $token->keyword === 'WHERE'
299
                ) {
300 4
                    ++$list->idx; // Skip 'WHERE'
301 4
                    $this->where = Condition::parse($parser, $list);
302 4
                    $state = 4;
303 3
                } elseif ($token->type === Token::TYPE_KEYWORD) {
304 1
                    $parser->error('Unexpected keyword.', $token);
305 1
                    break;
306
                } else {
307 2
                    $parser->error('Unexpected token.', $token);
308 6
                    break;
309
                }
310 11
            } elseif ($state === 4) {
311 10
                if ($multiTable === true
312 10
                    && $token->type === Token::TYPE_KEYWORD
313
                ) {
314 2
                    $parser->error(
315 2
                        'This type of clause is not valid in Multi-table queries.',
316
                        $token
317
                    );
318 2
                    break;
319
                }
320
321 8
                if ($token->type === Token::TYPE_KEYWORD
322 8
                    && $token->keyword === 'ORDER BY'
323
                ) {
324 6
                    ++$list->idx; // Skip 'ORDER  BY'
325 6
                    $this->order = OrderKeyword::parse($parser, $list);
326 6
                    $state = 5;
327 2
                } elseif ($token->type === Token::TYPE_KEYWORD
328 2
                    && $token->keyword === 'LIMIT'
329
                ) {
330 1
                    ++$list->idx; // Skip 'LIMIT'
331 1
                    $this->limit = Limit::parse($parser, $list);
332 1
                    $state = 6;
333 1
                } elseif ($token->type === Token::TYPE_KEYWORD) {
334 1
                    $parser->error('Unexpected keyword.', $token);
335 8
                    break;
336
                }
337 5
            } elseif ($state === 5) {
338 5
                if ($token->type === Token::TYPE_KEYWORD
339 5
                    && $token->keyword === 'LIMIT'
340
                ) {
341 3
                    ++$list->idx; // Skip 'LIMIT'
342 3
                    $this->limit = Limit::parse($parser, $list);
343 3
                    $state = 6;
344 2
                } elseif ($token->type === Token::TYPE_KEYWORD) {
345 2
                    $parser->error('Unexpected keyword.', $token);
346 2
                    break;
347
                }
348
            }
349
        }
350
351 29
        if ($state >= 2) {
352 26
            foreach ($this->from as $from_expr) {
353 26
                $from_expr->database = $from_expr->table;
354 26
                $from_expr->table = $from_expr->column;
355 26
                $from_expr->column = null;
356
            }
357
        }
358
359 29
        --$list->idx;
360 29
    }
361
}
362