Completed
Push — develop ( 8a0cb6...d5d9d0 )
by Tom
04:26
created

CompareVersionsCommand::sortAndDecorate()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
rs 9.2
cc 2
eloc 14
nc 1
nop 1
1
<?php
2
/*
3
 * this file is part of magerun
4
 *
5
 * @author Tom Klingenberg <https://github.com/ktomk>
6
 */
7
8
namespace N98\Magento\Command\System\Setup;
9
10
use N98\Util\ArrayFunctions;
11
use N98\Util\Console\Helper\Table\Renderer\RendererFactory;
12
use N98\Util\Console\Helper\TableHelper;
13
use N98\Util\JUnitSession;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
18
class CompareVersionsCommand extends AbstractSetupCommand
19
{
20
    /**
21
     * Setup
22
     */
23
    protected function configure()
24
    {
25
        $this
26
            ->setName('sys:setup:compare-versions')
27
            ->addOption('ignore-data', null, InputOption::VALUE_NONE, 'Ignore data updates')
28
            ->addOption('log-junit', null, InputOption::VALUE_REQUIRED, 'Log output to a JUnit xml file.')
29
            ->addOption(
30
                'format',
31
                null,
32
                InputOption::VALUE_OPTIONAL,
33
                'Output Format. One of [' . implode(',', RendererFactory::getFormats()) . ']'
34
            )
35
            ->setDescription('Compare module version with core_resource table.');
36
        $help = <<<HELP
37
Compares module version with saved setup version in `core_resource` table and displays version mismatch.
38
HELP;
39
        $this->setHelp($help);
40
    }
41
42
    /**
43
     * @param InputInterface $input
44
     * @param OutputInterface $output
45
     * @return int|null
46
     */
47
    protected function execute(InputInterface $input, OutputInterface $output)
48
    {
49
        $this->detectMagento($output, true);
50
51
        if (!$this->initMagento()) {
52
            return;
53
        }
54
55
        $junit = $input->getOption('log-junit') ? new JUnitSession($input->getOption('log-junit')) : null;
56
57
        $ignoreDataUpdate = $input->getOption('ignore-data');
58
        if ($ignoreDataUpdate) {
59
            $headers = array('Setup', 'Module', 'DB', 'Status');
60
        } else {
61
            $headers = array('Setup', 'Module', 'DB', 'Data', 'Status');
62
        }
63
64
        $table = $this->getModuleTable($ignoreDataUpdate, $headers, $errors);
65
66
        $this->output($input, $output, $headers, $table, $junit, $errors);
67
    }
68
69
    /**
70
     * @param array $data
71
     * @param JUnitSession $session
72
     */
73
    protected function logJUnit(array $data, JUnitSession $session)
74
    {
75
        $suite = $session->addTestSuite();
76
        $suite->setName('n98-magerun2: ' . $this->getName());
77
        $suite->setTimestamp(new \DateTime());
78
        $suite->setTime($session->getDuration());
79
80
        $testCase = $suite->addTestCase();
81
        $testCase->setName('Magento Setup Version Test');
82
        $testCase->setClassname('CompareVersionsCommand');
83
        if (count($data) > 0) {
84
            foreach ($data as $moduleSetup) {
85
                if (stristr($moduleSetup['Status'], 'error')) {
86
                    $testCase->addFailure(
87
                        'Setup Script Error',
88
                        'MagentoSetupScriptVersionException'
89
                    );
90
                }
91
            }
92
        }
93
94
        $session->save($session->getName());
95
    }
96
97
    /**
98
     * @param bool $ignoreDataUpdate
99
     * @param array $headers
100
     * @param int $errorCount
101
     * @return array
102
     */
103
    private function getModuleTable($ignoreDataUpdate, array $headers, &$errorCount)
104
    {
105
        $errorCount = 0;
106
        $table = array();
107
        foreach ($this->getMagentoModuleList() as $name => $module) {
108
            $row = $this->mapModuleToRow($name, $module, $ignoreDataUpdate, $errorCount);
109
            if ($ignoreDataUpdate) {
110
                unset($row['Data']);
111
            }
112
113
            $table[] = ArrayFunctions::columnOrder($headers, $row);
114
        }
115
116
        return $table;
117
    }
118
119
    private function testVersionProblem(array $row, $ignoreDataUpdate)
120
    {
121
        $moduleVersion = $row['Module'];
122
        $dbVersion = $row['DB'];
123
        $dataVersion = $row['Data'];
124
125
        $result = $dbVersion === $moduleVersion;
126
        if (!$ignoreDataUpdate && $result && $dataVersion !== $moduleVersion) {
127
            $result = false;
128
        }
129
130
        return $result;
131
    }
132
133
    /**
134
     * @param int $errorCount
135
     * @return string
136
     */
137
    private function buildSetupResultMessage($errorCount)
138
    {
139
        if (0 === $errorCount) {
140
            return 'No setup errors were found.';
141
        }
142
143
        $message = sprintf(
144
            '%s setup error%s %s found!',
145
            $errorCount,
146
            $errorCount === 1 ? '' : 's',
147
            $errorCount === 1 ? 'was' : 'were'
148
        );
149
150
        return $message;
151
    }
152
153
    /**
154
     * format highlight the status (green/red) and show error'd rows at bottom
155
     *
156
     * @param $table
157
     */
158
    private function sortAndDecorate(&$table)
159
    {
160
        usort($table, function ($a, $b) {
161
            if ($a['Status'] === $b['Status']) {
162
                return strcmp($a['Setup'], $b['Setup']);
163
            }
164
165
            return $a['Status'] !== 'OK';
166
        });
167
168
        array_walk($table, function (&$row) {
169
            $status = $row['Status'];
170
            $availableStatus = array('OK' => 'info', 'Error' => 'error');
171
            $statusString = sprintf(
172
                '<%s>%s</%s>',
173
                $availableStatus[$status],
174
                $status,
175
                $availableStatus[$status]
176
            );
177
            $row['Status'] = $statusString;
178
        });
179
    }
180
181
    /**
182
     * @param $ignoreDataUpdate
183
     * @param $errorCount
184
     * @param $name
185
     * @param $module
186
     * @return array
187
     */
188
    private function mapModuleToRow($name, $module, $ignoreDataUpdate, &$errorCount)
189
    {
190
        $resource = $this->getMagentoModuleResource();
191
192
        $row = array(
193
            'Setup'  => $name,
194
            'Module' => $module['setup_version'],
195
            'DB'     => $resource->getDbVersion($name),
196
            'Data'   => $resource->getDataVersion($name),
197
        );
198
199
        $test = $this->testVersionProblem($row, $ignoreDataUpdate);
200
201
        if (!$test) {
202
            $errorCount++;
203
        }
204
205
        $row['Status'] = $test ? 'OK' : 'Error';
206
207
        return $row;
208
    }
209
210
    /**
211
     * @param InputInterface $inputa
212
     * @param OutputInterface $output
213
     * @param $headers
214
     * @param $table
215
     * @param $junit
216
     * @param $errors
217
     */
218
    private function output(InputInterface $inputa, OutputInterface $output, $headers, $table, $junit, $errors)
219
    {
220
        if ($junit) {
221
            $this->logJUnit($table, $junit);
222
        } else {
223
            // sort errors to bottom and decorate status with colors if no output format is specified
224
            if (!$inputa->getOption('format')) {
225
                $this->sortAndDecorate($table);
226
            }
227
228
            /** @var $table TableHelper */
229
            $tableHelper = $this->getHelper('table');
230
            $tableHelper
231
                ->setHeaders($headers)
232
                ->renderByFormat($output, $table, $inputa->getOption('format'));
233
234
            // output summary line if no output format is specified
235
            if (!$inputa->getOption('format')) {
236
                $this->writeSection($output, $this->buildSetupResultMessage($errors), $errors ? 'error' : 'info');
237
            }
238
        }
239
    }
240
}
241