Completed
Push — develop ( 21139f...eaa140 )
by Tom
12s
created

DumpCommand::execute()   F

Complexity

Conditions 22
Paths 10368

Size

Total Lines 104
Code Lines 61

Duplication

Lines 22
Ratio 21.15 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 22
loc 104
rs 2
cc 22
eloc 61
nc 10368
nop 2

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
3
namespace N98\Magento\Command\Database;
4
5
use N98\Magento\Command\Database\Compressor\Compressor;
6
use N98\Util\Console\Enabler;
7
use N98\Util\Console\Helper\DatabaseHelper;
8
use N98\Util\Exec;
9
use N98\Util\VerifyOrDie;
10
use RuntimeException;
11
use Symfony\Component\Console\Helper\DialogHelper;
12
use Symfony\Component\Console\Input\InputArgument;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Output\OutputInterface;
16
17
class DumpCommand extends AbstractDatabaseCommand
18
{
19
    /**
20
     * @var array
21
     */
22
    protected $tableDefinitions = null;
23
24
    /**
25
     * @var array
26
     */
27
    protected $commandConfig = null;
28
29
    protected function configure()
30
    {
31
        $this
32
            ->setName('db:dump')
33
            ->addArgument('filename', InputArgument::OPTIONAL, 'Dump filename')
34
            ->addOption(
35
                'add-time',
36
                't',
37
                InputOption::VALUE_OPTIONAL,
38
                'Adds time to filename (only if filename was not provided)'
39
            )
40
            ->addOption(
41
                'compression',
42
                'c',
43
                InputOption::VALUE_REQUIRED,
44
                'Compress the dump file using one of the supported algorithms'
45
            )
46
            ->addOption(
47
                'xml',
48
                null,
49
                InputOption::VALUE_NONE,
50
                'Dump database in xml format'
51
            )
52
            ->addOption(
53
                'hex-blob',
54
                null,
55
                InputOption::VALUE_NONE,
56
                'Dump binary columns using hexadecimal notation (for example, "abc" becomes 0x616263)'
57
            )
58
            ->addOption(
59
                'only-command',
60
                null,
61
                InputOption::VALUE_NONE,
62
                'Print only mysqldump command. Do not execute'
63
            )
64
            ->addOption(
65
                'print-only-filename',
66
                null,
67
                InputOption::VALUE_NONE,
68
                'Execute and prints no output except the dump filename'
69
            )
70
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'do everything but the dump')
71
            ->addOption(
72
                'no-single-transaction',
73
                null,
74
                InputOption::VALUE_NONE,
75
                'Do not use single-transaction (not recommended, this is blocking)'
76
            )
77
            ->addOption(
78
                'human-readable',
79
                null,
80
                InputOption::VALUE_NONE,
81
                'Use a single insert with column names per row. Useful to track database differences. Use db:import ' .
82
                '--optimize for speeding up the import.'
83
            )
84
            ->addOption(
85
                'add-routines',
86
                null,
87
                InputOption::VALUE_NONE,
88
                'Include stored routines in dump (procedures & functions)'
89
            )
90
            ->addOption('stdout', null, InputOption::VALUE_NONE, 'Dump to stdout')
91
            ->addOption(
92
                'strip',
93
                's',
94
                InputOption::VALUE_OPTIONAL,
95
                'Tables to strip (dump only structure of those tables)'
96
            )
97
            ->addOption('exclude', 'e', InputOption::VALUE_OPTIONAL, 'Tables to exclude from the dump')
98
            ->addOption('force', 'f', InputOption::VALUE_NONE, 'Do not prompt if all options are defined')
99
            ->setDescription('Dumps database with mysqldump cli client according to informations from local.xml');
100
101
        $help = <<<HELP
102
Dumps configured magento database with `mysqldump`. You must have installed
103
the MySQL client tools.
104
105
On debian systems run `apt-get install mysql-client` to do that.
106
107
The command reads app/etc/local.xml to find the correct settings.
108
109
See it in action: http://youtu.be/ttjZHY6vThs
110
111
- If you like to prepend a timestamp to the dump name the --add-time option
112
  can be used.
113
114
- The command comes with a compression function. Add i.e. `--compression=gz`
115
  to dump directly in gzip compressed file.
116
117
HELP;
118
        $this->setHelp($help);
119
    }
120
121
    /**
122
     * @return array
123
     *
124
     * @deprecated Use database helper
125
     * @throws RuntimeException
126
     */
127
    public function getTableDefinitions()
128
    {
129
        $this->commandConfig = $this->getCommandConfig();
130
131
        if (is_null($this->tableDefinitions)) {
132
            /* @var $dbHelper DatabaseHelper */
133
            $dbHelper = $this->getHelper('database');
134
135
            $this->tableDefinitions = $dbHelper->getTableDefinitions($this->commandConfig);
136
        }
137
138
        return $this->tableDefinitions;
139
    }
140
141
    /**
142
     * Generate help for table definitions
143
     *
144
     * @return string
145
     */
146
    public function getTableDefinitionHelp()
147
    {
148
        $messages = PHP_EOL;
149
        $this->commandConfig = $this->getCommandConfig();
150
        $messages .= <<<HELP
151
<comment>Strip option</comment>
152
 If you like to skip data of some tables you can use the --strip option.
153
 The strip option creates only the structure of the defined tables and
154
 forces `mysqldump` to skip the data.
155
156
 Separate each table to strip by a space.
157
 You can use wildcards like * and ? in the table names to strip multiple
158
 tables. In addition you can specify pre-defined table groups, that start
159
 with an
160
161
 Example: "dataflow_batch_export unimportant_module_* @log
162
163
    $ n98-magerun.phar db:dump --strip="@stripped"
164
165
<comment>Available Table Groups</comment>
166
167
HELP;
168
169
        $definitions = $this->getTableDefinitions();
0 ignored issues
show
Deprecated Code introduced by
The method N98\Magento\Command\Data...::getTableDefinitions() has been deprecated with message: Use database helper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
170
        $list = array();
171
        $maxNameLen = 0;
172
        foreach ($definitions as $id => $definition) {
173
            $name = '@' . $id;
174
            $description = isset($definition['description']) ? $definition['description'] . '.' : '';
175
            $nameLen = strlen($name);
176
            if ($nameLen > $maxNameLen) {
177
                $maxNameLen = $nameLen;
178
            }
179
            $list[] = array($name, $description);
180
        }
181
182
        $decrSize = 78 - $maxNameLen - 3;
183
184
        foreach ($list as $entry) {
185
            list($name, $description) = $entry;
186
            $delta = max(0, $maxNameLen - strlen($name));
187
            $spacer = $delta ? str_repeat(' ', $delta) : '';
188
            $buffer = wordwrap($description, $decrSize);
189
            $buffer = strtr($buffer, array("\n" => "\n" . str_repeat(' ', 3 + $maxNameLen)));
190
            $messages .= sprintf(" <info>%s</info>%s  %s\n", $name, $spacer, $buffer);
191
        }
192
193
        return $messages;
194
    }
195
196
    public function getHelp()
197
    {
198
        return
199
            parent::getHelp() . PHP_EOL
200
            . $this->getCompressionHelp() . PHP_EOL
201
            . $this->getTableDefinitionHelp();
202
    }
203
204
    /**
205
     * @param InputInterface $input
206
     * @param OutputInterface $output
207
     *
208
     * @return int|void
209
     */
210
    protected function execute(InputInterface $input, OutputInterface $output)
211
    {
212
        // communicate early what is required for this command to run (is enabled)
213
        $enabler = new Enabler($this);
214
        $enabler->functionExists('exec');
215
        $enabler->functionExists('passthru');
216
        $enabler->operatingSystemIsNotWindows();
217
218
        $this->detectDbSettings($output);
219
220
        if (!$input->getOption('stdout') && !$input->getOption('only-command')
221
            && !$input->getOption('print-only-filename')
222
        ) {
223
            $this->writeSection($output, 'Dump MySQL Database');
224
        }
225
226
        $compressor = $this->getCompressor($input->getOption('compression'));
227
        $fileName = $this->getFileName($input, $output, $compressor);
228
229
        $stripTables = array();
230 View Code Duplication
        if ($input->getOption('strip')) {
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...
231
            /* @var $database DatabaseHelper */
232
            $database = $this->getHelper('database');
233
            $stripTables = $database->resolveTables(
234
                explode(' ', $input->getOption('strip')),
235
                $this->getTableDefinitions()
0 ignored issues
show
Deprecated Code introduced by
The method N98\Magento\Command\Data...::getTableDefinitions() has been deprecated with message: Use database helper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
236
            );
237
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
238
                && !$input->getOption('print-only-filename')
239
            ) {
240
                $output->writeln(
241
                    '<comment>No-data export for: <info>' . implode(' ', $stripTables) . '</info></comment>'
242
                );
243
            }
244
        }
245
246
        $excludeTables = array();
247 View Code Duplication
        if ($input->getOption('exclude')) {
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...
248
            $excludeTables = $this->getHelper('database')->resolveTables(
249
                explode(' ', $input->getOption('exclude')),
250
                $this->getTableDefinitions()
0 ignored issues
show
Deprecated Code introduced by
The method N98\Magento\Command\Data...::getTableDefinitions() has been deprecated with message: Use database helper

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
251
            );
252
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
253
                && !$input->getOption('print-only-filename')
254
            ) {
255
                $output->writeln(
256
                    '<comment>Excluded: <info>' . implode(' ', $excludeTables) . '</info></comment>'
257
                );
258
            }
259
        }
260
261
        $dumpOptions = '';
262
        if (!$input->getOption('no-single-transaction')) {
263
            $dumpOptions = '--single-transaction --quick ';
264
        }
265
266
        if ($input->getOption('human-readable')) {
267
            $dumpOptions .= '--complete-insert --skip-extended-insert ';
268
        }
269
270
        if ($input->getOption('add-routines')) {
271
            $dumpOptions .= '--routines ';
272
        }
273
274
        if ($input->getOption('xml')) {
275
            $dumpOptions .= '--xml ';
276
        }
277
278
        if ($input->getOption('hex-blob')) {
279
            $dumpOptions .= '--hex-blob ';
280
        }
281
282
        $execs = array();
283
284
        $ignore = '';
285
        foreach (array_merge($excludeTables, $stripTables) as $tableName) {
286
            $ignore .= '--ignore-table=' . $this->dbSettings['dbname'] . '.' . $tableName . ' ';
287
        }
288
289
        $mysqlClientToolConnectionString = $this->getHelper('database')->getMysqlClientToolConnectionString();
290
291
        if (count($stripTables) > 0) {
292
            // dump structure for strip-tables
293
            $exec = 'mysqldump ' . $dumpOptions . '--no-data ' . $mysqlClientToolConnectionString;
294
            $exec .= ' ' . implode(' ', $stripTables);
295
            $exec .= $this->postDumpPipeCommands();
296
            $exec = $compressor->getCompressingCommand($exec);
297
            if (!$input->getOption('stdout')) {
298
                $exec .= ' > ' . escapeshellarg($fileName);
299
            }
300
            $execs[] = $exec;
301
        }
302
303
        // dump data for all other tables
304
        $exec = 'mysqldump ' . $dumpOptions . $mysqlClientToolConnectionString . ' ' . $ignore;
305
        $exec .= $this->postDumpPipeCommands();
306
        $exec = $compressor->getCompressingCommand($exec);
307
        if (!$input->getOption('stdout')) {
308
            $exec .= (count($stripTables) > 0 ? ' >> ' : ' > ') . escapeshellarg($fileName);
309
        }
310
        $execs[] = $exec;
311
312
        $this->runExecs($execs, $fileName, $input, $output);
313
    }
314
315
    /**
316
     * @param array $execs
317
     * @param string $fileName
318
     * @param InputInterface $input
319
     * @param OutputInterface $output
320
     */
321
    private function runExecs(array $execs, $fileName, InputInterface $input, OutputInterface $output)
322
    {
323
        if ($input->getOption('only-command') && !$input->getOption('print-only-filename')) {
324
            foreach ($execs as $exec) {
325
                $output->writeln($exec);
326
            }
327
        } else {
328
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
329
                && !$input->getOption('print-only-filename')
330
            ) {
331
                $output->writeln(
332
                    '<comment>Start dumping database <info>' . $this->dbSettings['dbname']
333
                    . '</info> to file <info>' . $fileName . '</info>'
334
                );
335
            }
336
337
            if ($input->getOption('dry-run')) {
338
                $execs = array();
339
            }
340
341
            foreach ($execs as $exec) {
342
                $commandOutput = '';
343
                if ($input->getOption('stdout')) {
344
                    passthru($exec, $returnValue);
345
                } else {
346
                    Exec::run($exec, $commandOutput, $returnValue);
347
                }
348
                if ($returnValue > 0) {
349
                    $output->writeln('<error>' . $commandOutput . '</error>');
350
                    $output->writeln('<error>Return Code: ' . $returnValue . '. ABORTED.</error>');
351
352
                    return;
353
                }
354
            }
355
356
            if (!$input->getOption('stdout') && !$input->getOption('print-only-filename')) {
357
                $output->writeln('<info>Finished</info>');
358
            }
359
        }
360
361
        if ($input->getOption('print-only-filename')) {
362
            $output->writeln($fileName);
363
        }
364
    }
365
366
    /**
367
     * Commands which filter mysql data. Piped to mysqldump command
368
     *
369
     * @return string
370
     */
371
    protected function postDumpPipeCommands()
372
    {
373
        return ' | LANG=C LC_CTYPE=C LC_ALL=C sed -e ' . escapeshellarg('s/DEFINER[ ]*=[ ]*[^*]*\*/\*/');
374
    }
375
376
    /**
377
     * @param InputInterface $input
378
     * @param OutputInterface $output
379
     * @param Compressor $compressor
380
     *
381
     * @return string
382
     */
383
    protected function getFileName(InputInterface $input, OutputInterface $output, Compressor $compressor)
384
    {
385
        $namePrefix = '';
386
        $nameSuffix = '';
387
        if ($input->getOption('xml')) {
388
            $nameExtension = '.xml';
389
        } else {
390
            $nameExtension = '.sql';
391
        }
392
393
        if ($input->getOption('add-time') !== false) {
394
            $timeStamp = date('Y-m-d_His');
395
396
            if ($input->getOption('add-time') == 'suffix') {
397
                $nameSuffix = '_' . $timeStamp;
398
            } else {
399
                $namePrefix = $timeStamp . '_';
400
            }
401
        }
402
403
        if (
404
            (
405
                ($fileName = $input->getArgument('filename')) === null
406
                || ($isDir = is_dir($fileName))
407
            )
408
            && !$input->getOption('stdout')
409
        ) {
410
            /** @var DialogHelper $dialog */
411
            $dialog = $this->getHelper('dialog');
412
            $defaultName = VerifyOrDie::filename(
413
                $namePrefix . $this->dbSettings['dbname'] . $nameSuffix . $nameExtension
414
            );
415
            if (isset($isDir) && $isDir) {
416
                $defaultName = rtrim($fileName, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $defaultName;
417
            }
418
            if (!$input->getOption('force')) {
419
                $fileName = $dialog->ask(
420
                    $output,
421
                    '<question>Filename for SQL dump:</question> [<comment>' . $defaultName . '</comment>]',
422
                    $defaultName
423
                );
424
            } else {
425
                $fileName = $defaultName;
426
            }
427
        } else {
428
            if ($input->getOption('add-time')) {
429
                $pathParts = pathinfo($fileName);
430
                $fileName = ($pathParts['dirname'] == '.' ? '' : $pathParts['dirname'] . DIRECTORY_SEPARATOR) .
431
                    $namePrefix . $pathParts['filename'] . $nameSuffix . '.' . $pathParts['extension'];
432
            }
433
        }
434
435
        $fileName = $compressor->getFileName($fileName);
436
437
        return $fileName;
438
    }
439
}
440