Completed
Push — master ( ad0611...abd676 )
by Tom
09:08 queued 04:30
created

DumpCommand::configure()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 102
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 1
Metric Value
c 3
b 2
f 1
dl 0
loc 102
rs 8.2857
cc 1
eloc 53
nc 1
nop 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 N98\Magento\Command\Database;
4
5
use N98\Util\OperatingSystem;
6
use RuntimeException;
7
use Symfony\Component\Console\Helper\DialogHelper;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
class DumpCommand extends AbstractDatabaseCommand
14
{
15
    /**
16
     * @var array
17
     */
18
    protected $tableDefinitions = null;
19
20
    /**
21
     * @var array
22
     */
23
    protected $commandConfig = null;
24
25
    protected function configure()
26
    {
27
        $this
28
            ->setName('db:dump')
29
            ->addArgument('filename', InputArgument::OPTIONAL, 'Dump filename')
30
            ->addOption(
31
                'add-time',
32
                't',
33
                InputOption::VALUE_OPTIONAL,
34
                'Adds time to filename (only if filename was not provided)'
35
            )
36
            ->addOption(
37
                'compression',
38
                'c',
39
                InputOption::VALUE_REQUIRED,
40
                'Compress the dump file using one of the supported algorithms'
41
            )
42
            ->addOption(
43
                'only-command',
44
                null,
45
                InputOption::VALUE_NONE,
46
                'Print only mysqldump command. 
47
                Do not execute'
48
            )
49
            ->addOption(
50
                'print-only-filename',
51
                null,
52
                InputOption::VALUE_NONE,
53
                'Execute and prints no output except the dump filename'
54
            )
55
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'do everything but the dump')
56
            ->addOption(
57
                'no-single-transaction',
58
                null,
59
                InputOption::VALUE_NONE,
60
                'Do not use single-transaction (not recommended, this is blocking)'
61
            )
62
            ->addOption(
63
                'human-readable',
64
                null,
65
                InputOption::VALUE_NONE,
66
                'Use a single insert with column names per row. Useful to track database differences. Use ' .
67
                'db:import --optimize for speeding up the import.'
68
            )
69
            ->addOption(
70
                'add-routines',
71
                null,
72
                InputOption::VALUE_NONE,
73
                'Include stored routines in dump (procedures & functions)'
74
            )
75
            ->addOption('stdout', null, InputOption::VALUE_NONE, 'Dump to stdout')
76
            ->addOption(
77
                'strip',
78
                's',
79
                InputOption::VALUE_OPTIONAL,
80
                'Tables to strip (dump only structure of those tables)'
81
            )
82
            ->addOption('force', 'f', InputOption::VALUE_NONE, 'Do not prompt if all options are defined')
83
            ->setDescription('Dumps database with mysqldump cli client according to informations from local.xml');
84
85
        $help = <<<HELP
86
Dumps configured magento database with `mysqldump`.
87
You must have installed the MySQL client tools.
88
89
On debian systems run `apt-get install mysql-client` to do that.
90
91
The command reads app/etc/local.xml to find the correct settings.
92
If you like to skip data of some tables you can use the --strip option.
93
The strip option creates only the structure of the defined tables and
94
forces `mysqldump` to skip the data.
95
96
Dumps your database and excludes some tables. This is useful i.e. for development.
97
98
Separate each table to strip by a space.
99
You can use wildcards like * and ? in the table names to strip multiple tables.
100
In addition you can specify pre-defined table groups, that start with an @
101
Example: "dataflow_batch_export unimportant_module_* @log
102
103
   $ n98-magerun.phar db:dump --strip="@stripped"
104
105
Available Table Groups:
106
107
* @log Log tables
108
* @dataflowtemp Temporary tables of the dataflow import/export tool
109
* @stripped Standard definition for a stripped dump (logs and dataflow)
110
* @sales Sales data (orders, invoices, creditmemos etc)
111
* @customers Customer data
112
* @trade Current trade data (customers and orders). You usally do not want those in developer systems.
113
* @development Removes logs and trade data so developers do not have to work with real customer data
114
115
Extended: https://github.com/netz98/n98-magerun/wiki/Stripped-Database-Dumps
116
117
See it in action: http://youtu.be/ttjZHY6vThs
118
119
- If you like to prepend a timestamp to the dump name the --add-time option can be used.
120
121
- The command comes with a compression function. Add i.e. `--compression=gz` to dump directly in
122
 gzip compressed file.
123
124
HELP;
125
        $this->setHelp($help);
126
    }
127
128
    /**
129
     * @return bool
130
     */
131
    public function isEnabled()
132
    {
133
        return function_exists('exec') && !OperatingSystem::isWindows();
134
    }
135
136
    /**
137
     * @return array
138
     *
139
     * @deprecated Use database helper
140
     * @throws RuntimeException
141
     */
142
    public function getTableDefinitions()
143
    {
144
        $this->commandConfig = $this->getCommandConfig();
145
146
        if (is_null($this->tableDefinitions)) {
147
            $this->tableDefinitions = array();
148
            if (isset($this->commandConfig['table-groups'])) {
149
                $tableGroups = $this->commandConfig['table-groups'];
150 View Code Duplication
                foreach ($tableGroups as $index => $definition) {
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...
151
                    $description = isset($definition['description']) ? $definition['description'] : '';
152
                    if (!isset($definition['id'])) {
153
                        throw new RuntimeException('Invalid definition of table-groups (id missing) Index: ' . $index);
154
                    }
155
                    if (!isset($definition['id'])) {
156
                        throw new RuntimeException(
157
                            'Invalid definition of table-groups (tables missing) Id: ' . $definition['id']
158
                        );
159
                    }
160
161
                    $this->tableDefinitions[$definition['id']] = array(
162
                        'tables' => $definition['tables'],
163
                        'description' => $description,
164
                    );
165
                }
166
            };
167
        }
168
169
        return $this->tableDefinitions;
170
    }
171
172
    /**
173
     * Generate help for table definitions
174
     *
175
     * @return string
176
     */
177
    public function getTableDefinitionHelp()
178
    {
179
        $messages = array();
180
        $this->commandConfig = $this->getCommandConfig();
181
        $messages[] = '';
182
        $messages[] = '<comment>Strip option</comment>';
183
        $messages[] = ' Separate each table to strip by a space.';
184
        $messages[] = ' You can use wildcards like * and ? in the table names to strip multiple tables.';
185
        $messages[] = ' In addition you can specify pre-defined table groups, that start with an @';
186
        $messages[] = ' Example: "dataflow_batch_export unimportant_module_* @log';
187
        $messages[] = '';
188
        $messages[] = '<comment>Available Table Groups</comment>';
189
190
        $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...
191
        foreach ($definitions as $id => $definition) {
192
            $description = isset($definition['description']) ? $definition['description'] : '';
193
            /** @TODO:
194
             * Column-Wise formatting of the options, see InputDefinition::asText for code to pad by the max length,
195
             * but I do not like to copy and paste ..
196
             */
197
            $messages[] = ' <info>@' . $id . '</info> ' . $description;
198
        }
199
200
        return implode(PHP_EOL, $messages);
201
    }
202
203
    public function getHelp()
204
    {
205
        return parent::getHelp() . PHP_EOL
206
        . $this->getCompressionHelp() . PHP_EOL
207
        . $this->getTableDefinitionHelp();
208
    }
209
210
    /**
211
     * @param \Symfony\Component\Console\Input\InputInterface $input
212
     * @param \Symfony\Component\Console\Output\OutputInterface $output
213
     * @return int|void
214
     */
215
    protected function execute(InputInterface $input, OutputInterface $output)
216
    {
217
        $this->detectDbSettings($output);
218
219
        if (!$input->getOption('stdout') && !$input->getOption('only-command')
220
            && !$input->getOption('print-only-filename')
221
        ) {
222
            $this->writeSection($output, 'Dump MySQL Database');
223
        }
224
225
        $compressor = $this->getCompressor($input->getOption('compression'));
226
        $fileName = $this->getFileName($input, $output, $compressor);
227
228
        $stripTables = false;
229
        if ($input->getOption('strip')) {
230
            $stripTables = $this->getHelper('database')->resolveTables(
231
                explode(' ', $input->getOption('strip')),
232
                $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...
233
            );
234
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
235
                && !$input->getOption('print-only-filename')
236
            ) {
237
                $output->writeln(
238
                    '<comment>No-data export for: <info>' . implode(' ', $stripTables)
239
                    . '</info></comment>'
240
                );
241
            }
242
        }
243
244
        $dumpOptions = '';
245
        if (!$input->getOption('no-single-transaction')) {
246
            $dumpOptions = '--single-transaction --quick ';
247
        }
248
249
        if ($input->getOption('human-readable')) {
250
            $dumpOptions .= '--complete-insert --skip-extended-insert ';
251
        }
252
253
        if ($input->getOption('add-routines')) {
254
            $dumpOptions .= '--routines ';
255
        }
256
        $execs = array();
257
258
        if (!$stripTables) {
259
            $exec = 'mysqldump ' . $dumpOptions . $this->getHelper('database')->getMysqlClientToolConnectionString();
260
            $exec .= $this->postDumpPipeCommands();
261
            $exec = $compressor->getCompressingCommand($exec);
262
            if (!$input->getOption('stdout')) {
263
                $exec .= ' > ' . escapeshellarg($fileName);
264
            }
265
            $execs[] = $exec;
266
        } else {
267
            // dump structure for strip-tables
268
            $exec = 'mysqldump ' . $dumpOptions . '--no-data ' .
269
                $this->getHelper('database')->getMysqlClientToolConnectionString();
270
            $exec .= ' ' . implode(' ', $stripTables);
271
            $exec .= $this->postDumpPipeCommands();
272
            $exec = $compressor->getCompressingCommand($exec);
273
            if (!$input->getOption('stdout')) {
274
                $exec .= ' > ' . escapeshellarg($fileName);
275
            }
276
            $execs[] = $exec;
277
278
            $ignore = '';
279
            foreach ($stripTables as $stripTable) {
280
                $ignore .= '--ignore-table=' . $this->dbSettings['dbname'] . '.' . $stripTable . ' ';
281
            }
282
283
            // dump data for all other tables
284
            $exec = 'mysqldump ' . $dumpOptions . $ignore .
285
                $this->getHelper('database')->getMysqlClientToolConnectionString();
286
            $exec .= $this->postDumpPipeCommands();
287
            $exec = $compressor->getCompressingCommand($exec);
288
            if (!$input->getOption('stdout')) {
289
                $exec .= ' >> ' . escapeshellarg($fileName);
290
            }
291
            $execs[] = $exec;
292
        }
293
294
        $this->runExecs($execs, $fileName, $input, $output);
295
    }
296
297
    /**
298
     * @param array $execs
299
     * @param string $fileName
300
     * @param InputInterface $input
301
     * @param OutputInterface $output
302
     */
303
    private function runExecs(array $execs, $fileName, InputInterface $input, OutputInterface $output)
304
    {
305
        if ($input->getOption('only-command') && !$input->getOption('print-only-filename')) {
306
            foreach ($execs as $exec) {
307
                $output->writeln($exec);
308
            }
309
        } else {
310
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
311
                && !$input->getOption('print-only-filename')
312
            ) {
313
                $output->writeln(
314
                    '<comment>Start dumping database <info>' . $this->dbSettings['dbname'] .
315
                    '</info> to file <info>' . $fileName . '</info>'
316
                );
317
            }
318
319
            if ($input->getOption('dry-run')) {
320
                $execs = array();
321
            }
322
323
            foreach ($execs as $exec) {
324
                $commandOutput = '';
325
                if ($input->getOption('stdout')) {
326
                    passthru($exec, $returnValue);
327
                } else {
328
                    exec($exec, $commandOutput, $returnValue);
329
                }
330
                if ($returnValue > 0) {
331
                    $output->writeln('<error>' . implode(PHP_EOL, $commandOutput) . '</error>');
332
                    $output->writeln('<error>Return Code: ' . $returnValue . '. ABORTED.</error>');
333
334
                    return;
335
                }
336
            }
337
338
            if (!$input->getOption('stdout') && !$input->getOption('print-only-filename')) {
339
                $output->writeln('<info>Finished</info>');
340
            }
341
        }
342
343
        if ($input->getOption('print-only-filename')) {
344
            $output->writeln($fileName);
345
        }
346
    }
347
348
    /**
349
     * Commands which filter mysql data. Piped to mysqldump command
350
     *
351
     * @return string
352
     */
353
    protected function postDumpPipeCommands()
354
    {
355
        return ' | sed -e ' . escapeshellarg('s/DEFINER[ ]*=[ ]*[^*]*\*/\*/');
356
    }
357
358
    /**
359
     * @param \Symfony\Component\Console\Input\InputInterface $input
360
     * @param \Symfony\Component\Console\Output\OutputInterface $output
361
     * @param \N98\Magento\Command\Database\Compressor\AbstractCompressor $compressor
362
     * @return string
363
     */
364
    protected function getFileName(
365
        InputInterface $input,
366
        OutputInterface $output,
367
        Compressor\AbstractCompressor $compressor
368
    ) {
369
        $namePrefix = '';
370
        $nameSuffix = '';
371
        $nameExtension = '.sql';
372
373
        if ($input->getOption('add-time') !== false) {
374
            $timeStamp = date('Y-m-d_His');
375
376
            if ($input->getOption('add-time') == 'suffix') {
377
                $nameSuffix = '_' . $timeStamp;
378
            } else {
379
                $namePrefix = $timeStamp . '_';
380
            }
381
        }
382
383
        if (
384
            (
385
                ($fileName = $input->getArgument('filename')) === null
386
                || ($isDir = is_dir($fileName))
387
            )
388
            && !$input->getOption('stdout')
389
        ) {
390
            /** @var DialogHelper $dialog */
391
            $dialog = $this->getHelperSet()->get('dialog');
392
            $defaultName = $namePrefix . $this->dbSettings['dbname'] . $nameSuffix . $nameExtension;
393
            if (isset($isDir) && $isDir) {
394
                $defaultName = rtrim($fileName, '/') . '/' . $defaultName;
395
            }
396
            if (!$input->getOption('force')) {
397
                $fileName = $dialog->ask(
398
                    $output,
399
                    '<question>Filename for SQL dump:</question> [<comment>' . $defaultName . '</comment>]',
400
                    $defaultName
401
                );
402
            } else {
403
                $fileName = $defaultName;
404
            }
405
        } else {
406
            if ($input->getOption('add-time')) {
407
                $pathParts = pathinfo($fileName);
408
                $fileName = ($pathParts['dirname'] == '.' ? '' : $pathParts['dirname'] . '/') .
409
                    $namePrefix . $pathParts['filename'] . $nameSuffix . '.' . $pathParts['extension'];
410
            }
411
        }
412
413
        $fileName = $compressor->getFileName($fileName);
414
415
        return $fileName;
416
    }
417
}
418