GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 192451...617de4 )
by Burhan
11s
created

IndexDefinition::parseReferenceOption()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 2
crap 3
1
<?php
2
namespace Graze\Morphism\Parse;
3
4
use LogicException;
5
use RuntimeException;
6
7
/**
8
 * Represents the definition of an index.
9
 */
10
class IndexDefinition
11
{
12
    /** @var string|null */
13
    public $constraint = null;
14
15
    /** @var string */
16
    public $type = '';
17
18
    /** @var string|null */
19
    public $name = null;
20
21
    /**
22
     * @var array[] the columns which are indexed.
23
     *
24
     * Each entry is an array of the form:
25
     *
26
     * 'name'   => string
27
     * 'length' => int|null
28
     * 'sort'   => 'ASC'|'DESC'
29
     */
30
    public $columns = [];
31
32
    /** @var mixed[] Associative array of index options
33
     *
34
     * 'USING'          => string 'BTREE' | 'HASH'
35
     * 'KEY_BLOCK_SIZE' => integer
36
     * 'WITH PARSER'    => string;
37
     * 'COMMENT'        => string
38
     */
39
    public $options = [];
40
41
    /**
42
     * @var string[] Only present for FOREIGN KEYS
43
     *
44
     * 'table'     => string name of foreign table
45
     * 'columns'   => array('name' => string, 'length' => int|null)[] columns referenced in foreign table
46
     * 'ON DELETE' => string 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'NO ACTION'
47
     * 'ON UPDATE' => string 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'NO ACTION'
48
     */
49
    public $reference = [];
50
51
    /**
52
     * Parses an index definition from $stream
53
     *
54
     * The type of key (PRIMARY KEY, UNIQUE KEY, etc) should already have
55
     * been parsed from the stream. If the optional preceding CONSTRAINT clause
56
     * was parsed, you should supply its optional name in $constraint.
57
     *
58
     * @param TokenStream $stream
59
     * @param string $type 'PRIMARY KEY' | 'UNIQUE KEY' | 'KEY' | 'FULLTEXT KEY' | 'FOREIGN KEY'
60
     * @param string|null $constraint name supplied in optional CONSTRAINT clause
61
     */
62 82
    public function parse(TokenStream $stream, $type, $constraint = null)
63
    {
64 82
        $this->type = $type;
65
66
        switch ($type) {
67 82
            case 'PRIMARY KEY':
68 12
                $this->parseOptionalIndexType($stream);
69 12
                $this->parseIndexColumns($stream);
70 12
                $this->parseIndexOptions($stream);
71 12
                break;
72
73 71
            case 'UNIQUE KEY':
74 71
            case 'KEY':
75 71
            case 'FULLTEXT KEY':
76 53
                $this->parseOptionalIndexType($stream);
77 52
                if (!isset($this->options['USING'])) {
78 51
                    $this->parseOptionalIndexName($stream);
79 51
                    $this->parseOptionalIndexType($stream);
80 51
                }
81 52
                if (!is_null($constraint)) {
82 2
                    $this->name = $constraint;
83 2
                }
84 52
                $this->parseIndexColumns($stream);
85 52
                $this->parseIndexOptions($stream);
86 52
                break;
87
88 23
            case 'FOREIGN KEY':
89 22
                $this->constraint = $constraint;
90 22
                $this->parseOptionalIndexName($stream);
91 22
                $this->parseIndexColumns($stream);
92 22
                $this->parseReferenceDefinition($stream);
93 20
                break;
94
95 1
            default:
96 1
                throw new LogicException("Internal error - unknown index type '$type'");
97 1
        }
98 78
    }
99
100
    /**
101
     * @param TokenStream $stream
102
     */
103 68
    private function parseOptionalIndexName(TokenStream $stream)
104
    {
105 68
        $mark = $stream->getMark();
106 68
        $token = $stream->nextToken();
107 68
        if ($token->type === 'identifier') {
108 11
            $this->name = $token->text;
109 11
        } else {
110 62
            $stream->rewind($mark);
111
        }
112 68
    }
113
114
    /**
115
     * @param TokenStream $stream
116
     */
117 64
    private function parseOptionalIndexType(TokenStream $stream)
118
    {
119 64
        if ($stream->consume('USING')) {
120 3
            $this->parseIndexType($stream);
121 2
        }
122 63
    }
123
124
    /**
125
     * @param TokenStream $stream
126
     */
127 6
    private function parseIndexType(TokenStream $stream)
128
    {
129 6
        if ($stream->consume('BTREE')) {
130 4
            $using = 'BTREE';
131 6
        } elseif ($stream->consume('HASH')) {
132 1
            $using = 'HASH';
133 1
        } else {
134 1
            throw new RuntimeException("Expected BTREE or HASH");
135
        }
136 5
        $this->options['USING'] = $using;
137 5
    }
138
139
    /**
140
     * @param TokenStream $stream
141
     */
142 80
    private function parseIndexColumns(TokenStream $stream)
143
    {
144 80
        $this->columns = $this->expectIndexColumns($stream);
145 80
    }
146
147
    /**
148
     * @param TokenStream $stream
149
     * @return array
150
     */
151 80
    private function expectIndexColumns(TokenStream $stream)
152
    {
153 80
        $columns = [];
154 80
        $stream->expect('symbol', '(');
155 80
        while (true) {
156
            $column = [
157 80
                'name'   => $stream->expectName(),
158 80
                'length' => null,
159 80
                'sort'   => 'ASC',
160 80
            ];
161 80
            if ($stream->consume([['symbol', '(']])) {
162 5
                $column['length'] = $stream->expectNumber();
163 5
                $stream->expect('symbol', ')');
164 5
            }
165 80
            if ($stream->consume('ASC')) {
166 1
                $column['sort'] = 'ASC';
167 80
            } elseif ($stream->consume('DESC')) {
168 1
                $column['sort'] = 'DESC';
169 1
            }
170 80
            $columns[] = $column;
171 80
            if (!$stream->consume([['symbol', ',']])) {
172 80
                break;
173
            }
174 14
        }
175
176 80
        $stream->expect('symbol', ')');
177
178 80
        return $columns;
179
    }
180
181
    /**
182
     * @param TokenStream $stream
183
     */
184 63
    private function parseIndexOptions(TokenStream $stream)
185
    {
186 63
        while (true) {
187 63
            if ($stream->consume('KEY_BLOCK_SIZE')) {
188 4
                $stream->consume([['symbol', '=']]);
189 4
                $this->options['KEY_BLOCK_SIZE'] = $stream->expectNumber();
190 63
            } elseif ($stream->consume('WITH PARSER')) {
191 1
                $this->options['WITH PARSER'] = $stream->expectName();
192 63
            } elseif ($stream->consume('COMMENT')) {
193 3
                $this->options['COMMENT'] = $stream->expectString();
194 63
            } elseif ($stream->consume('USING')) {
195 3
                $this->parseIndexType($stream);
196 3
            } else {
197 63
                break;
198
            }
199 9
        }
200 63
    }
201
202
    /**
203
     * @param TokenStream $stream
204
     */
205 22
    private function parseReferenceDefinition(TokenStream $stream)
206
    {
207 22
        $stream->expect('identifier', 'REFERENCES');
208
209 22
        $tableOrSchema = $stream->expectName();
210 22
        if ($stream->consume([['symbol', '.']])) {
211 1
            $schema = $tableOrSchema;
212 1
            $table = $stream->expectName();
213 1
        } else {
214 21
            $schema = null;
215 21
            $table = $tableOrSchema;
216
        }
217
218 22
        $this->reference['schema'] = $schema;
219 22
        $this->reference['table'] = $table;
220 22
        $this->reference['columns'] = $this->expectIndexColumns($stream);
221 22
        $this->reference['ON DELETE'] = 'RESTRICT';
222 22
        $this->reference['ON UPDATE'] = 'RESTRICT';
223
224 22
        while (true) {
225 22
            if ($stream->consume('MATCH')) {
226 1
                throw new RuntimeException("MATCH clause is not supported in this tool, or in MySQL itself!");
227 21
            } elseif ($stream->consume('ON DELETE')) {
228 5
                $this->parseReferenceOption($stream, 'ON DELETE');
229 21
            } elseif ($stream->consume('ON UPDATE')) {
230 6
                $this->parseReferenceOption($stream, 'ON UPDATE');
231 5
            } else {
232 20
                break;
233
            }
234 9
        }
235 20
    }
236
237
    /**
238
     * @param TokenStream $stream
239
     * @param string $clause
240
     */
241 10
    private function parseReferenceOption(TokenStream $stream, $clause)
242
    {
243 10
        $availableOptions = ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION'];
244 10
        foreach ($availableOptions as $option) {
245 10
            if ($stream->consume($option)) {
246 9
                $this->reference[$clause] = $option;
247 9
                return;
248
            }
249 8
        }
250 1
        throw new RuntimeException("Expected one of: " . implode(", ", $availableOptions));
251
    }
252
253
    /**
254
     * Returns an array of all sequences of columns covered by this index. Includes optional lengths.
255
     *
256
     * E.g. the index ```KEY idx (x,y(12),z)``` will give ```[['x', 'y(12)', 'z'], ['x', 'y(12)'], ['x']]```.
257
     *
258
     * @return string[][]
259
     */
260 38
    public function getCovers()
261
    {
262
        // TODO - we should really have separate IndexPart objects so we can
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
263
        // correctly cope with pathological cases like:
264
        //
265
        //      CREATE TABLE evil (
266
        //          `bad(16)` varchar(32) not null,
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
267
        //          KEY (`bad(16)`(8))
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
268
        //      )
269
        //
270 38
        $covers = [];
271 38
        $cover = [];
272 38 View Code Duplication
        foreach ($this->columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
273 38
            $name = $column['name'];
274 38
            if ($column['length']) {
275 1
                $name .= '(' . $column['length'] . ')';
276 1
            }
277 38
            $cover[] = $name;
278 38
            $covers[] = $cover;
279 38
        }
280 38
        return $covers;
281
    }
282
283
    /**
284
     * Returns the sequence of indexed columns, including optional lengths.
285
     *
286
     * E.g. the index ```KEY idx (x, y(12))``` will give ```['x', 'y(12)']```
287
     *
288
     * @return string[]
289
     */
290 12
    public function getColumns()
291
    {
292 12
        $columns = [];
293 12 View Code Duplication
        foreach ($this->columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
294 12
            $name = $column['name'];
295 12
            if ($column['length']) {
296 1
                $name .= '(' . $column['length'] . ')';
297 1
            }
298 12
            $columns[] = $name;
299 12
        }
300 12
        return $columns;
301
    }
302
303
    /**
304
     * Returns an SQL fragment for declaring this index as part of a table definition.
305
     *
306
     * @return string
307
     */
308 71
    public function toString()
309
    {
310 71
        $line = '';
311 71
        if ($this->type === 'FOREIGN KEY') {
312 20
            $line = "CONSTRAINT " . Token::escapeIdentifier($this->constraint) . " ";
313 20
        }
314 71
        $line .= $this->type;
315 71
        if (!in_array($this->type, ['PRIMARY KEY', 'FOREIGN KEY'])) {
316 44
            $line .= " " . Token::escapeIdentifier($this->name);
317 44
        }
318 71
        $cols = [];
319 71 View Code Duplication
        foreach ($this->columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
320 71
            $col = Token::escapeIdentifier($column['name']);
321 71
            if (!is_null($column['length'])) {
322 2
                $col .= "(" . $column['length'] . ")";
323 2
            }
324 71
            $cols[] = $col;
325 71
        }
326 71
        $line .= " (" . implode(',', $cols) . ")";
327
328 71
        if (isset($this->options['USING'])) {
329 5
            $line .= " USING " . $this->options['USING'];
330 5
        }
331 71
        if (isset($this->options['KEY_BLOCK_SIZE']) && $this->options['KEY_BLOCK_SIZE'] !== 0) {
332 3
            $line .= " KEY_BLOCK_SIZE=" . $this->options['KEY_BLOCK_SIZE'];
333 3
        }
334 71
        if (isset($this->options['COMMENT']) && $this->options['COMMENT'] !== '') {
335 2
            $line .= " COMMENT " . Token::escapeString($this->options['COMMENT']);
336 2
        }
337 71
        if (isset($this->options['WITH PARSER'])) {
338 1
            $line .= " WITH PARSER " . $this->options['WITH PARSER'];
339 1
        }
340
341 71
        if ($this->type === 'FOREIGN KEY') {
342 20
            $reference = Token::escapeIdentifier($this->reference['table']);
343 20
            if (!is_null($this->reference['schema'])) {
344 1
                $reference = Token::escapeIdentifier($this->reference['schema']) . '.' . $reference;
345 1
            }
346 20
            $line .= " REFERENCES $reference";
347 20
            $cols = [];
348 20 View Code Duplication
            foreach ($this->reference['columns'] as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
349 20
                $col = Token::escapeIdentifier($column['name']);
350 20
                if (!is_null($column['length'])) {
351 1
                    $col .= "(" . $column['length'] . ")";
352 1
                }
353 20
                $cols[] = $col;
354 20
            }
355 20
            $line .= " (" . implode(',', $cols) . ")";
356 20
            foreach (['ON DELETE', 'ON UPDATE'] as $clause) {
357 20
                $action = $this->reference[$clause];
358 20
                if ($action !== 'RESTRICT') {
359 7
                    $line .= " $clause $action";
360 7
                }
361 20
            }
362 20
        }
363 71
        return $line;
364
    }
365
}
366