Completed
Pull Request — master (#177)
by
unknown
04:33
created

ImportCommand::checkFilename()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace N98\Magento\Command\Database;
4
5
use Symfony\Component\Console\Input\InputArgument;
6
use Symfony\Component\Console\Input\InputInterface;
7
use Symfony\Component\Console\Input\InputOption;
8
use Symfony\Component\Console\Output\OutputInterface;
9
10
class ImportCommand extends AbstractDatabaseCommand
11
{
12
    protected function configure()
13
    {
14
        $this
15
            ->setName('db:import')
16
            ->addArgument('filename', InputArgument::OPTIONAL, 'Dump filename')
17
            ->addOption('compression', 'c', InputOption::VALUE_REQUIRED, 'The compression of the specified file')
18
            ->addOption('only-command', null, InputOption::VALUE_NONE, 'Print only mysql command. Do not execute')
19
            ->addOption('only-if-empty', null, InputOption::VALUE_NONE, 'Imports only if database is empty')
20
            ->addOption('optimize', null, InputOption::VALUE_NONE, 'Convert verbose INSERTs to short ones before import (not working with compression)')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 152 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...
21
            ->addOption('drop', null, InputOption::VALUE_NONE, 'Drop and recreate database before import')
22
            ->addOption('drop-tables', null, InputOption::VALUE_NONE, 'Drop tables before import')
23
            ->setDescription('Imports database with mysql cli client according to database defined in local.xml');
24
25
        $help = <<<HELP
26
Imports an SQL file with mysql cli client into current configured database.
27
28
You need to have MySQL client tools installed on your system.
29
HELP;
30
        $this->setHelp($help);
31
32
    }
33
34
    /**
35
     * @return bool
36
     */
37
    public function isEnabled()
38
    {
39
        return function_exists('exec');
40
    }
41
42
    /**
43
     * Optimize a dump by converting single INSERTs per line to INSERTs with multiple lines
44
     * @param $fileName
45
     * @return string temporary filename
46
     */
47
    protected function optimize($fileName)
48
    {
49
        $in = fopen($fileName,'r');
50
        $result = tempnam(sys_get_temp_dir(), 'dump') . '.sql';
51
        $out = fopen($result, 'w');
52
53
        $currentTable = '';
54
        $maxlen = 8 * 1024 * 1024; // 8 MB
55
        $len = 0;
56
        while ($line = fgets($in)) {
57
            if (strtolower(substr($line, 0, 11)) == 'insert into') {
58
                preg_match('/^insert into `(.*)` \(.*\) values (.*);/i', $line, $m);
59
60 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...
61
                    if ($currentTable != '') {
62
                        fwrite($out, ";\n");
63
                    }
64
                    fwrite($out, $line);
65
                    $currentTable = '';
66
                    continue;
67
                }
68
69
                $table = $m[1];
70
                $values = $m[2];
71
72
                if ($table != $currentTable or ($len > $maxlen - 1000)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
73
                    if ($currentTable != '') {
74
                        fwrite($out, ";\n\n");
75
                    }
76
                    $currentTable = $table;
77
                    $insert = 'INSERT INTO `' . $table . '` VALUES ' . $values;
78
                    fwrite($out, $insert);
79
                    $len = strlen($insert);
80
                } else {
81
                    fwrite($out, ',' . $values);
82
                    $len += strlen($values) + 1;
83
                }
84 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...
85
                if ($currentTable != '') {
86
                    fwrite($out, ";\n");
87
                    $currentTable = '';
88
                }
89
                fwrite($out, $line);
90
            }
91
92
        }
93
        fclose($in);
94
        fclose($out);
95
96
        return $result;
97
98
    }
99
    /**
100
     * @param \Symfony\Component\Console\Input\InputInterface $input
101
     * @param \Symfony\Component\Console\Output\OutputInterface $output
102
     * @return int|void
103
     */
104
    protected function execute(InputInterface $input, OutputInterface $output)
105
    {
106
        $this->detectDbSettings($output);
107
        $this->writeSection($output, 'Import MySQL Database');
108
        $dbHelper = $this->getHelper('database');
109
110
        $fileName = $this->checkFilename($input);
111
112
        $compressor = $this->getCompressor($input->getOption('compression'));
113
114
        // create import command
115
        $exec = $compressor->getDecompressingCommand(
116
            'mysql ' . $dbHelper->getMysqlClientToolConnectionString(),
117
            $fileName
118
        );
119
        if ($input->getOption('only-command')) {
120
            $output->writeln($exec);
121
122
            return;
123
        } else {
124
            if ($input->getOption('only-if-empty')
125
                && count($dbHelper->getTables()) > 0
126
            ) {
127
                $output->writeln('<comment>Skip import. Database is not empty</comment>');
128
129
                return;
130
            }
131
        }
132
133
        if ($input->getOption('optimize')) {
134
            if ($input->getOption('compression')) {
135
                throw new \Exception('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
        if( $input->getOption('drop') ) {
142
            $dbHelper->dropDatabase($output);
143
            $dbHelper->createDatabase($output);
144
        }
145
        if( $input->getOption('drop-tables') ) {
146
            $dbHelper->dropTables($output);
147
        }
148
149
150
151
152
            $this->doImport($output, $fileName, $exec);
153
154
        if ($input->getOption('optimize')) {
155
            unlink($fileName);
156
        }
157
    }
158
159
    public function asText() {
160
        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...
161
            $this->getCompressionHelp();
162
    }
163
164
    /**
165
     * @param InputInterface $input
166
     *
167
     * @return mixed
168
     * @throws \InvalidArgumentException
169
     */
170
    protected function checkFilename(InputInterface $input)
171
    {
172
        $fileName = $input->getArgument('filename');
173
        if (!file_exists($fileName)) {
174
            throw new \InvalidArgumentException('File does not exist');
175
        }
176
        return $fileName;
177
    }
178
179
    /**
180
     * @param OutputInterface $output
181
     * @param string          $fileName
182
     * @param string          $exec
183
     *
184
     * @return void
185
     */
186
    protected function doImport(OutputInterface $output, $fileName, $exec)
187
    {
188
        $returnValue = null;
189
        $commandOutput = null;
190
        $output->writeln(
191
            '<comment>Importing SQL dump <info>' . $fileName . '</info> to database <info>'
192
            . $this->dbSettings['dbname'] . '</info>'
193
        );
194
        exec($exec, $commandOutput, $returnValue);
195
        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...
196
            $output->writeln('<error>' . implode(PHP_EOL, $commandOutput) . '</error>');
197
        }
198
        $output->writeln('<info>Finished</info>');
199
    }
200
}
201