Completed
Push — master ( c365cd...202f3c )
by Tom
05:19
created

ConflictsCommand::execute()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 38
rs 6.7272
c 2
b 0
f 0
cc 7
eloc 24
nc 7
nop 2
1
<?php
2
3
namespace N98\Magento\Command\Developer\Module\Rewrite;
4
5
use DateTime;
6
use Exception;
7
use Mage;
8
use N98\JUnitXml\Document as JUnitXmlDocument;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Zend_Text_Table;
13
14
class ConflictsCommand extends AbstractRewriteCommand
15
{
16
    protected function configure()
17
    {
18
        $this
19
            ->setName('dev:module:rewrite:conflicts')
20
            ->addOption('log-junit', null, InputOption::VALUE_REQUIRED,
21
                'Log conflicts in JUnit XML format to defined file.')
22
            ->setDescription('Lists all magento rewrite conflicts');
23
24
        $help = <<<HELP
25
Lists all duplicated rewrites and tells you which class is loaded by Magento.
26
The command checks class inheritance in order of your module dependencies.
27
28
* If a filename with `--log-junit` option is set the tool generates an XML file and no output to *stdout*.
29
30
Exit status is 0 if no conflicts were found, 1 if conflicts were found and 2 if there was a problem to
31
initialize Magento.
32
HELP;
33
        $this->setHelp($help);
34
    }
35
36
    /**
37
     * @param InputInterface  $input
38
     * @param OutputInterface $output
39
     *
40
     * @return int exit code: 0 no conflicts found, 1 conflicts found, 2 magento could not be initialized
41
     */
42
    protected function execute(InputInterface $input, OutputInterface $output)
43
    {
44
        $this->detectMagento($output, true);
45
        if (!$this->initMagento()) {
46
            return 2;
47
        }
48
49
        $conflicts = array();
50
        $time = microtime(true);
51
        $rewrites = $this->loadRewrites();
52
53
        foreach ($rewrites as $type => $data) {
54
            if (!is_array($data)) {
55
                continue;
56
            }
57
            foreach ($data as $class => $rewriteClasses) {
58
                if (!$this->_isInheritanceConflict($rewriteClasses)) {
59
                    continue;
60
                }
61
62
                $conflicts[] = array(
63
                    'Type'         => $type,
64
                    'Class'        => $class,
65
                    'Rewrites'     => implode(', ', $rewriteClasses),
66
                    'Loaded Class' => $this->_getLoadedClass($type, $class),
67
                );
68
            }
69
        }
70
71
        if ($input->getOption('log-junit')) {
72
            $duration = microtime($time) - $time;
73
            $this->logJUnit($conflicts, $input->getOption('log-junit'), $duration);
74
        } else {
75
            $this->writeOutput($output, $conflicts);
76
        }
77
78
        return (int) (bool) $conflicts;
79
    }
80
81
    /**
82
     * Returns loaded class by type like models or blocks
83
     *
84
     * @param string $type
85
     * @param string $class
86
     * @return string
87
     */
88
    protected function _getLoadedClass($type, $class)
89
    {
90
        switch ($type) {
91
            case 'blocks':
92
                return Mage::getConfig()->getBlockClassName($class);
93
94
            case 'helpers':
95
                return Mage::getConfig()->getHelperClassName($class);
96
97
            case 'models': // fall-through intended
98
            default:
99
                /** @noinspection PhpParamsInspection */
100
                return Mage::getConfig()->getModelClassName($class);
101
        }
102
    }
103
104
    /**
105
     * @param array  $conflicts
106
     * @param string $filename
107
     * @param float  $duration
108
     */
109
    protected function logJUnit(array $conflicts, $filename, $duration)
110
    {
111
        $document = new JUnitXmlDocument();
112
        $suite = $document->addTestSuite();
113
        $suite->setName('n98-magerun: ' . $this->getName());
114
        $suite->setTimestamp(new DateTime());
115
        $suite->setTime($duration);
116
117
        $testCase = $suite->addTestCase();
118
        $testCase->setName('Magento Rewrite Conflict Test');
119
        $testCase->setClassname('ConflictsCommand');
120
        foreach ($conflicts as $conflict) {
121
            $message = sprintf(
122
                'Rewrite conflict: Type %s | Class: %s, Rewrites: %s | Loaded class: %s',
123
                $conflict['Type'], $conflict['Class'], $conflict['Rewrites'], $conflict['Loaded Class']
124
            );
125
            $testCase->addFailure($message, 'MagentoRewriteConflictException');
126
        }
127
128
        $document->save($filename);
129
    }
130
131
    /**
132
     * Check if rewritten class has inherited the parent class.
133
     * If yes we have no conflict. The top class can extend every core class.
134
     * So we cannot check this.
135
     *
136
     * @var array $classes
137
     * @return bool
138
     */
139
    protected function _isInheritanceConflict($classes)
140
    {
141
        $count = count($classes);
142
143
        $classes = array_reverse($classes);
144
145
        for ($i = 1; $i < $count; $i++) {
146
            try {
147
                if (class_exists($classes[$i - 1])
148
                    && class_exists($classes[$i])
149
                ) {
150
                    if (!is_a($classes[$i - 1], $classes[$i], true)) {
151
                        return true;
152
                    }
153
                }
154
            } catch (Exception $e) {
155
                return true;
156
            }
157
        }
158
159
        return false;
160
    }
161
162
    /**
163
     * @param OutputInterface $output
164
     * @param array           $conflicts
165
     * @return int
166
     */
167
    private function writeOutput(OutputInterface $output, array $conflicts)
168
    {
169
        if (!$conflicts) {
170
            $output->writeln('<info>No rewrite conflicts were found.</info>');
171
            return;
172
        }
173
174
        $number = count($conflicts);
175
        $table = new Zend_Text_Table(array('columnWidths' => array(8, 30, 60, 60)));
176
177
        array_map(array($table, 'appendRow'), $conflicts);
178
        $output->write($table->render());
179
        $message = sprintf(
180
                '%d %s found!', $number, $number === 1 ? 'conflict was' : 'conflicts were'
181
        );
182
183
        $output->writeln('<error>' . $message . '</error>');
184
    }
185
}
186