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
Push — master ( 42a542...6d81cd )
by Burhan
02:01
created

Diff::configure()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 86
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 65
nc 1
nop 0
dl 0
loc 86
rs 8.6583
c 0
b 0
f 0

How to fix   Long Method   

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
namespace Graze\Morphism\Command;
4
5
use Doctrine\DBAL\Connection;
6
use Exception;
7
use Graze\Morphism\Parse\TokenStream;
8
use Graze\Morphism\Parse\Token;
9
use Graze\Morphism\Parse\MysqlDump;
10
use Graze\Morphism\Extractor;
11
use Graze\Morphism\Config;
12
use InvalidArgumentException;
13
use RuntimeException;
14
use Symfony\Component\Console\Command\Command;
15
use Symfony\Component\Console\Input\InputArgument;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Input\InputOption;
18
use Symfony\Component\Console\Output\OutputInterface;
19
20
class Diff extends Command
21
{
22
    const COMMAND_NAME              = 'diff';
23
24
    // Command line arguments
25
    const ARGUMENT_CONFIG_FILE      = 'config-file';
26
    const ARGUMENT_CONNECTIONS      = 'connections';
27
28
    // Command line options
29
    const OPTION_ENGINE             = 'engine';
30
    const OPTION_COLLATION          = 'collation';
31
    const OPTION_APPLY_CHANGES      = 'apply-changes';
32
    const OPTION_LOG_DIR            = 'log-dir';
33
34
    const OPTION_QUOTE_NAMES        = 'quote-names';
35
    const OPTION_NO_QUOTE_NAMES     = 'no-quote-names';
36
    const OPTION_CREATE_TABLE       = 'create-table';
37
    const OPTION_NO_CREATE_TABLE    = 'no-create-table';
38
    const OPTION_DROP_TABLE         = 'drop-table';
39
    const OPTION_NO_DROP_TABLE      = 'no-drop-table';
40
    const OPTION_ALTER_ENGINE       = 'alter-engine';
41
    const OPTION_NO_ALTER_ENGINE    = 'no-alter-engine';
42
    const OPTION_LOG_SKIPPED        = 'log-skipped';
43
    const OPTION_NO_LOG_SKIPPED     = 'no-log-skipped';
44
45
    /** @var string */
46
    private $engine = 'InnoDB';
47
    /** @var string|null */
48
    private $collation = null;
49
    /** @var bool */
50
    private $quoteNames = true;
51
    /** @var bool */
52
    private $createTable = true;
53
    /** @var bool */
54
    private $dropTable = true;
55
    /** @var bool */
56
    private $alterEngine = true;
57
    /** @var string|null */
58
    private $configFile = null;
59
    /** @var array */
60
    private $connectionNames = [];
61
    /** @var string */
62
    private $applyChanges = 'no';
63
    /** @var string null */
64
    private $logDir = null;
65
    /** @var bool */
66
    private $logSkipped = true;
67
68
    protected function configure()
69
    {
70
        $this->setName(self::COMMAND_NAME);
71
72
        $helpText = sprintf(
73
            "Usage: %s [OPTION] CONFIG-FILE [CONN] ...\n" .
74
            "Extracts schema definitions from the named connections, and outputs the\n" .
75
            "necessary ALTER TABLE statements to transform them into what is defined\n" .
76
            "under the schema path. If no connections are specified, all connections\n" .
77
            "in the config with 'morphism: enable: true' will be used.\n" .
78
            "\n" .
79
            "GENERAL OPTIONS:\n" .
80
            "  -h, -help, --help      display this message, and exit\n" .
81
            "  --engine=ENGINE        set the default database engine\n" .
82
            "  --collation=COLLATION  set the default collation\n" .
83
            "  --[no-]quote-names     quote names with `...`; default: yes\n" .
84
            "  --[no-]create-table    output CREATE TABLE statements; default: yes\n" .
85
            "  --[no-]drop-table      output DROP TABLE statements; default: yes\n" .
86
            "  --[no-]alter-engine    output ALTER TABLE ... ENGINE=...; default: yes\n" .
87
            "  --apply-changes=WHEN   apply changes (yes/no/confirm); default: no\n" .
88
            "  --log-dir=DIR          log applied changes to DIR - one log file will be\n" .
89
            "                         created per connection; default: none\n" .
90
            "  --[no-]log-skipped     log skipped queries (commented out); default: yes\n" .
91
            "\n" .
92
            "CONFIG-FILE\n" .
93
            "A YAML file mapping connection names to parameters. See the morphism project's\n" .
94
            "README.md file for detailed information.\n" .
95
            "",
96
            self::COMMAND_NAME
97
        );
98
        $this->setHelp($helpText);
99
100
        $this->addArgument(
101
            self::ARGUMENT_CONFIG_FILE,
102
            InputArgument::REQUIRED
103
        );
104
105
        $this->addArgument(
106
            self::ARGUMENT_CONNECTIONS,
107
            InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
108
            null,
109
            []
110
        );
111
112
        $this->addOption(
113
            self::OPTION_ENGINE,
114
            null,
115
            InputOption::VALUE_REQUIRED,
116
            'Database engine',
117
            'InnoDB'
118
        );
119
        $this->addOption(
120
            self::OPTION_COLLATION,
121
            null,
122
            InputOption::VALUE_REQUIRED,
123
            'Database collation'
124
        );
125
126
        $this->addOption(self::OPTION_QUOTE_NAMES);
127
        $this->addOption(self::OPTION_NO_QUOTE_NAMES);
128
129
        $this->addOption(self::OPTION_CREATE_TABLE);
130
        $this->addOption(self::OPTION_NO_CREATE_TABLE);
131
132
        $this->addOption(self::OPTION_DROP_TABLE);
133
        $this->addOption(self::OPTION_NO_DROP_TABLE);
134
135
        $this->addOption(self::OPTION_ALTER_ENGINE);
136
        $this->addOption(self::OPTION_NO_ALTER_ENGINE);
137
138
        $this->addOption(
139
            self::OPTION_APPLY_CHANGES,
140
            null,
141
            InputOption::VALUE_REQUIRED,
142
            null,
143
            "no"
144
        );
145
146
        $this->addOption(
147
            self::OPTION_LOG_DIR,
148
            null,
149
            InputOption::VALUE_REQUIRED
150
        );
151
152
        $this->addOption(self::OPTION_LOG_SKIPPED);
153
        $this->addOption(self::OPTION_NO_LOG_SKIPPED);
154
    }
155
156
    /**
157
     * @param Connection $connection
158
     * @param string $dbName
159
     * @return MysqlDump
160
     */
161
    private function getCurrentSchema(Connection $connection, $dbName)
162
    {
163
        $extractor = new Extractor($connection);
164
        $extractor->setDatabases([$dbName]);
165
        $extractor->setCreateDatabases(false);
166
        $extractor->setQuoteNames($this->quoteNames);
167
168
        $text = '';
169
        foreach ($extractor->extract() as $query) {
170
            $text .= "$query;\n";
171
        }
172
        $stream = TokenStream::newFromText($text, '');
173
174
        $dump = new MysqlDump();
175
        $dump->setDefaultDatabase($dbName);
176
        $dump->parse($stream);
177
178
        return $dump;
179
    }
180
181
    /**
182
     * @param string $schemaDefinitionPath
183
     * @param string $dbName
184
     *
185
     * @return MySqlDump
186
     */
187
    private function getTargetSchema($schemaDefinitionPath, $dbName)
188
    {
189
        return MysqlDump::parseFromPaths(
190
            [$schemaDefinitionPath],
191
            $this->engine,
192
            $this->collation,
193
            $dbName
194
        );
195
    }
196
197
    /**
198
     * @param Connection $connection
199
     * @param string $connectionName
200
     * @param array $diff
201
     * @throws Exception
202
     */
203
    private function applyChanges(Connection $connection, $connectionName, array $diff)
204
    {
205
        if (count($diff) == 0) {
206
            return;
207
        }
208
        if ($this->applyChanges == 'no') {
209
            return;
210
        }
211
212
        $confirm = $this->applyChanges == 'confirm';
213
        $defaultResponse = 'y';
214
        $logHandle = null;
215
216
        if (!is_null($this->logDir)) {
0 ignored issues
show
introduced by
The condition is_null($this->logDir) is always false.
Loading history...
217
            $logFile = "{$this->logDir}/{$connectionName}.sql";
218
            $logHandle = fopen($logFile, "w");
219
            if ($logHandle == false) {
220
                fprintf(STDERR, "Could not open log file for writing: $logFile\n");
221
                exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
222
            }
223
        }
224
225
        if (count($diff) > 0 && $confirm) {
226
            echo "\n";
227
            echo "-- Confirm changes to $connectionName:\n";
228
        }
229
230
        foreach ($diff as $query) {
231
            $response = $defaultResponse;
232
            $apply = false;
233
234
            if ($confirm) {
235
                echo "\n";
236
                echo "$query;\n\n";
237
                do {
238
                    echo "-- Apply this change? [y]es [n]o [a]ll [q]uit: ";
239
                    $response = fgets(STDIN);
240
                    if ($response === false) {
241
                        throw new Exception("Could not read response");
242
                    }
243
                    $response = rtrim($response);
244
                } while (!in_array($response, ['y', 'n', 'a', 'q']));
245
            }
246
247
            switch ($response) {
248
                case 'y':
249
                    $apply = true;
250
                    break;
251
252
                case 'n':
253
                    $apply = false;
254
                    break;
255
256
                case 'a':
257
                    $apply = true;
258
                    $confirm = false;
259
                    $defaultResponse = 'y';
260
                    break;
261
262
                case 'q':
263
                    $apply = false;
264
                    $confirm = false;
265
                    $defaultResponse = 'n';
266
                    break;
267
            }
268
269
            if ($apply) {
270
                if ($logHandle) {
271
                    fwrite($logHandle, "$query;\n\n");
272
                }
273
                $connection->executeQuery($query);
274
            } elseif ($logHandle && $this->logSkipped) {
275
                fwrite(
276
                    $logHandle,
277
                    "-- [SKIPPED]\n" .
278
                    preg_replace('/^/xms', '-- ', $query) .  ";\n" .
279
                    "\n"
280
                );
281
            }
282
        }
283
    }
284
285
    /**
286
     * @param InputInterface $input
287
     * @param OutputInterface $output
288
     * @throws Exception
289
     */
290
    protected function execute(InputInterface $input, OutputInterface $output)
291
    {
292
        $this->configFile = $input->getArgument(self::ARGUMENT_CONFIG_FILE);
293
        $this->connectionNames = $input->getArgument(self::ARGUMENT_CONNECTIONS);
294
295
        if ($input->getOption(self::OPTION_NO_QUOTE_NAMES)) {
296
            $this->quoteNames = false;
297
        }
298
299
        if ($input->getOption(self::OPTION_NO_CREATE_TABLE)) {
300
            $this->createTable = false;
301
        }
302
303
        if ($input->getOption(self::OPTION_NO_DROP_TABLE)) {
304
            $this->dropTable = false;
305
        }
306
307
        $this->applyChanges = $input->getOption(self::OPTION_APPLY_CHANGES);
308
        if (!in_array($this->applyChanges, ['yes', 'no', 'confirm'])) {
309
            throw new InvalidArgumentException(sprintf(
310
                "Unknown value for --%s: %s",
311
                self::OPTION_APPLY_CHANGES,
312
                $this->applyChanges
313
            ));
314
        }
315
316
        if ($input->getOption(self::OPTION_NO_LOG_SKIPPED)) {
317
            $this->logSkipped = false;
318
        }
319
320
        try {
321
            $config = new Config($this->configFile);
322
            $config->parse();
323
324
            $connectionNames =
325
                (count($this->connectionNames) > 0)
326
                    ? $this->connectionNames
327
                    : $config->getConnectionNames();
328
329
            Token::setQuoteNames($this->quoteNames);
330
331
            if (!is_null($this->logDir)) {
0 ignored issues
show
introduced by
The condition is_null($this->logDir) is always false.
Loading history...
332
                if (!is_dir($this->logDir)) {
333
                    if (!@mkdir($this->logDir, 0755, true)) {
334
                        fprintf(STDERR, "Could not create log directory: {$this->logDir}\n");
335
                        exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
336
                    }
337
                }
338
            }
339
340
            foreach ($connectionNames as $connectionName) {
341
                echo "-- --------------------------------\n";
342
                echo "--   Connection: $connectionName\n";
343
                echo "-- --------------------------------\n";
344
                $connection = $config->getConnection($connectionName);
345
                $entry = $config->getEntry($connectionName);
346
                $dbName = $entry['connection']['dbname'];
347
                $schemaDefinitionPath = $entry['morphism']['schemaDefinitionPath'];
348
                $matchTables = [
349
                    $dbName => $entry['morphism']['matchTables'],
350
                ];
351
352
                $currentSchema = $this->getCurrentSchema($connection, $dbName);
353
                $targetSchema = $this->getTargetSchema($schemaDefinitionPath, $dbName);
354
355
                $diff = $currentSchema->diff(
356
                    $targetSchema,
357
                    [
358
                        'createTable'    => $this->createTable,
359
                        'dropTable'      => $this->dropTable,
360
                        'alterEngine'    => $this->alterEngine,
361
                        'matchTables'    => $matchTables,
362
                    ]
363
                );
364
365
                $statements = array_reduce($diff, function ($acc, $item) {
366
                    if (is_array($item)) {
367
                        foreach ($item as $statement) {
368
                            $acc[] = $statement;
369
                        }
370
                    } else {
371
                        $acc[] = $item;
372
                    }
373
374
                    return $acc;
375
                }, []);
376
377
                foreach ($statements as $query) {
378
                    echo "$query;\n\n";
379
                }
380
381
                $this->applyChanges($connection, $connectionName, $statements);
382
            }
383
        } catch (RuntimeException $e) {
384
            throw $e;
385
        } catch (Exception $e) {
386
            throw new Exception($e->getMessage() . "\n\n" . $e->getTraceAsString());
387
        }
388
    }
389
}
390