Completed
Pull Request — develop (#528)
by Steve
05:09
created

ConflictsCommand::_isClassInstanceOf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 8
rs 9.4286
cc 2
eloc 5
nc 2
nop 2
1
<?php
2
3
namespace N98\Magento\Command\Developer\Module\Rewrite;
4
5
use Exception;
6
use N98\JUnitXml\Document as JUnitXmlDocument;
7
use Symfony\Component\Console\Input\InputInterface;
8
use Symfony\Component\Console\Input\InputOption;
9
use Symfony\Component\Console\Output\OutputInterface;
10
11
class ConflictsCommand extends AbstractRewriteCommand
12
{
13
    protected function configure()
14
    {
15
        $this
16
            ->setName('dev:module:rewrite:conflicts')
17
            ->addOption('log-junit', null, InputOption::VALUE_REQUIRED, 'Log conflicts in JUnit XML format to defined file.')
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 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...
18
            ->setDescription('Lists all magento rewrite conflicts')
19
        ;
20
21
        $help = <<<HELP
22
Lists all duplicated rewrites and tells you which class is loaded by Magento.
23
The command checks class inheritance in order of your module dependencies.
24
25
* If a filename with `--log-junit` option is set the tool generates an XML file and no output to *stdout*.
26
HELP;
27
        $this->setHelp($help);
28
    }
29
30
    /**
31
     * @param InputInterface  $input
32
     * @param OutputInterface $output
33
     *
34
     * @return int|void
35
     */
36
    protected function execute(InputInterface $input, OutputInterface $output)
37
    {
38
        $this->detectMagento($output, true);
39
        if ($this->initMagento()) {
40
            $table = new \Zend_Text_Table(array('columnWidths' => array(8, 30, 60, 60)));
41
            $tableData = array();
42
            if ($this->initMagento()) {
43
                $time = microtime(true);
44
                $rewrites = $this->loadRewrites();
45
                $conflictCounter = 0;
46
                foreach ($rewrites as $type => $data) {
47
                    if (count($data) > 0 && is_array($data)) {
48
                        foreach ($data as $class => $rewriteClass) {
49
                            if (count($rewriteClass) > 1) {
50
                                if ($this->_isInheritanceConflict($rewriteClass)) {
51
                                    $tableData[] = array(
52
                                        'Type'         => $type,
53
                                        'Class'        => $class,
54
                                        'Rewrites'     => implode(', ', $rewriteClass),
55
                                        'Loaded Class' => $this->_getLoadedClass($type, $class),
56
                                    );
57
                                    $conflictCounter++;
58
                                }
59
                            }
60
                        }
61
                    }
62
                }
63
64
                if ($input->getOption('log-junit')) {
65
                    $this->logJUnit($tableData, $input->getOption('log-junit'), microtime($time) - $time);
66
                } else {
67
                    if ($conflictCounter > 0) {
68
                        array_map(array($table, 'appendRow'), $tableData);
69
                        $output->write($table->render());
70
                        $message = sprintf(
71
                            '%d %s found!',
72
                            $conflictCounter,
73
                            $conflictCounter == 1 ? 'conflict was' : 'conflicts were'
74
                        );
75
                        $output->writeln('<error>' . $message . '</error>');
76
                        return 1;
77
                    } else {
78
                        $output->writeln('<info>No rewrite conflicts were found.</info>');
79
                    }
80
                }
81
            }
82
        }
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
            default:
102
            case 'models':
0 ignored issues
show
Unused Code introduced by
case 'models': retur...ame($class); break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
103
                return \Mage::getConfig()->getModelClassName($class);
104
                break;
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
        if (count($conflicts) > 0) {
125
            foreach ($conflicts as $conflictRow) {
126
                $testCase->addFailure(
127
                    sprintf(
128
                        'Rewrite conflict: Type %s | Class: %s, Rewrites: %s | Loaded class: %s',
129
                        $conflictRow['Type'],
130
                        $conflictRow['Class'],
131
                        $conflictRow['Rewrites'],
132
                        $conflictRow['Loaded Class']
133
                    ),
134
                    'MagentoRewriteConflictException'
135
                );
136
            }
137
        }
138
139
        $document->save($filename);
140
    }
141
142
    /**
143
     * Check if rewritten class has inherited the parent class.
144
     * If yes we have no conflict. The top class can extend every core class.
145
     * So we cannot check this.
146
     *
147
     * @param  array $classes
148
     * @return bool
149
     */
150
    protected function _isInheritanceConflict(array $classes)
151
    {
152
        $classes = array_reverse($classes);
153
        $count   = count($classes) - 1;
154
        for ($i = 0; $i < $count; $i++) {
155
            try {
156
                if (class_exists($classes[$i])
157
                    && class_exists($classes[$i + 1])
158
                ) {
159
                    return !$this->_isClassInstanceOf(
160
                        $classes[$i],
161
                        $classes[$i + 1]
162
                    );
163
                }
164
            } catch (Exception $e) {
165
                return true;
166
            }
167
        }
168
169
        return false;
170
    }
171
172
    /**
173
     * Check if a classA is an instance of classB
174
     *
175
     * @param  object|string  $classA
176
     * @param  object|string  $classB
177
     * @return boolean
178
     */
179
    private function _isClassInstanceOf($classA, $classB)
180
    {
181
        if (is_string($classA)) {
182
            $classA = new $classA;
183
        }
184
        $reflectionB = new \ReflectionClass($classB);
185
        return $reflectionB->isInstance($classA);
186
    }
187
}
188