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

CheckTablesCommand::progressAdvance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 0
1
<?php
2
3
namespace N98\Magento\Command\Database\Maintain;
4
5
use N98\Magento\Command\AbstractMagentoCommand;
6
use Symfony\Component\Console\Input\InputInterface;
7
use Symfony\Component\Console\Input\InputOption;
8
use Symfony\Component\Console\Output\OutputInterface;
9
use N98\Util\Console\Helper\Table\Renderer\RendererFactory;
10
11
class CheckTablesCommand extends AbstractMagentoCommand
12
{
13
    const MESSAGE_CHECK_NOT_SUPPORTED = 'The storage engine for the table doesn\'t support check';
14
    const MESSAGE_REPAIR_NOT_SUPPORTED = 'The storage engine for the table doesn\'t support repair';
15
16
    /**
17
     * @var \Symfony\Component\Console\Input\InputInterface
18
     */
19
    protected $input = null;
20
21
    /**
22
     * @var \Symfony\Component\Console\Output\OutputInterface
23
     */
24
    protected $output = null;
25
26
    /**
27
     * @var \N98\Util\Console\Helper\DatabaseHelper
28
     */
29
    protected $dbHelper = null;
30
31
    /**
32
     * @var bool
33
     */
34
    protected $showProgress = false;
35
36
    /**
37
     * @var array
38
     */
39
    protected $allowedTypes = array(
40
        'QUICK',
41
        'FAST',
42
        'CHANGED',
43
        'MEDIUM',
44
        'EXTENDED',
45
    );
46
47
    protected function configure()
48
    {
49
        $help = <<<'HELP'
50
<comment>TYPE OPTIONS</comment>
51
52
<info>QUICK</info>
53
            Do not scan the rows to check for incorrect links.
54
            Applies to InnoDB and MyISAM tables and views.
55
<info>FAST</info>
56
            Check only tables that have not been closed properly.
57
            Applies only to MyISAM tables and views; ignored for InnoDB.
58
<info>CHANGED</info>
59
            Check only tables that have been changed since the last check or that
60
            have not been closed properly. Applies only to MyISAM tables and views;
61
            ignored for InnoDB.
62
<info>MEDIUM</info>
63
            Scan rows to verify that deleted links are valid.
64
            This also calculates a key checksum for the rows and verifies this with a
65
            calculated checksum for the keys. Applies only to MyISAM tables and views;
66
            ignored for InnoDB.
67
<info>EXTENDED</info>
68
            Do a full key lookup for all keys for each row. This ensures that the table
69
            is 100% consistent, but takes a long time.
70
            Applies only to MyISAM tables and views; ignored for InnoDB.
71
72
<comment>InnoDB</comment>
73
            InnoDB tables will be optimized with the ALTER TABLE ... ENGINE=InnoDB statement.
74
            The options above do not apply to them.
75
HELP;
76
77
        $this
78
            ->setName('db:maintain:check-tables')
79
            ->setDescription('Check database tables')
80
            ->addOption('type', null, InputOption::VALUE_OPTIONAL, 'Check type (one of QUICK, FAST, MEDIUM, EXTENDED, CHANGED)', 'MEDIUM')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 138 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...
81
            ->addOption('repair', null, InputOption::VALUE_NONE, 'Repair tables (only MyISAM)')
82
            ->addOption('table', null, InputOption::VALUE_OPTIONAL, 'Process only given table (wildcards are supported)')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 121 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...
83
            ->addOption(
84
                'format',
85
                null,
86
                InputOption::VALUE_OPTIONAL,
87
                'Output Format. One of [' . implode(',', RendererFactory::getFormats()) . ']'
88
            )
89
            ->setHelp($help);;
90
    }
91
92
    /**
93
     * @throws \InvalidArgumentException
94
     *
95
     */
96
    protected function isTypeAllowed()
97
    {
98
        $type = $this->input->getOption('type');
99
        $type = strtoupper($type);
100
        if ($type && !in_array($type, $this->allowedTypes)) {
101
            throw new \InvalidArgumentException('Invalid type was given');
102
        }
103
    }
104
105
    protected function progressAdvance()
106
    {
107
        if ($this->showProgress) {
108
            $this->getHelper('progress')->advance();
109
        }
110
    }
111
112
    /**
113
     * @param \Symfony\Component\Console\Input\InputInterface   $input
114
     * @param \Symfony\Component\Console\Output\OutputInterface $output
115
     *
116
     * @throws \InvalidArgumentException
117
     * @return int|void
118
     */
119
    protected function execute(InputInterface $input, OutputInterface $output)
120
    {
121
        $this->input  = $input;
122
        $this->output = $output;
123
        $this->isTypeAllowed();
124
        $this->detectMagento($output);
125
        $this->dbHelper     = $this->getHelper('database');
126
        $this->showProgress = $input->getOption('format') == null;
127
128
        if ($input->getOption('table')) {
129
            $resolvedTables = array(
130
                $this->dbHelper->resolveTables(
131
                    array('@check'),
132
                    array(
133
                        'check' => array(
134
                            'tables' => $input->getOption('table')
135
                        )
136
                    )
137
                )
138
            );
139
            $tables         = $resolvedTables[0];
140
        } else {
141
            $tables = $this->dbHelper->getTables();
142
        }
143
144
        $allTableStatus = $this->dbHelper->getTablesStatus();
145
146
        $tableOutput = array();
147
        /** @var \Symfony\Component\Console\Helper\ProgressHelper $progress */
148
        $progress = $this->getHelper('progress');
149
        if ($this->showProgress) {
150
            $progress->start($output, count($tables));
151
        }
152
153
        $methods = array(
154
            'InnoDB' => 1,
155
            'MEMORY' => 1,
156
            'MyISAM' => 1,
157
        );
158
159
        foreach ($tables as $tableName) {
160
161
            if (isset($allTableStatus[$tableName]) && isset($methods[$allTableStatus[$tableName]['Engine']])) {
162
                $m           = '_check' . $allTableStatus[$tableName]['Engine'];
163
                $tableOutput = array_merge($tableOutput, $this->$m($tableName));
164
            } else {
165
                $tableOutput[] = array(
166
                    'table'     => $tableName,
167
                    'operation' => 'not supported',
168
                    'type'      => '',
169
                    'status'    => '',
170
                );
171
            }
172
            $this->progressAdvance();
173
        }
174
175
        if ($this->showProgress) {
176
            $progress->finish();
177
        }
178
179
        $this->getHelper('table')
180
            ->setHeaders(array('Table', 'Operation', 'Type', 'Status'))
181
            ->renderByFormat($this->output, $tableOutput, $this->input->getOption('format'));
182
    }
183
184
    /**
185
     * @param string $tableName
186
     * @param string $engine
187
     *
188
     * @return array
189
     */
190
    protected function _queryAlterTable($tableName, $engine)
191
    {
192
        /** @var \PDO $connection */
193
        $connection   = $this->dbHelper->getConnection($this->output);
194
        $start        = microtime(true);
195
        $affectedRows = $connection->exec(sprintf('ALTER TABLE %s ENGINE=%s', $tableName, $engine));
196
197
        return array(array(
198
            'table'     => $tableName,
199
            'operation' => 'ENGINE ' . $engine,
200
            'type'      => sprintf('%15s rows', (string)$affectedRows),
201
            'status'    => sprintf('%.3f secs', microtime(true) - $start),
202
        )
203
        );
204
    }
205
206
    /**
207
     * @param string $tableName
208
     *
209
     * @return array
210
     */
211
    protected function _checkInnoDB($tableName)
212
    {
213
        return $this->_queryAlterTable($tableName, 'InnoDB');
214
    }
215
216
    /**
217
     * @param string $tableName
218
     *
219
     * @return array
220
     */
221
    protected function _checkMEMORY($tableName)
222
    {
223
        return $this->_queryAlterTable($tableName, 'MEMORY');
224
    }
225
226
    /**
227
     * @param string $tableName
228
     *
229
     * @return array
230
     */
231
    protected function _checkMyISAM($tableName)
232
    {
233
        $table  = array();
234
        $type   = $this->input->getOption('type');
235
        $result = $this->_query(sprintf('CHECK TABLE %s %s', $tableName, $type));
236
        if ($result['Msg_text'] == self::MESSAGE_CHECK_NOT_SUPPORTED) {
237
            return array();
238
        }
239
240
        $table[] = array(
241
            'table'     => $tableName,
242
            'operation' => $result['Op'],
243
            'type'      => $type,
244
            'status'    => $result['Msg_text'],
245
        );
246
247
        if ($result['Msg_text'] != 'OK'
248
            && $this->input->getOption('repair')
249
        ) {
250
            $result = $this->_query(sprintf('REPAIR TABLE %s %s', $tableName, $type));
251
            if ($result['Msg_text'] != self::MESSAGE_REPAIR_NOT_SUPPORTED) {
252
                $table[] = array(
253
                    'table'     => $tableName,
254
                    'operation' => $result['Op'],
255
                    'type'      => $type,
256
                    'status'    => $result['Msg_text'],
257
                );
258
            }
259
        }
260
        return $table;
261
    }
262
263
    /**
264
     * @param string $sql
265
     *
266
     * @return array|bool
267
     */
268
    protected function _query($sql)
269
    {
270
        /** @var \PDO $connection */
271
        $connection = $this->dbHelper->getConnection($this->output);
272
        $query      = $connection->prepare($sql);
273
        $query->execute();
274
        $result = $query->fetch(\PDO::FETCH_ASSOC);
275
        return $result;
276
    }
277
}
278