Completed
Pull Request — develop (#182)
by Tom
04:37
created

DumpCommand::configure()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 101
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

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