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.
Passed
Pull Request — master (#49)
by Harry
02:30
created

TableOptions::parseOption()   D

Complexity

Conditions 26
Paths 48

Size

Total Lines 59
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 47
CRAP Score 26

Importance

Changes 0
Metric Value
cc 26
eloc 47
nc 48
nop 2
dl 0
loc 59
ccs 47
cts 47
cp 1
crap 26
rs 4.1666
c 0
b 0
f 0

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
namespace Graze\Morphism\Parse;
3
4
/**
5
 * Represents a set of table options - MIN_ROWS, PACK_KEYS, COMMENT, etc.
6
 */
7
class TableOptions
8
{
9
    /** @var string|null */
10
    public $engine = null;
11
12
    /** @var CollationInfo */
13
    public $collation = null;
14
15
    /**
16
     * @var array
17
     * maps option names to values (string|int|null)
18
     */
19
    public $options = [];
20
21
    /** @var string */
22
    private $defaultEngine = null;
23
    /** @var CollationInfo|null */
24
    private $defaultCollation = null;
25
    /** @var array */
26
    private $defaultOptions = [
27
        'AUTO_INCREMENT'  => null,
28
        'MIN_ROWS'        => 0,
29
        'MAX_ROWS'        => 0,
30
        'AVG_ROW_LENGTH'  => 0,
31
        'PACK_KEYS'       => 'DEFAULT',
32
        'CHECKSUM'        => '0',
33
        'DELAY_KEY_WRITE' => '0',
34
        'ROW_FORMAT'      => 'DEFAULT',
35
        'KEY_BLOCK_SIZE'  => 0,
36
        'COMMENT'         => '',
37
        'CONNECTION'      => '',
38
    ];
39
40
    /**
41
     * Constructor
42
     * @param CollationInfo $databaseCollation
43
     */
44 196
    public function __construct(CollationInfo $databaseCollation)
45
    {
46 196
        $this->collation = new CollationInfo;
47 196
        $this->defaultCollation = clone $databaseCollation;
48 196
    }
49
50
    /**
51
     * Set the default storage engine for the table to use in case the
52
     * ENGINE option is not supplied.
53
     *
54
     * @param string $engine e.g. 'InnoDB"
55
     */
56 194
    public function setDefaultEngine($engine)
57
    {
58 194
        $this->defaultEngine = self::normaliseEngine($engine);
59 194
    }
60
61
    /**
62
     * Parses table options from $stream.
63
     * @param TokenStream $stream
64
     */
65 177
    public function parse(TokenStream $stream)
66
    {
67 177
        $this->engine = $this->defaultEngine;
68 177
        $this->options = $this->defaultOptions;
69
70 177
        while (true) {
71 177
            $mark = $stream->getMark();
72 177
            $token = $stream->nextToken();
73 177
            if ($token->type !== Token::IDENTIFIER) {
74 162
                $stream->rewind($mark);
75 162
                break;
76
            }
77
78 102
            if ($token->eq(Token::IDENTIFIER, 'DEFAULT')) {
79 4
                $token = $stream->nextToken();
80 4
                if (!($token->type === Token::IDENTIFIER &&
81 4
                      in_array(strtoupper($token->text), ['CHARSET', 'CHARACTER', 'COLLATE']))
82 4
                ) {
83 1
                    throw new \RuntimeException("Expected CHARSET, CHARACTER SET or COLLATE");
84
                }
85 3
            }
86
87 101
            $this->parseOption($stream, strtoupper($token->text));
88 87
        }
89
90 162
        if (!$this->collation->isSpecified()) {
91 154
            $this->collation = clone $this->defaultCollation;
92 154
        }
93 162
    }
94
95
    /**
96
     * @param TokenStream $stream
97
     * @param string $option
98
     */
99 101
    private function parseOption(TokenStream $stream, $option)
100
    {
101
        switch ($option) {
102 101
            case 'ENGINE':
103 101
            case 'COLLATE':
104 101
            case 'CHARSET':
105 22
                $this->parseIdentifier($stream, $option);
106 20
                break;
107
108 79
            case 'CHARACTER':
109 2
                $stream->expect(Token::IDENTIFIER, 'SET');
110 2
                $this->parseIdentifier($stream, 'CHARSET');
111 2
                break;
112
113 77
            case 'AUTO_INCREMENT':
114 77
            case 'AVG_ROW_LENGTH':
115 77
            case 'KEY_BLOCK_SIZE':
116 77
            case 'MAX_ROWS':
117 77
            case 'MIN_ROWS':
118 25
                $this->parseNumber($stream, $option);
119 25
                break;
120
121 52
            case 'CHECKSUM':
122 52
            case 'DELAY_KEY_WRITE':
123 10
                $this->parseEnum($stream, $option, ['0', '1']);
124 10
                break;
125
126 42
            case 'PACK_KEYS':
127 6
                $this->parseEnum($stream, $option, ['DEFAULT', '0', '1']);
128 6
                break;
129
130 36
            case 'DATA':
131 36
            case 'INDEX':
132 2
                $stream->expect(Token::IDENTIFIER, 'DIRECTORY');
133
                // fall through //
134 36
            case 'COMMENT':
135 36
            case 'CONNECTION':
136 36
            case 'PASSWORD':
137 15
                $this->parseString($stream, $option);
138 15
                break;
139
140 21
            case 'INSERT_METHOD':
141 4
                $this->parseEnum($stream, $option, ['NO', 'FIRST', 'LAST']);
142 3
                throw new \RuntimeException("$option is not currently supported by this tool");
143
144 17
            case 'ROW_FORMAT':
145 10
                $this->parseEnum($stream, $option, ['DEFAULT', 'DYNAMIC', 'FIXED', 'COMPRESSED', 'REDUNDANT', 'COMPACT']);
146 9
                break;
147
148 7
            case 'PARTITION':
149 7
            case 'STATS_AUTO_RECALC':
150 7
            case 'STATS_PERSISTENT':
151 7
            case 'STATS_SAMPLE_PAGES':
152 7
            case 'TABLESPACE':
153 7
            case 'UNION':
154 6
                throw new \RuntimeException("$option is not currently supported by this tool");
155
156 1
            default:
157 1
                throw new \RuntimeException("Unknown table option: $option");
158 1
        }
159 87
    }
160
161
    /**
162
     * @param string $engine
163
     * @return string
164
     */
165 195
    private static function normaliseEngine($engine)
166
    {
167 195
        $engine = strtoupper($engine);
168
        switch ($engine) {
169 195
            case 'INNODB':
170 193
                return 'InnoDB';
171 9
            case 'MYISAM':
172 7
                return 'MyISAM';
173 3
            default:
174 3
                return $engine;
175 3
        }
176
    }
177
178
    /**
179
     * @param string $option
180
     * @param string $value
181
     */
182 90
    private function setOption($option, $value)
183
    {
184
        switch ($option) {
185 90
            case 'ENGINE':
186 7
                $this->engine = self::normaliseEngine($value);
187 7
                break;
188
189 83
            case 'CHARSET':
190 9
                if (strtoupper($value) === 'DEFAULT') {
191 2
                    $this->collation = new CollationInfo();
192 2
                } else {
193 8
                    $this->collation->setCharset($value);
194
                }
195 9
                break;
196
197 74
            case 'COLLATE':
198 6
                if (strtoupper($value) === 'DEFAULT') {
199 1
                    $this->collation = new CollationInfo();
200 1
                } else {
201 5
                    $this->collation->setCollation($value);
202
                }
203 6
                break;
204
205 68
            default:
206 68
                $this->options[$option] = $value;
207 68
                break;
208 68
        }
209 90
    }
210
211
    /**
212
     * @param TokenStream $stream
213
     * @param string $option
214
     */
215 24
    private function parseIdentifier(TokenStream $stream, $option)
216
    {
217 24
        $stream->consume([[Token::SYMBOL, '=']]);
218 24
        $token = $stream->nextToken();
219 24
        if ($token->isEof()) {
220 1
            throw new \RuntimeException("Unexpected end-of-file");
221
        }
222 23
        if (!in_array($token->type, [Token::IDENTIFIER, Token::STRING])) {
223 1
            throw new \RuntimeException("Bad table option value: '$token->text'");
224
        }
225 22
        $this->setOption($option, strtolower($token->text));
226 22
    }
227
228
    /**
229
     * @param TokenStream $stream
230
     * @param string $option
231
     */
232 25
    private function parseNumber(TokenStream $stream, $option)
233
    {
234 25
        $stream->consume([[Token::SYMBOL, '=']]);
235 25
        $this->setOption($option, $stream->expectNumber());
236 25
    }
237
238
    /**
239
     * @param TokenStream $stream
240
     * @param string $option
241
     * @param array $enums
242
     */
243 30
    private function parseEnum(TokenStream $stream, $option, array $enums)
244
    {
245 30
        $stream->consume([[Token::SYMBOL, '=']]);
246 30
        $token = $stream->nextToken();
247 30
        if (!in_array($token->type, [Token::IDENTIFIER, Token::NUMBER])) {
248 1
            throw new \RuntimeException("Bad table option value");
249
        }
250 29
        $value = strtoupper($token->text);
251 29
        if (!in_array($value, $enums)) {
252 1
            throw new \RuntimeException("Invalid option value, expected " . implode(' | ', $enums));
253
        }
254 28
        $this->setOption($option, $value);
255 28
    }
256
257
    /**
258
     * @param TokenStream $stream
259
     * @param string $option
260
     */
261 15
    private function parseString(TokenStream $stream, $option)
262
    {
263 15
        $stream->consume([[Token::SYMBOL, '=']]);
264 15
        $this->setOption($option, $stream->expectString());
265 15
    }
266
267
    /**
268
     * Returns an SQL fragment to set the options as part of a CREATE TABLE statement.
269
     * Note that the AUTO_INCREMENT option is explicitly *not* included in the output.
270
     */
271 98
    public function toString()
272
    {
273 98
        $items = [];
274
275 98
        $items[] = "ENGINE=" . $this->engine;
276
277
        // (omit AUTO_INCREMENT)
278
279 98
        $collation = $this->collation;
280 98
        if ($collation->isSpecified()) {
281 7
            $items[] = "DEFAULT CHARSET=" . $collation->getCharset();
282 7
            if (!$collation->isDefaultCollation()) {
283 2
                $items[] = "COLLATE=" . $collation->getCollation();
284 2
            }
285 7
        }
286
287
        foreach ([
288 98
            'MIN_ROWS',
289 98
            'MAX_ROWS',
290 98
            'AVG_ROW_LENGTH',
291 98
            'PACK_KEYS',
292 98
            'CHECKSUM',
293 98
            'DELAY_KEY_WRITE',
294 98
            'ROW_FORMAT',
295 98
            'KEY_BLOCK_SIZE',
296 98
            'COMMENT',
297 98
            'CONNECTION',
298 98
        ] as $option) {
299 98
            if ($this->options[$option] !== $this->defaultOptions[$option]) {
300 17
                $value = $this->options[$option];
301 17
                if (in_array($option, ['COMMENT', 'CONNECTION'])) {
302 4
                    $value = Token::escapeString($value);
303 4
                }
304 17
                $items[] = "$option=$value";
305 17
            }
306 98
        }
307
308 98
        return implode(' ', $items);
309
    }
310
311
    /**
312
     * Returns an SQL fragment to transform these table options into those
313
     * specified by $that as part of an ALTER TABLE statement.
314
     *
315
     * The empty string is returned if nothing needs to be done.
316
     *
317
     * $flags           |
318
     * :----------------|
319
     * 'alterEngine'    | (bool) include 'ALTER TABLE ... ENGINE=' [default: true]
320
     *
321
     * @param TableOptions $that
322
     * @param array $flags
323
     * @return string
324
     */
325 64
    public function diff(TableOptions $that, array $flags = [])
326
    {
327
        $flags += [
328 64
            'alterEngine' => true,
329
        ];
330
331 64
        $alters = [];
332 64
        if ($flags['alterEngine']) {
333 62
            if (strcasecmp($this->engine, $that->engine) !== 0) {
334 4
                $alters[] = "ENGINE=" . $that->engine;
335 4
            }
336 62
        }
337
338 64
        $thisCollation = $this->collation->isSpecified()
339 64
            ? $this->collation->getCollation()
340 64
            : null;
341 64
        $thatCollation = $that->collation->isSpecified()
342 64
            ? $that->collation->getCollation()
343 64
            : null;
344 64
        if ($thisCollation !== $thatCollation) {
345
            // TODO - what if !$that->collation->isSpecified()
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...
Unused Code Comprehensibility introduced by
42% 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...
346 6
            if (!is_null($thatCollation)) {
347 4
                $alters[] = "DEFAULT CHARSET=" . $that->collation->getCharset();
348 4
                if (!$that->collation->isDefaultCollation()) {
349 2
                    $alters[] = "COLLATE=" . $thatCollation;
350 2
                }
351 4
            }
352 6
        }
353
354
        foreach ([
355 64
            'MIN_ROWS',
356 64
            'MAX_ROWS',
357 64
            'AVG_ROW_LENGTH',
358 64
            'PACK_KEYS',
359 64
            'CHECKSUM',
360 64
            'DELAY_KEY_WRITE',
361
362
            // The storage engine may pick a different row format when
363
            // ROW_FORMAT=DEFAULT (or no ROW_FORMAT)/ is specified, depending
364
            // on whether any variable length columns are present. Since we
365
            // don't (currently) explicitly specify ROW_FORMAT in any of our
366
            // tables, I'm choosing to ignore it for the time being...
367
        //  'ROW_FORMAT',
368 64
            'KEY_BLOCK_SIZE',
369 64
            'COMMENT',
370 64
            'CONNECTION',
371 64
        ] as $option) {
372 64
            $thisValue = $this->options[$option];
373 64
            $thatValue = $that->options[$option];
374 64
            if (in_array($option, ['COMMENT', 'CONNECTION'])) {
375 64
                $thisValue = Token::escapeString($thisValue);
376 64
                $thatValue = Token::escapeString($thatValue);
377 64
            }
378
379 64
            if ($thisValue !== $thatValue) {
380 27
                $alters[] = "$option=$thatValue";
381 27
            }
382 64
        }
383
384 64
        return implode(' ', $alters);
385
    }
386
}
387