Completed
Push — master ( 202f3c...15bdf1 )
by Tom
09:14 queued 04:46
created

DumpCommand   C

Complexity

Total Complexity 58

Size/Duplication

Total Lines 351
Duplicated Lines 6.27 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 7
Bugs 2 Features 0
Metric Value
wmc 58
c 7
b 2
f 0
lcom 1
cbo 7
dl 22
loc 351
rs 6.3005

8 Methods

Rating   Name   Duplication   Size   Complexity  
A postDumpPipeCommands() 0 4 1
C runExecs() 0 39 13
C getFileName() 0 47 12
A getTableDefinitions() 0 13 2
A getHelp() 0 6 1
F execute() 22 98 22
B configure() 0 40 1
B getTableDefinitionHelp() 0 49 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DumpCommand often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DumpCommand, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace N98\Magento\Command\Database;
4
5
use N98\Magento\Command\Database\Compressor\AbstractCompressor;
6
use N98\Util\Console\Enabler;
7
use N98\Util\Console\Helper\DatabaseHelper;
8
use N98\Util\Exec;
9
use RuntimeException;
10
use Symfony\Component\Console\Helper\DialogHelper;
11
use Symfony\Component\Console\Input\InputArgument;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
16
class DumpCommand extends AbstractDatabaseCommand
17
{
18
    /**
19
     * @var array
20
     */
21
    protected $tableDefinitions = null;
22
23
    /**
24
     * @var array
25
     */
26
    protected $commandConfig = null;
27
28
    protected function configure()
29
    {
30
        $this
31
            ->setName('db:dump')
32
            ->addArgument('filename', InputArgument::OPTIONAL, 'Dump filename')
33
            ->addOption('add-time', 't', InputOption::VALUE_OPTIONAL, 'Adds time to filename (only if filename was not provided)')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
34
            ->addOption('compression', 'c', InputOption::VALUE_REQUIRED, 'Compress the dump file using one of the supported algorithms')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
35
            ->addOption('xml', null, InputOption::VALUE_NONE, 'Dump database in xml format')
36
            ->addOption('hex-blob', null, InputOption::VALUE_NONE, 'Dump binary columns using hexadecimal notation (for example, "abc" becomes 0x616263)')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 154 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
37
            ->addOption('only-command', null, InputOption::VALUE_NONE, 'Print only mysqldump command. Do not execute')
38
            ->addOption('print-only-filename', null, InputOption::VALUE_NONE, 'Execute and prints no output except the dump filename')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 134 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
39
            ->addOption('no-single-transaction', null, InputOption::VALUE_NONE, 'Do not use single-transaction (not recommended, this is blocking)')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
40
            ->addOption('human-readable', null, InputOption::VALUE_NONE, 'Use a single insert with column names per row. Useful to track database differences. Use db:import --optimize for speeding up the import.')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 213 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
41
            ->addOption('add-routines', null, InputOption::VALUE_NONE, 'Include stored routines in dump (procedures & functions)')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
42
            ->addOption('stdout', null, InputOption::VALUE_NONE, 'Dump to stdout')
43
            ->addOption('strip', 's', InputOption::VALUE_OPTIONAL, 'Tables to strip (dump only structure of those tables)')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
44
            ->addOption('exclude', 'e', InputOption::VALUE_OPTIONAL, 'Tables to exclude from the dump')
45
            ->addOption('force', 'f', InputOption::VALUE_NONE, 'Do not prompt if all options are defined')
46
            ->setDescription('Dumps database with mysqldump cli client according to informations from local.xml');
47
48
        $help = <<<HELP
49
Dumps configured magento database with `mysqldump`. You must have installed
50
the MySQL client tools.
51
52
On debian systems run `apt-get install mysql-client` to do that.
53
54
The command reads app/etc/local.xml to find the correct settings.
55
56
See it in action: http://youtu.be/ttjZHY6vThs
57
58
- If you like to prepend a timestamp to the dump name the --add-time option
59
  can be used.
60
61
- The command comes with a compression function. Add i.e. `--compression=gz`
62
  to dump directly in gzip compressed file.
63
64
HELP;
65
        $this->setHelp($help);
66
67
    }
68
69
    /**
70
     * @return array
71
     *
72
     * @deprecated Use database helper
73
     * @throws RuntimeException
74
     */
75
    public function getTableDefinitions()
76
    {
77
        $this->commandConfig = $this->getCommandConfig();
78
79
        if (is_null($this->tableDefinitions)) {
80
            /* @var $dbHelper DatabaseHelper */
81
            $dbHelper = $this->getHelper('database');
82
83
            $this->tableDefinitions = $dbHelper->getTableDefinitions($this->commandConfig);
84
        }
85
86
        return $this->tableDefinitions;
87
    }
88
89
    /**
90
     * Generate help for table definitions
91
     *
92
     * @return string
93
     */
94
    public function getTableDefinitionHelp()
95
    {
96
        $messages = PHP_EOL;
97
        $this->commandConfig = $this->getCommandConfig();
98
        $messages .= <<<HELP
99
<comment>Strip option</comment>
100
 If you like to skip data of some tables you can use the --strip option.
101
 The strip option creates only the structure of the defined tables and
102
 forces `mysqldump` to skip the data.
103
104
 Separate each table to strip by a space.
105
 You can use wildcards like * and ? in the table names to strip multiple
106
 tables. In addition you can specify pre-defined table groups, that start
107
 with an
108
109
 Example: "dataflow_batch_export unimportant_module_* @log
110
111
    $ n98-magerun.phar db:dump --strip="@stripped"
112
113
<comment>Available Table Groups</comment>
114
115
HELP;
116
117
        $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...
118
        $list = array();
119
        $maxNameLen = 0;
120
        foreach ($definitions as $id => $definition) {
121
            $name    = '@' . $id;
122
            $description = isset($definition['description']) ? $definition['description'] . '.' : '';
123
            $nameLen = strlen($name);
124
            if ($nameLen > $maxNameLen) {
125
                $maxNameLen = $nameLen;
126
            }
127
            $list[] = array($name, $description);
128
        }
129
130
        $decrSize = 78 - $maxNameLen - 3;
131
132
        foreach ($list as $entry) {
133
            list($name, $description) = $entry;
134
            $delta  = max(0, $maxNameLen - strlen($name));
135
            $spacer = $delta ? str_repeat(' ', $delta) : '';
136
            $buffer = wordwrap($description, $decrSize);
137
            $buffer = strtr($buffer, array("\n" => "\n" . str_repeat(' ', 3 + $maxNameLen)));
138
            $messages .= sprintf(" <info>%s</info>%s  %s\n", $name, $spacer, $buffer);
139
        }
140
141
        return $messages;
142
    }
143
144
    public function getHelp()
145
    {
146
        return parent::getHelp() . PHP_EOL
147
            . $this->getCompressionHelp() . PHP_EOL
148
            . $this->getTableDefinitionHelp();
149
    }
150
151
    /**
152
     * @param InputInterface  $input
153
     * @param OutputInterface $output
154
     *
155
     * @return int|void
156
     */
157
    protected function execute(InputInterface $input, OutputInterface $output)
158
    {
159
        // communicate early what is required for this command to run (is enabled)
160
        $enabler = new Enabler($this);
161
        $enabler->functionExists('exec');
162
        $enabler->functionExists('passthru');
163
        $enabler->operatingSystemIsNotWindows();
164
165
        $this->detectDbSettings($output);
166
167
        if (!$input->getOption('stdout') && !$input->getOption('only-command')
168
            && !$input->getOption('print-only-filename')
169
        ) {
170
            $this->writeSection($output, 'Dump MySQL Database');
171
        }
172
173
        $compressor = $this->getCompressor($input->getOption('compression'));
174
        $fileName   = $this->getFileName($input, $output, $compressor);
175
176
        $stripTables = array();
177 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...
178
            /* @var $database DatabaseHelper */
179
            $database = $this->getHelper('database');
180
            $stripTables = $database->resolveTables(explode(' ', $input->getOption('strip')), $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...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 124 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
181
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
182
                && !$input->getOption('print-only-filename')
183
            ) {
184
                $output->writeln('<comment>No-data export for: <info>' . implode(' ', $stripTables)
185
                    . '</info></comment>'
186
                );
187
            }
188
        }
189
190
        $excludeTables = array();
191 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...
192
            $excludeTables = $this->getHelper('database')->resolveTables(explode(' ', $input->getOption('exclude')), $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...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 147 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
193
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
194
                && !$input->getOption('print-only-filename')
195
            ) {
196
                $output->writeln('<comment>Excluded: <info>' . implode(' ', $excludeTables)
197
                    . '</info></comment>'
198
                );
199
            }
200
        }
201
202
        $dumpOptions = '';
203
        if (!$input->getOption('no-single-transaction')) {
204
            $dumpOptions = '--single-transaction --quick ';
205
        }
206
207
        if ($input->getOption('human-readable')) {
208
            $dumpOptions .= '--complete-insert --skip-extended-insert ';
209
        }
210
211
        if ($input->getOption('add-routines')) {
212
            $dumpOptions .= '--routines ';
213
        }
214
215
        if ($input->getOption('xml')) {
216
            $dumpOptions .= '--xml ';
217
        }
218
219
        if ($input->getOption('hex-blob')) {
220
            $dumpOptions .= '--hex-blob ';
221
        }
222
223
        $execs = array();
224
225
        $ignore = '';
226
        foreach (array_merge($excludeTables, $stripTables) as $tableName) {
227
            $ignore .= '--ignore-table=' . $this->dbSettings['dbname'] . '.' . $tableName . ' ';
228
        }
229
230
        $mysqlClientToolConnectionString = $this->getHelper('database')->getMysqlClientToolConnectionString();
231
232
        if (count($stripTables) > 0) {
233
            // dump structure for strip-tables
234
            $exec = 'mysqldump ' . $dumpOptions . '--no-data ' . $mysqlClientToolConnectionString;
235
            $exec .= ' ' . implode(' ', $stripTables);
236
            $exec .= $this->postDumpPipeCommands();
237
            $exec = $compressor->getCompressingCommand($exec);
238
            if (!$input->getOption('stdout')) {
239
                $exec .= ' > ' . escapeshellarg($fileName);
240
            }
241
            $execs[] = $exec;
242
        }
243
244
        // dump data for all other tables
245
        $exec = 'mysqldump ' . $dumpOptions . $mysqlClientToolConnectionString . ' ' . $ignore;
246
        $exec .= $this->postDumpPipeCommands();
247
        $exec = $compressor->getCompressingCommand($exec);
248
        if (!$input->getOption('stdout')) {
249
            $exec .= (count($stripTables) > 0 ? ' >> ' : ' > ') . escapeshellarg($fileName);
250
        }
251
        $execs[] = $exec;
252
253
        $this->runExecs($execs, $fileName, $input, $output);
254
    }
255
256
    /**
257
     * @param array $execs
258
     * @param string $fileName
259
     * @param InputInterface $input
260
     * @param OutputInterface $output
261
     */
262
    private function runExecs(array $execs, $fileName, InputInterface $input, OutputInterface $output)
263
    {
264
        if ($input->getOption('only-command') && !$input->getOption('print-only-filename')) {
265
            foreach ($execs as $exec) {
266
                $output->writeln($exec);
267
            }
268
        } else {
269
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
270
                && !$input->getOption('print-only-filename')
271
            ) {
272
                $output->writeln('<comment>Start dumping database <info>' . $this->dbSettings['dbname']
273
                    . '</info> to file <info>' . $fileName . '</info>'
274
                );
275
            }
276
277
            foreach ($execs as $exec) {
278
                $commandOutput = '';
279
                if ($input->getOption('stdout')) {
280
                    passthru($exec, $returnValue);
281
                } else {
282
                    Exec::run($exec, $commandOutput, $returnValue);
283
                }
284
                if ($returnValue > 0) {
285
                    $output->writeln('<error>' . $commandOutput . '</error>');
286
                    $output->writeln('<error>Return Code: ' . $returnValue . '. ABORTED.</error>');
287
288
                    return;
289
                }
290
            }
291
292
            if (!$input->getOption('stdout') && !$input->getOption('print-only-filename')) {
293
                $output->writeln('<info>Finished</info>');
294
            }
295
        }
296
297
        if ($input->getOption('print-only-filename')) {
298
            $output->writeln($fileName);
299
        }
300
    }
301
302
    /**
303
     * Commands which filter mysql data. Piped to mysqldump command
304
     *
305
     * @return string
306
     */
307
    protected function postDumpPipeCommands()
308
    {
309
        return ' | LANG=C LC_CTYPE=C LC_ALL=C sed -e ' . escapeshellarg('s/DEFINER[ ]*=[ ]*[^*]*\*/\*/');
310
    }
311
312
    /**
313
     * @param InputInterface     $input
314
     * @param OutputInterface    $output
315
     * @param AbstractCompressor $compressor
316
     *
317
     * @return string
318
     */
319
    protected function getFileName(InputInterface $input, OutputInterface $output, AbstractCompressor $compressor)
320
    {
321
        $namePrefix    = '';
322
        $nameSuffix    = '';
323
        if ($input->getOption('xml')) {
324
            $nameExtension = '.xml';
325
        } else {
326
            $nameExtension = '.sql';
327
        }
328
329
330
        if ($input->getOption('add-time') !== false) {
331
            $timeStamp = date('Y-m-d_His');
332
333
            if ($input->getOption('add-time') == 'suffix') {
334
                $nameSuffix = '_' . $timeStamp;
335
            } else {
336
                $namePrefix = $timeStamp . '_';
337
            }
338
        }
339
340
        if ((($fileName = $input->getArgument('filename')) === null || ($isDir = is_dir($fileName))) && !$input->getOption('stdout')) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 135 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
341
            /** @var DialogHelper $dialog */
342
            $dialog      = $this->getHelperSet()->get('dialog');
343
            $defaultName = $namePrefix . $this->dbSettings['dbname'] . $nameSuffix . $nameExtension;
344
            if (isset($isDir) && $isDir) {
345
                $defaultName = rtrim($fileName, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $defaultName;
346
            }
347
            if (!$input->getOption('force')) {
348
                $fileName = $dialog->ask($output, '<question>Filename for SQL dump:</question> [<comment>'
349
                    . $defaultName . '</comment>]', $defaultName
350
                );
351
            } else {
352
                $fileName = $defaultName;
353
            }
354
        } else {
355
            if ($input->getOption('add-time')) {
356
                $pathParts = pathinfo($fileName);
357
                $fileName = ($pathParts['dirname'] == '.' ? '' : $pathParts['dirname'] . DIRECTORY_SEPARATOR) .
358
                    $namePrefix . $pathParts['filename'] . $nameSuffix . '.' . $pathParts['extension'];
359
            }
360
        }
361
362
        $fileName = $compressor->getFileName($fileName);
363
364
        return $fileName;
365
    }
366
}
367