Completed
Push — develop ( fae898...646b61 )
by Tom
04:48
created

ConflictsCommand::_isInheritanceConflict()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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