Completed
Push — master ( 65f66e...428edc )
by Michal
04:14
created

IntoKeyword   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 241
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 241
ccs 88
cts 88
cp 1
rs 9.2
c 0
b 0
f 0
wmc 34
lcom 1
cbo 6

3 Methods

Rating   Name   Duplication   Size   Complexity  
D parse() 0 96 23
B parseFileOptions() 0 26 4
C build() 0 26 7
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(1, 'expr'),
30
        'OPTIONALLY' => 2,
31
        'ENCLOSED BY' => array(3, 'expr'),
32
        'ESCAPED BY' => array(4, 'expr'),
33
    );
34
35
    /**
36
     * LINES Options for `SELECT...INTO` statements.
37
     *
38
     * @var array
39
     */
40
    public static $LINES_OPTIONS = array(
41
        'STARTING BY' => array(1, 'expr'),
42
        'TERMINATED BY' => array(2, 'expr'),
43
    );
44
45
    /**
46
     * Type of target (OUTFILE or SYMBOL).
47
     *
48
     * @var string
49
     */
50
    public $type;
51
52
    /**
53
     * The destination, which can be a table or a file.
54
     *
55
     * @var string|Expression
56
     */
57
    public $dest;
58
59
    /**
60
     * The name of the columns.
61
     *
62
     * @var array
63
     */
64
    public $columns;
65
66
    /**
67
     * The values to be selected into (SELECT .. INTO @var1).
68
     *
69
     * @var ExpressionArray
70
     */
71
    public $values;
72
73
    /**
74
     * Options for FIELDS/COLUMNS keyword.
75
     *
76
     * @var OptionsArray
77
     *
78
     * @see static::$FIELDS_OPTIONS
79
     */
80
    public $fields_options;
81
82
    /**
83
     * Whether to use `FIELDS` or `COLUMNS` while building.
84
     *
85
     * @var bool
86
     */
87
    public $fields_keyword;
88
89
    /**
90
     * Options for OPTIONS keyword.
91
     *
92
     * @var OptionsArray
93
     *
94
     * @see static::$LINES_OPTIONS
95
     */
96
    public $lines_options;
97
98
    /**
99
     * @param Parser     $parser  the parser that serves as context
100
     * @param TokensList $list    the list of tokens that are being parsed
101
     * @param array      $options parameters for parsing
102
     *
103
     * @return IntoKeyword
104
     */
105 36
    public static function parse(Parser $parser, TokensList $list, array $options = array())
106
    {
107 36
        $ret = new self();
108
109
        /**
110
         * The state of the parser.
111
         *
112
         * Below are the states of the parser.
113
         *
114
         *      0 -----------------------[ name ]----------------------> 1
115
         *      0 ---------------------[ OUTFILE ]---------------------> 2
116
         *
117
         *      1 ------------------------[ ( ]------------------------> (END)
118
         *
119
         *      2 ---------------------[ filename ]--------------------> 1
120
         *
121
         * @var int
122
         */
123 36
        $state = 0;
124
125 36
        for (; $list->idx < $list->count; ++$list->idx) {
126
            /**
127
             * Token parsed at this moment.
128
             *
129
             * @var Token
130
             */
131 36
            $token = $list->tokens[$list->idx];
132
133
            // End of statement.
134 36
            if ($token->type === Token::TYPE_DELIMITER) {
135 7
                break;
136
            }
137
138
            // Skipping whitespaces and comments.
139 36
            if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
140 33
                continue;
141
            }
142
143 36
            if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
144 17
                if (($state === 0) && ($token->value === 'OUTFILE')) {
145 10
                    $ret->type = 'OUTFILE';
146 10
                    $state = 2;
147 10
                    continue;
148
                }
149
150
                // No other keyword is expected except for $state = 4, which expects `LINES`
151 12
                if ($state !== 4) {
152 11
                    break;
153
                }
154 4
            }
155
156 35
            if ($state === 0) {
157 26
                if ((isset($options['fromInsert'])
158 26
                    && $options['fromInsert'])
159 14
                    || (isset($options['fromReplace'])
160 14
                    && $options['fromReplace'])
161 26
                ) {
162 24
                    $ret->dest = Expression::parse(
163 24
                        $parser,
164 24
                        $list,
165
                        array(
166 24
                            'parseField' => 'table',
167 24
                            'breakOnAlias' => true,
168
                        )
169 24
                    );
170 24
                } else {
171 2
                    $ret->values = ExpressionArray::parse($parser, $list);
0 ignored issues
show
Documentation Bug introduced by
It seems like \PhpMyAdmin\SqlParser\Co...::parse($parser, $list) of type array<integer,object<Php...Components\Expression>> is incompatible with the declared type object<PhpMyAdmin\SqlPar...onents\ExpressionArray> of property $values.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

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

Loading history...
172
                }
173 26
                $state = 1;
174 35
            } elseif ($state === 1) {
175 18
                if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
176 16
                    $ret->columns = ArrayObj::parse($parser, $list)->values;
177 16
                    ++$list->idx;
178 16
                }
179 18
                break;
180 9
            } elseif ($state === 2) {
181 9
                $ret->dest = $token->value;
182
183 9
                $state = 3;
184 9
            } elseif ($state == 3) {
185 4
                $ret->parseFileOptions($parser, $list, $token->value);
186 4
                $state = 4;
187 4
            } elseif ($state == 4) {
188 4
                if ($token->type === Token::TYPE_KEYWORD && $token->value !== 'LINES') {
189 1
                    break;
190
                }
191
192 3
                $ret->parseFileOptions($parser, $list, $token->value);
193 3
                $state = 5;
194 3
            }
195 35
        }
196
197 36
        --$list->idx;
198
199 36
        return $ret;
200
    }
201
202 4
    public function parseFileOptions(Parser $parser, TokensList $list, $keyword = 'FIELDS')
203
    {
204 4
        ++$list->idx;
205
206 4
        if ($keyword === 'FIELDS' || $keyword === 'COLUMNS') {
207
            // parse field options
208 4
            $this->fields_options = OptionsArray::parse(
209 4
                $parser,
210 4
                $list,
211
                static::$FIELDS_OPTIONS
212 4
            );
213
214 4
            if ($keyword === 'FIELDS') {
215 1
                $this->fields_keyword = true;
216 1
            } else {
217 3
                $this->fields_keyword = false;
218
            }
219 4
        } else {
220
            // parse line options
221 3
            $this->lines_options = OptionsArray::parse(
222 3
                $parser,
223 3
                $list,
224
                static::$LINES_OPTIONS
225 3
            );
226
        }
227 4
    }
228
229
    /**
230
     * @param IntoKeyword $component the component to be built
231
     * @param array       $options   parameters for building
232
     *
233
     * @return string
234
     */
235 8
    public static function build($component, array $options = array())
236
    {
237 8
        if ($component->dest instanceof Expression) {
238 4
            $columns = !empty($component->columns) ?
239 4
                '(`' . implode('`, `', $component->columns) . '`)' : '';
240
241 4
            return $component->dest . $columns;
242 4
        } elseif (isset($component->values)) {
243 2
            return ExpressionArray::build($component->values);
0 ignored issues
show
Documentation introduced by
$component->values is of type object<PhpMyAdmin\SqlPar...onents\ExpressionArray>, but the function expects a array<integer,object<Php...Components\Expression>>.

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...
244
        } else {
245 2
            $ret = 'OUTFILE "' . $component->dest . '"';
246
247 2
            $fields_options_str = OptionsArray::build($component->fields_options);
248 2
            if (trim($fields_options_str) !== '') {
249 1
                $ret .= ($component->fields_keyword) ? ' FIELDS' : ' COLUMNS';
250 1
                $ret .= ' ' . $fields_options_str;
251 1
            }
252
253 2
            $lines_options_str = OptionsArray::build($component->lines_options, array('expr' => true));
254 2
            if (trim($lines_options_str) !== '') {
255 1
                $ret .= ' LINES ' . $lines_options_str;
256 1
            }
257
258 2
            return $ret;
259
        }
260
    }
261
}
262