Completed
Push — master ( bd5921...d9293e )
by Christian
17:54 queued 08:59
created

DumpCommand::getTableDefinitions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 13
rs 9.4286
cc 2
eloc 6
nc 2
nop 0
1
<?php
2
3
namespace N98\Magento\Command\Database;
4
5
use N98\Magento\Command\Database\Compressor\AbstractCompressor;
6
use N98\Util\Console\Helper\DatabaseHelper;
7
use N98\Util\OperatingSystem;
8
use RuntimeException;
9
use Symfony\Component\Console\Helper\DialogHelper;
10
use Symfony\Component\Console\Input\InputArgument;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Input\InputOption;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use N98\Util\Exec;
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 bool
71
     */
72
    public function isEnabled()
73
    {
74
        return function_exists('exec') && !OperatingSystem::isWindows();
75
    }
76
77
    /**
78
     * @return array
79
     *
80
     * @deprecated Use database helper
81
     * @throws RuntimeException
82
     */
83
    public function getTableDefinitions()
84
    {
85
        $this->commandConfig = $this->getCommandConfig();
86
87
        if (is_null($this->tableDefinitions)) {
88
            /* @var $dbHelper DatabaseHelper */
89
            $dbHelper = $this->getHelper('database');
90
91
            $this->tableDefinitions = $dbHelper->getTableDefinitions($this->commandConfig);
92
        }
93
94
        return $this->tableDefinitions;
95
    }
96
97
    /**
98
     * Generate help for table definitions
99
     *
100
     * @return string
101
     */
102
    public function getTableDefinitionHelp()
103
    {
104
        $messages = PHP_EOL;
105
        $this->commandConfig = $this->getCommandConfig();
106
        $messages .= <<<HELP
107
<comment>Strip option</comment>
108
 If you like to skip data of some tables you can use the --strip option.
109
 The strip option creates only the structure of the defined tables and
110
 forces `mysqldump` to skip the data.
111
112
 Separate each table to strip by a space.
113
 You can use wildcards like * and ? in the table names to strip multiple
114
 tables. In addition you can specify pre-defined table groups, that start
115
 with an
116
117
 Example: "dataflow_batch_export unimportant_module_* @log
118
119
    $ n98-magerun.phar db:dump --strip="@stripped"
120
121
<comment>Available Table Groups</comment>
122
123
HELP;
124
125
        $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...
126
        $list = array();
127
        $maxNameLen = 0;
128
        foreach ($definitions as $id => $definition) {
129
            $name    = '@' . $id;
130
            $description = isset($definition['description']) ? $definition['description'] . '.' : '';
131
            $nameLen = strlen($name);
132
            if ($nameLen > $maxNameLen) {
133
                $maxNameLen = $nameLen;
134
            }
135
            $list[] = array($name, $description);
136
        }
137
138
        $decrSize = 78 - $maxNameLen - 3;
139
140
        foreach ($list as $entry) {
141
            list($name, $description) = $entry;
142
            $delta  = max(0, $maxNameLen - strlen($name));
143
            $spacer = $delta ? str_repeat(' ', $delta) : '';
144
            $buffer = wordwrap($description, $decrSize);
145
            $buffer = strtr($buffer, array("\n" => "\n" . str_repeat(' ', 3 + $maxNameLen)));
146
            $messages .= sprintf(" <info>%s</info>%s  %s\n", $name, $spacer, $buffer);
147
        }
148
149
        return $messages;
150
    }
151
152
    public function getHelp()
153
    {
154
        return parent::getHelp() . PHP_EOL
155
            . $this->getCompressionHelp() . PHP_EOL
156
            . $this->getTableDefinitionHelp();
157
    }
158
159
    /**
160
     * @param InputInterface  $input
161
     * @param OutputInterface $output
162
     *
163
     * @return int|void
164
     */
165
    protected function execute(InputInterface $input, OutputInterface $output)
166
    {
167
        $this->detectDbSettings($output);
168
169
        if (!$input->getOption('stdout') && !$input->getOption('only-command')
170
            && !$input->getOption('print-only-filename')
171
        ) {
172
            $this->writeSection($output, 'Dump MySQL Database');
173
        }
174
175
        $compressor = $this->getCompressor($input->getOption('compression'));
176
        $fileName   = $this->getFileName($input, $output, $compressor);
177
178
        $stripTables = array();
179 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...
180
            /* @var $database DatabaseHelper */
181
            $database = $this->getHelper('database');
182
            $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...
183
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
184
                && !$input->getOption('print-only-filename')
185
            ) {
186
                $output->writeln('<comment>No-data export for: <info>' . implode(' ', $stripTables)
187
                    . '</info></comment>'
188
                );
189
            }
190
        }
191
192
        $excludeTables = array();
193 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...
194
            $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...
195
            if (!$input->getOption('stdout') && !$input->getOption('only-command')
196
                && !$input->getOption('print-only-filename')
197
            ) {
198
                $output->writeln('<comment>Excluded: <info>' . implode(' ', $excludeTables)
199
                    . '</info></comment>'
200
                );
201
            }
202
        }
203
204
        $dumpOptions = '';
205
        if (!$input->getOption('no-single-transaction')) {
206
            $dumpOptions = '--single-transaction --quick ';
207
        }
208
209
        if ($input->getOption('human-readable')) {
210
            $dumpOptions .= '--complete-insert --skip-extended-insert ';
211
        }
212
213
        if ($input->getOption('add-routines')) {
214
            $dumpOptions .= '--routines ';
215
        }
216
217
        if ($input->getOption('xml')) {
218
            $dumpOptions .= '--xml ';
219
        }
220
221
        if ($input->getOption('hex-blob')) {
222
            $dumpOptions .= '--hex-blob ';
223
        }
224
225
        $execs = array();
226
227
        $ignore = '';
228
        foreach (array_merge($excludeTables, $stripTables) as $tableName) {
229
            $ignore .= '--ignore-table=' . $this->dbSettings['dbname'] . '.' . $tableName . ' ';
230
        }
231
232
        if (count($stripTables) > 0) {
233
            // dump structure for strip-tables
234
            $exec = 'mysqldump ' . $dumpOptions . '--no-data ' . $this->getHelper('database')->getMysqlClientToolConnectionString();
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 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...
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 . $ignore . $this->getHelper('database')->getMysqlClientToolConnectionString();
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...
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 ' | 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