Completed
Push — develop ( 33d5a5...a5f0b9 )
by Christian
02:26
created

src/N98/Magento/Command/Database/ImportCommand.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace N98\Magento\Command\Database;
4
5
use Exception;
6
use N98\Magento\Command\Database\Compressor\AbstractCompressor;
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
/**
13
 * Class ImportCommand
14
 * @package N98\Magento\Command\Database
15
 */
16
class ImportCommand extends AbstractDatabaseCommand
17
{
18
    protected function configure()
19
    {
20
        parent::configure();
21
        $this
22
            ->setName('db:import')
23
            ->addArgument('filename', InputArgument::OPTIONAL, 'Dump filename')
24
            ->addOption('compression', 'c', InputOption::VALUE_REQUIRED, 'The compression of the specified file')
25
            ->addOption('only-command', null, InputOption::VALUE_NONE, 'Print only mysql command. Do not execute')
26
            ->addOption('only-if-empty', null, InputOption::VALUE_NONE, 'Imports only if database is empty')
27
            ->addOption(
28
                'optimize',
29
                null,
30
                InputOption::VALUE_NONE,
31
                'Convert verbose INSERTs to short ones before import (not working with compression)'
32
            )
33
            ->addOption('drop', null, InputOption::VALUE_NONE, 'Drop and recreate database before import')
34
            ->addOption('drop-tables', null, InputOption::VALUE_NONE, 'Drop tables before import')
35
            ->addOption('force', null, InputOption::VALUE_NONE, 'Continue even if an SQL error occurs')
36
            ->setDescription('Imports database with mysql cli client according to database defined in env.php');
37
38
        $help = <<<HELP
39
Imports an SQL file with mysql cli client into current configured database.
40
41
You need to have MySQL client tools installed on your system.
42
HELP;
43
        $this->setHelp($help);
44
    }
45
46
    /**
47
     * @return bool
48
     */
49
    public function isEnabled()
50
    {
51
        return function_exists('exec');
52
    }
53
54
    /**
55
     * Optimize a dump by converting single INSERTs per line to INSERTs with multiple lines
56
     * as well as wrapping everything into one transaction.
57
     * @param $fileName
58
     * @return string temporary filename
59
     */
60
    protected function optimize($fileName)
61
    {
62
        $in = fopen($fileName, 'r');
63
        $result = tempnam(sys_get_temp_dir(), 'dump') . '.sql';
64
        $out = fopen($result, 'w');
65
        fwrite($out, 'SET autocommit=0;' . "\n");
66
        $currentTable = '';
67
        $maxlen = 8 * 1024 * 1024; // 8 MB
68
        $len = 0;
69
        while ($line = fgets($in)) {
70
            if (strtolower(substr($line, 0, 11)) == 'insert into') {
71
                preg_match('/^insert into `(.*)` \([^)]*\) values (.*);/i', $line, $m);
72 View Code Duplication
                if (count($m) < 3) { // fallback for very long lines or other cases where the preg_match fails
73
                    if ($currentTable != '') {
74
                        fwrite($out, ";\n");
75
                    }
76
                    fwrite($out, $line);
77
                    $currentTable = '';
78
                    continue;
79
                }
80
                $table = $m[1];
81
                $values = $m[2];
82
                if ($table != $currentTable || ($len > $maxlen - 1000)) {
83
                    if ($currentTable != '') {
84
                        fwrite($out, ";\n");
85
                    }
86
                    $currentTable = $table;
87
                    $insert = 'INSERT INTO `' . $table . '` VALUES ' . $values;
88
                    fwrite($out, $insert);
89
                    $len = strlen($insert);
90
                } else {
91
                    fwrite($out, ',' . $values);
92
                    $len += strlen($values) + 1;
93
                }
94 View Code Duplication
            } else {
95
                if ($currentTable != '') {
96
                    fwrite($out, ";\n");
97
                    $currentTable = '';
98
                }
99
                fwrite($out, $line);
100
            }
101
        }
102
        fwrite($out, ";\n");
103
        fwrite($out, 'COMMIT;' . "\n");
104
        fclose($in);
105
        fclose($out);
106
        return $result;
107
    }
108
109
    /**
110
     * @param InputInterface $input
111
     * @param OutputInterface $output
112
     * @return int|null|void
113
     * @throws Exception
114
     */
115
    protected function execute(InputInterface $input, OutputInterface $output)
116
    {
117
        $this->detectDbSettings($output);
118
        $this->writeSection($output, 'Import MySQL Database');
119
        $dbHelper = $this->getHelper('database');
120
121
        $fileName = $this->checkFilename($input);
122
123
        $compressor = AbstractCompressor::create($input->getOption('compression'));
124
125
        $exec = 'mysql ';
126
        if ($input->getOption('force')) {
127
            $exec = 'mysql --force ';
128
        }
129
130
        // create import command
131
        $exec = $compressor->getDecompressingCommand(
132
            $exec . $dbHelper->getMysqlClientToolConnectionString(),
133
            $fileName
134
        );
135
        if ($input->getOption('only-command')) {
136
            $output->writeln($exec);
137
138
            return;
139
        } elseif ($input->getOption('only-if-empty')
140
            && count($dbHelper->getTables()) > 0
141
        ) {
142
            $output->writeln('<comment>Skip import. Database is not empty</comment>');
143
144
            return;
145
        }
146
147
        if ($input->getOption('optimize')) {
148
            if ($input->getOption('compression')) {
149
                throw new Exception('Options --compression and --optimize are not compatible');
150
            }
151
            $output->writeln('<comment>Optimizing <info>' . $fileName . '</info> to temporary file');
152
            $fileName = $this->optimize($fileName);
153
        }
154
155
        if ($input->getOption('drop')) {
156
            $dbHelper->dropDatabase($output);
157
            $dbHelper->createDatabase($output);
158
        }
159
        if ($input->getOption('drop-tables')) {
160
            $dbHelper->dropTables($output);
161
        }
162
163
        $this->doImport($output, $fileName, $exec);
0 ignored issues
show
It seems like $fileName defined by $this->checkFilename($input) on line 121 can also be of type array<integer,string> or null; however, N98\Magento\Command\Data...portCommand::doImport() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
164
165
        if ($input->getOption('optimize')) {
166
            unlink($fileName);
167
        }
168
    }
169
170
    public function asText()
171
    {
172
        return parent::asText() . "\n" .
173
        $this->getCompressionHelp();
174
    }
175
176
    /**
177
     * @param InputInterface $input
178
     *
179
     * @return mixed
180
     * @throws \InvalidArgumentException
181
     */
182
    protected function checkFilename(InputInterface $input)
183
    {
184
        $fileName = $input->getArgument('filename');
185
        if (!file_exists($fileName)) {
186
            throw new \InvalidArgumentException('File does not exist');
187
        }
188
189
        return $fileName;
190
    }
191
192
    /**
193
     * @param OutputInterface $output
194
     * @param string $fileName
195
     * @param string $exec
196
     *
197
     * @return void
198
     */
199
    protected function doImport(OutputInterface $output, $fileName, $exec)
200
    {
201
        $returnValue = null;
202
        $commandOutput = null;
203
        $output->writeln(
204
            '<comment>Importing SQL dump <info>' . $fileName . '</info> to database <info>'
205
            . $this->dbSettings['dbname'] . '</info>'
206
        );
207
        exec($exec, $commandOutput, $returnValue);
208
        if ($returnValue != 0) {
209
            $output->writeln('<error>' . implode(PHP_EOL, $commandOutput) . '</error>');
210
        }
211
        $output->writeln('<info>Finished</info>');
212
    }
213
}
214