ImportCommand::optimize()   B
last analyzed

Complexity

Conditions 9
Paths 8

Size

Total Lines 56

Duplication

Lines 15
Ratio 26.79 %

Importance

Changes 0
Metric Value
cc 9
nc 8
nop 1
dl 15
loc 56
rs 7.4044
c 0
b 0
f 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 InvalidArgumentException;
6
use N98\Util\Exec;
7
use Symfony\Component\Console\Input\InputArgument;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Input\InputOption;
10
use Symfony\Component\Console\Output\OutputInterface;
11
12
class ImportCommand extends AbstractDatabaseCommand
13
{
14
    protected function configure()
15
    {
16
        $this
17
            ->setName('db:import')
18
            ->addArgument('filename', InputArgument::OPTIONAL, 'Dump filename')
19
            ->addOption('compression', 'c', InputOption::VALUE_REQUIRED, 'The compression of the specified file')
20
            ->addOption('only-command', null, InputOption::VALUE_NONE, 'Print only mysql command. Do not execute')
21
            ->addOption('only-if-empty', null, InputOption::VALUE_NONE, 'Imports only if database is empty')
22
            ->addOption(
23
                'optimize',
24
                null,
25
                InputOption::VALUE_NONE,
26
                'Convert verbose INSERTs to short ones before import (not working with compression)'
27
            )
28
            ->addOption('drop', null, InputOption::VALUE_NONE, 'Drop and recreate database before import')
29
            ->addOption('stdin', null, InputOption::VALUE_NONE, 'Import data from STDIN rather than file')
30
            ->addOption('drop-tables', null, InputOption::VALUE_NONE, 'Drop tables before import')
31
            ->setDescription('Imports database with mysql cli client according to database defined in local.xml');
32
33
        $help = <<<HELP
34
Imports an SQL file with mysql cli client into current configured database.
35
36
You need to have MySQL client tools installed on your system.
37
HELP;
38
        $this->setHelp($help);
39
    }
40
41
    /**
42
     * @return bool
43
     */
44
    public function isEnabled()
45
    {
46
        return Exec::allowed();
47
    }
48
49
    /**
50
     * Optimize a dump by converting single INSERTs per line to INSERTs with multiple lines
51
     * @param $fileName
52
     * @return string temporary filename
53
     */
54
    protected function optimize($fileName)
55
    {
56
        $in = fopen($fileName, 'r');
57
        $result = tempnam(sys_get_temp_dir(), 'dump') . '.sql';
58
        $out = fopen($result, 'w');
59
60
        fwrite($out, 'SET autocommit=0;' . "\n");
61
        $currentTable = '';
62
        $maxlen = 8 * 1024 * 1024; // 8 MB
63
        $len = 0;
64
        while ($line = fgets($in)) {
65
            if (strtolower(substr($line, 0, 11)) == 'insert into') {
66
                preg_match('/^insert into `(.*)` \([^)]*\) values (.*);/i', $line, $m);
67
68 View Code Duplication
                if (count($m) < 3) { // fallback for very long lines or other cases where the preg_match fails
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...
69
                    if ($currentTable != '') {
70
                        fwrite($out, ";\n");
71
                    }
72
                    fwrite($out, $line);
73
                    $currentTable = '';
74
                    continue;
75
                }
76
77
                $table = $m[1];
78
                $values = $m[2];
79
80
                if ($table != $currentTable || ($len > $maxlen - 1000)) {
81
                    if ($currentTable != '') {
82
                        fwrite($out, ";\n");
83
                    }
84
                    $currentTable = $table;
85
                    $insert = 'INSERT INTO `' . $table . '` VALUES ' . $values;
86
                    fwrite($out, $insert);
87
                    $len = strlen($insert);
88
                } else {
89
                    fwrite($out, ',' . $values);
90
                    $len += strlen($values) + 1;
91
                }
92 View Code Duplication
            } else {
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...
93
                if ($currentTable != '') {
94
                    fwrite($out, ";\n");
95
                    $currentTable = '';
96
                }
97
                fwrite($out, $line);
98
            }
99
        }
100
101
        fwrite($out, ";\n");
102
103
        fwrite($out, 'COMMIT;' . "\n");
104
105
        fclose($in);
106
        fclose($out);
107
108
        return $result;
109
    }
110
    /**
111
     * @param InputInterface  $input
112
     * @param OutputInterface $output
113
     *
114
     * @return int|void
115
     */
116
    protected function execute(InputInterface $input, OutputInterface $output)
117
    {
118
        $this->detectDbSettings($output);
119
120
        $this->writeSection($output, 'Import MySQL Database');
121
        $dbHelper = $this->getHelper('database');
122
123
        $fileName = $this->checkFilename($input);
124
125
        $compressor = $this->getCompressor($input->getOption('compression'));
0 ignored issues
show
Deprecated Code introduced by
The method N98\Magento\Command\Data...ommand::getCompressor() has been deprecated with message: Since 1.97.29; use AbstractCompressor::create() instead

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
127
        if ($input->getOption('optimize')) {
128
            if ($fileName === '-') {
129
                throw new InvalidArgumentException('Option --optimize not compatible with STDIN import');
130
            }
131
            if ($input->getOption('only-command')) {
132
                throw new InvalidArgumentException('Options --only-command and --optimize are not compatible');
133
            }
134
            if ($input->getOption('compression')) {
135
                throw new InvalidArgumentException('Options --compression and --optimize are not compatible');
136
            }
137
            $output->writeln('<comment>Optimizing <info>' . $fileName . '</info> to temporary file');
138
            $fileName = $this->optimize($fileName);
139
        }
140
141
        // create import command
142
        $exec = 'mysql ' . $dbHelper->getMysqlClientToolConnectionString();
143
        if ($fileName !== '-') {
144
            $exec = $compressor->getDecompressingCommand($exec, $fileName);
145
        }
146
147
        if ($input->getOption('only-command')) {
148
            $output->writeln($exec);
149
            return;
150
        } else {
151
            if ($input->getOption('only-if-empty')
152
                && count($dbHelper->getTables()) > 0
153
            ) {
154
                $output->writeln('<comment>Skip import. Database is not empty</comment>');
155
156
                return;
157
            }
158
        }
159
160
        if ($input->getOption('drop')) {
161
            $dbHelper->dropDatabase($output);
162
            $dbHelper->createDatabase($output);
163
        }
164
        if ($input->getOption('drop-tables')) {
165
            $dbHelper->dropTables($output);
166
        }
167
168
        $this->doImport($output, $fileName, $exec);
169
170
        if ($input->getOption('optimize')) {
171
            unlink($fileName);
172
        }
173
    }
174
175
    public function asText()
176
    {
177
        return parent::asText() . "\n" .
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Consol...mmand\Command::asText() has been deprecated with message: since version 2.3, to be removed in 3.0.

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...
178
            $this->getCompressionHelp();
179
    }
180
181
    /**
182
     * @param InputInterface $input
183
     *
184
     * @return mixed
185
     * @throws InvalidArgumentException
186
     */
187
    protected function checkFilename(InputInterface $input)
188
    {
189
        if ($input->getOption('stdin')) {
190
            return '-';
191
        }
192
        $fileName = $input->getArgument('filename');
193
        if (!file_exists($fileName)) {
194
            throw new InvalidArgumentException('File does not exist');
195
        }
196
        return $fileName;
197
    }
198
199
    /**
200
     * @param OutputInterface $output
201
     * @param string          $fileName
202
     * @param string          $exec
203
     *
204
     * @return void
205
     */
206
    protected function doImport(OutputInterface $output, $fileName, $exec)
207
    {
208
        $returnValue = null;
209
        $commandOutput = null;
210
        $output->writeln(
211
            '<comment>Importing SQL dump <info>' . $fileName . '</info> to database <info>'
212
            . $this->dbSettings['dbname'] . '</info>'
213
        );
214
215
        Exec::run($exec, $commandOutput, $returnValue);
216
217
        if ($returnValue != 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $returnValue of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
218
            $output->writeln('<error>' . $commandOutput . '</error>');
219
        }
220
        $output->writeln('<info>Finished</info>');
221
    }
222
}
223