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 Burhan
02:28
created

TableOptions::parseOption()   D

Complexity

Conditions 26
Paths 48

Size

Total Lines 59
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 46
CRAP Score 26

Importance

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

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
                ) {
83 1
                    throw new \RuntimeException("Expected CHARSET, CHARACTER SET or COLLATE");
84
                }
85
            }
86
87 101
            $this->parseOption($stream, strtoupper($token->text));
88
        }
89
90 162
        if (!$this->collation->isSpecified()) {
91 154
            $this->collation = clone $this->defaultCollation;
92
        }
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 92
            case 'COLLATE':
104 86
            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 72
            case 'AVG_ROW_LENGTH':
115 67
            case 'KEY_BLOCK_SIZE':
116 62
            case 'MAX_ROWS':
117 57
            case 'MIN_ROWS':
118 25
                $this->parseNumber($stream, $option);
119 25
                break;
120
121 52
            case 'CHECKSUM':
122 47
            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 35
            case 'INDEX':
132 2
                $stream->expect(Token::IDENTIFIER, 'DIRECTORY');
133
                // fall through //
134 34
            case 'COMMENT':
135 28
            case 'CONNECTION':
136 22
            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 6
            case 'STATS_AUTO_RECALC':
150 5
            case 'STATS_PERSISTENT':
151 4
            case 'STATS_SAMPLE_PAGES':
152 3
            case 'TABLESPACE':
153 2
            case 'UNION':
154 6
                throw new \RuntimeException("$option is not currently supported by this tool");
155
156
            default:
157 1
                throw new \RuntimeException("Unknown table option: $option");
158
        }
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
            default:
174 3
                return $engine;
175
        }
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
                } 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
                } else {
201 5
                    $this->collation->setCollation($value);
202
                }
203 6
                break;
204
205
            default:
206 68
                $this->options[$option] = $value;
207 68
                break;
208
        }
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
            }
285
        }
286
287
        foreach ([
288 98
            'MIN_ROWS',
289
            'MAX_ROWS',
290
            'AVG_ROW_LENGTH',
291
            'PACK_KEYS',
292
            'CHECKSUM',
293
            'DELAY_KEY_WRITE',
294
            'ROW_FORMAT',
295
            'KEY_BLOCK_SIZE',
296
            'COMMENT',
297
            'CONNECTION',
298
        ] 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
                }
304 98
                $items[] = "$option=$value";
305
            }
306
        }
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
            }
336
        }
337
338 64
        $thisCollation = $this->collation->isSpecified()
339 3
            ? $this->collation->getCollation()
340 64
            : null;
341 64
        $thatCollation = $that->collation->isSpecified()
342 4
            ? $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
                }
351
            }
352
        }
353
354
        foreach ([
355 64
            'MIN_ROWS',
356
            'MAX_ROWS',
357
            'AVG_ROW_LENGTH',
358
            'PACK_KEYS',
359
            'CHECKSUM',
360
            '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
            'KEY_BLOCK_SIZE',
369
            'COMMENT',
370
            'CONNECTION',
371
        ] 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
            }
378
379 64
            if ($thisValue !== $thatValue) {
380 64
                $alters[] = "$option=$thatValue";
381
            }
382
        }
383
384 64
        return implode(' ', $alters);
385
    }
386
}
387