Test Failed
Pull Request — master (#291)
by William
12:25
created

IntoKeyword::parse()   D

Complexity

Conditions 23
Paths 9

Size

Total Lines 96

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 23

Importance

Changes 0
Metric Value
dl 0
loc 96
ccs 45
cts 45
cp 1
rs 4.1666
c 0
b 0
f 0
cc 23
nc 9
nop 3
crap 23

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
 * `INTO` keyword parser.
5
 */
6
7
namespace PhpMyAdmin\SqlParser\Components;
8
9
use PhpMyAdmin\SqlParser\Component;
10
use PhpMyAdmin\SqlParser\Parser;
11
use PhpMyAdmin\SqlParser\Token;
12
use PhpMyAdmin\SqlParser\TokensList;
13
14
/**
15
 * `INTO` keyword parser.
16
 *
17
 * @category   Keywords
18
 *
19
 * @license    https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
20
 */
21
class IntoKeyword extends Component
22
{
23
    /**
24
     * FIELDS/COLUMNS Options for `SELECT...INTO` statements.
25
     *
26
     * @var array
27
     */
28
    public static $FIELDS_OPTIONS = array(
29
        'TERMINATED BY' => array(
30
            1,
31
            'expr',
32
        ),
33
        'OPTIONALLY' => 2,
34
        'ENCLOSED BY' => array(
35
            3,
36
            'expr',
37
        ),
38
        'ESCAPED BY' => array(
39
            4,
40
            'expr',
41
        )
42
    );
43
44
    /**
45
     * LINES Options for `SELECT...INTO` statements.
46
     *
47
     * @var array
48
     */
49
    public static $LINES_OPTIONS = array(
50
        'STARTING BY' => array(
51
            1,
52
            'expr',
53
        ),
54
        'TERMINATED BY' => array(
55
            2,
56
            'expr',
57
        )
58
    );
59
60
    /**
61
     * Type of target (OUTFILE or SYMBOL).
62
     *
63
     * @var string
64
     */
65
    public $type;
66
67
    /**
68
     * The destination, which can be a table or a file.
69
     *
70
     * @var string|Expression
71
     */
72
    public $dest;
73
74
    /**
75
     * The name of the columns.
76
     *
77
     * @var array
78
     */
79
    public $columns;
80
81
    /**
82
     * The values to be selected into (SELECT .. INTO @var1).
83
     *
84
     * @var Expression[]
85
     */
86
    public $values;
87
88
    /**
89
     * Options for FIELDS/COLUMNS keyword.
90
     *
91
     * @var OptionsArray
92
     *
93
     * @see static::$FIELDS_OPTIONS
94
     */
95
    public $fields_options;
96
97
    /**
98
     * Whether to use `FIELDS` or `COLUMNS` while building.
99
     *
100
     * @var bool
101
     */
102
    public $fields_keyword;
103
104
    /**
105
     * Options for OPTIONS keyword.
106
     *
107
     * @var OptionsArray
108
     *
109
     * @see static::$LINES_OPTIONS
110
     */
111
    public $lines_options;
112
113
    /**
114
     * Constructor.
115
     *
116
     * @param string            $type           type of destination (may be OUTFILE)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
117
     * @param string|Expression $dest           actual destination
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dest not be string|Expression|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
118
     * @param array             $columns        column list of destination
0 ignored issues
show
Documentation introduced by
Should the type for parameter $columns not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
119
     * @param array             $values         selected fields
0 ignored issues
show
Documentation introduced by
Should the type for parameter $values not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
120
     * @param OptionsArray      $fields_options options for FIELDS/COLUMNS keyword
0 ignored issues
show
Documentation introduced by
Should the type for parameter $fields_options not be OptionsArray|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
121
     * @param bool              $fields_keyword options for OPTIONS keyword
0 ignored issues
show
Documentation introduced by
Should the type for parameter $fields_keyword not be boolean|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
122
     */
123 39
    public function __construct(
124
        $type = null,
125
        $dest = null,
126
        $columns = null,
127
        $values = null,
128
        $fields_options = null,
129
        $fields_keyword = null
130
    ) {
131 39
        $this->type = $type;
132 39
        $this->dest = $dest;
133 39
        $this->columns = $columns;
0 ignored issues
show
Documentation Bug introduced by
It seems like $columns can be null. However, the property $columns is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
134 39
        $this->values = $values;
0 ignored issues
show
Documentation Bug introduced by
It seems like $values can be null. However, the property $values is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
135 39
        $this->fields_options = $fields_options;
136 39
        $this->fields_keyword = $fields_keyword;
137 39
    }
138
139
    /**
140
     * @param Parser     $parser  the parser that serves as context
141
     * @param TokensList $list    the list of tokens that are being parsed
142
     * @param array      $options parameters for parsing
143
     *
144
     * @return IntoKeyword
145
     */
146 39
    public static function parse(Parser $parser, TokensList $list, array $options = array())
147
    {
148 39
        $ret = new self();
149
150
        /**
151
         * The state of the parser.
152
         *
153
         * Below are the states of the parser.
154
         *
155
         *      0 -----------------------[ name ]----------------------> 1
156
         *      0 ---------------------[ OUTFILE ]---------------------> 2
157
         *
158
         *      1 ------------------------[ ( ]------------------------> (END)
159
         *
160
         *      2 ---------------------[ filename ]--------------------> 1
161
         *
162
         * @var int
163
         */
164 39
        $state = 0;
165
166 39
        for (; $list->idx < $list->count; ++$list->idx) {
167
            /**
168
             * Token parsed at this moment.
169
             *
170
             * @var Token
171
             */
172 39
            $token = $list->tokens[$list->idx];
173
174
            // End of statement.
175 39
            if ($token->type === Token::TYPE_DELIMITER) {
176 7
                break;
177
            }
178
179
            // Skipping whitespaces and comments.
180 39
            if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
181 36
                continue;
182
            }
183
184 39
            if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
185 18
                if (($state === 0) && ($token->keyword === 'OUTFILE')) {
186 10
                    $ret->type = 'OUTFILE';
187 10
                    $state = 2;
188 10
                    continue;
189
                }
190
191
                // No other keyword is expected except for $state = 4, which expects `LINES`
192 13
                if ($state !== 4) {
193 12
                    break;
194
                }
195
            }
196
197 38
            if ($state === 0) {
198 29
                if ((isset($options['fromInsert'])
199 14
                    && $options['fromInsert'])
200 15
                    || (isset($options['fromReplace'])
201 29
                    && $options['fromReplace'])
202
                ) {
203 27
                    $ret->dest = Expression::parse(
204 27
                        $parser,
205 27
                        $list,
206
                        array(
207 27
                            'parseField' => 'table',
208
                            'breakOnAlias' => true
209
                        )
210
                    );
211
                } else {
212 2
                    $ret->values = ExpressionArray::parse($parser, $list);
213
                }
214 29
                $state = 1;
215 29
            } elseif ($state === 1) {
216 20
                if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
217 18
                    $ret->columns = ArrayObj::parse($parser, $list)->values;
218 18
                    ++$list->idx;
219
                }
220 20
                break;
221 9
            } elseif ($state === 2) {
222 9
                $ret->dest = $token->value;
223
224 9
                $state = 3;
225 4
            } elseif ($state === 3) {
226 4
                $ret->parseFileOptions($parser, $list, $token->value);
227 4
                $state = 4;
228 4
            } elseif ($state === 4) {
229 4
                if ($token->type === Token::TYPE_KEYWORD && $token->keyword !== 'LINES') {
230 1
                    break;
231
                }
232
233 3
                $ret->parseFileOptions($parser, $list, $token->value);
234 3
                $state = 5;
235
            }
236
        }
237
238 39
        --$list->idx;
239
240 39
        return $ret;
241
    }
242
243 4 View Code Duplication
    public function parseFileOptions(Parser $parser, TokensList $list, $keyword = 'FIELDS')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
244
    {
245 4
        ++$list->idx;
246
247 4
        if ($keyword === 'FIELDS' || $keyword === 'COLUMNS') {
248
            // parse field options
249 4
            $this->fields_options = OptionsArray::parse(
250 4
                $parser,
251
                $list,
252
                static::$FIELDS_OPTIONS
253
            );
254
255 4
            $this->fields_keyword = ($keyword === 'FIELDS');
256
        } else {
257
            // parse line options
258 3
            $this->lines_options = OptionsArray::parse(
259 3
                $parser,
260
                $list,
261
                static::$LINES_OPTIONS
262
            );
263
        }
264 4
    }
265
266
    /**
267
     * @param IntoKeyword $component the component to be built
268
     * @param array       $options   parameters for building
269
     *
270
     * @return string
271
     */
272 9
    public static function build($component, array $options = array())
273
    {
274 9
        if ($component->dest instanceof Expression) {
275 5
            $columns = ! empty($component->columns) ? '(`' . implode('`, `', $component->columns) . '`)' : '';
276
277 5
            return $component->dest . $columns;
278 4
        } elseif (isset($component->values)) {
279 2
            return ExpressionArray::build($component->values);
280
        }
281
282 2
        $ret = 'OUTFILE "' . $component->dest . '"';
283
284 2
        $fields_options_str = OptionsArray::build($component->fields_options);
285 2
        if (trim($fields_options_str) !== '') {
286 1
            $ret .= $component->fields_keyword ? ' FIELDS' : ' COLUMNS';
287 1
            $ret .= ' ' . $fields_options_str;
288
        }
289
290 2
        $lines_options_str = OptionsArray::build($component->lines_options, array('expr' => true));
291 2
        if (trim($lines_options_str) !== '') {
292 1
            $ret .= ' LINES ' . $lines_options_str;
293
        }
294
295 2
        return $ret;
296
    }
297
}
298