Test Failed
Push — master ( d4bdbc...950b45 )
by Arne
02:36
created

InheritanceResolver   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 282
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 51
dl 0
loc 282
rs 8.3206
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
B processImplements() 0 25 5
A setupDependencies() 0 15 3
A getUnresolved() 0 2 1
A hasErrors() 0 2 1
A __construct() 0 2 1
A getErrors() 0 2 1
A getUnitByName() 0 7 3
A addError() 0 6 2
D processExtends() 0 40 10
A addUnresolved() 0 7 2
C processTraitUse() 0 29 7
A hasUnresolved() 0 2 1
C resolve() 0 61 14

How to fix   Complexity   

Complex Class

Complex classes like InheritanceResolver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use InheritanceResolver, and based on these observations, apply Extract Interface, too.

1
<?php
2
    /**
3
     * Copyright (c) 2010-2018 Arne Blankerts <[email protected]>
4
     * All rights reserved.
5
     *
6
     * Redistribution and use in source and binary forms, with or without modification,
7
     * are permitted provided that the following conditions are met:
8
     *
9
     *   * Redistributions of source code must retain the above copyright notice,
10
     *     this list of conditions and the following disclaimer.
11
     *
12
     *   * Redistributions in binary form must reproduce the above copyright notice,
13
     *     this list of conditions and the following disclaimer in the documentation
14
     *     and/or other materials provided with the distribution.
15
     *
16
     *   * Neither the name of Arne Blankerts nor the names of contributors
17
     *     may be used to endorse or promote products derived from this software
18
     *     without specific prior written permission.
19
     *
20
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  * NOT LIMITED TO,
22
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
24
     * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25
     * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
     * POSSIBILITY OF SUCH DAMAGE.
31
     *
32
     * @package    phpDox
33
     * @author     Arne Blankerts <[email protected]>
34
     * @copyright  Arne Blankerts <[email protected]>, All rights reserved.
35
     * @license    BSD License
36
     */
37
namespace TheSeer\phpDox\Collector {
38
39
    use TheSeer\fDOM\fDOMDocument;
40
    use TheSeer\phpDox\InheritanceConfig;
41
    use TheSeer\phpDox\ProgressLogger;
42
43
    /**
44
     * Inheritance resolving class
45
     */
46
    class InheritanceResolver {
47
48
        /**
49
         * @var ProgressLogger
50
         */
51
        private $logger;
52
53
        /**
54
         * @var \TheSeer\phpDox\Collector\Project
55
         */
56
        private $project;
57
58
        /**
59
         * @var InheritanceConfig
60
         */
61
        private $config;
62
63
        private $dependencyStack = array();
64
65
        /**
66
         * @var array
67
         */
68
        private $unresolved = array();
69
70
        /**
71
         * @var array
72
         */
73
        private $errors = array();
74
75
        /**
76
         * @param ProgressLogger $logger
77
         */
78
        public function __construct(ProgressLogger $logger) {
79
            $this->logger = $logger;
80
        }
81
82
        /**
83
         * @param array             $changed
84
         * @param Project           $project
85
         * @param InheritanceConfig $config
86
         *
87
         * @throws ProjectException
88
         * @throws UnitObjectException
89
         */
90
        public function resolve(Array $changed, Project $project, InheritanceConfig $config) {
91
            if (count($changed) == 0) {
92
                return;
93
            }
94
            $this->logger->reset();
95
            $this->logger->log("Resolving inheritance\n");
96
97
            $this->project = $project;
98
            $this->config = $config;
99
100
            $this->setupDependencies();
101
102
            foreach($changed as $unit) {
103
                /** @var AbstractUnitObject $unit */
104
                if ($unit->hasExtends()) {
105
                    foreach($unit->getExtends() as $name) {
106
                        try {
107
                            $extendedUnit = $this->getUnitByName($name);
108
                            $this->processExtends($unit, $extendedUnit);
109
                        } catch (ProjectException $e) {
110
                            $this->addUnresolved($unit, $name);
111
                        }
112
                    }
113
                }
114
                if ($unit->hasImplements()) {
115
                    foreach($unit->getImplements() as $implements) {
116
                        try {
117
                            $implementsUnit = $this->getUnitByName($implements);
118
                            $this->processImplements($unit, $implementsUnit);
119
                        } catch (ProjectException $e) {
120
                            $this->addUnresolved($unit, $implements);
121
                        }
122
                    }
123
                }
124
                if ($unit->usesTraits()) {
125
                    foreach($unit->getUsedTraits() as $traitName) {
126
                        try {
127
                            $traitUnit = $this->getUnitByName($traitName);
128
                            $this->processTraitUse(
129
                                $unit,
130
                                $unit->getTraitUse($traitName),
131
                                $traitUnit
132
                            );
133
                        } catch (ProjectException $e) {
134
                            $this->addUnresolved($unit, $traitName);
135
                        }
136
                    }
137
                }
138
139
                $unitName = $unit->getName();
140
                if (isset($this->unresolved[$unitName])) {
141
                    foreach($this->unresolved[$unitName] as $missingUnit) {
142
                        $unit->markDependencyAsUnresolved($missingUnit);
143
                    }
144
                }
145
146
                $this->logger->progress('processed');
147
            }
148
149
            $this->project->save();
150
            $this->logger->completed();
151
        }
152
153
        public function hasUnresolved() {
154
            return count($this->unresolved) > 0;
155
        }
156
157
        public function getUnresolved() {
158
            return $this->unresolved;
159
        }
160
161
        public function hasErrors() {
162
            return count($this->errors) > 0;
163
        }
164
165
        public function getErrors() {
166
            return $this->errors;
167
        }
168
169
        private function addError(AbstractUnitObject $unit, $errorInfo) {
170
            $unitName = $unit->getName();
171
            if (!isset($this->errors[$unitName])) {
172
                $this->errors[$unitName] = array();
173
            }
174
            $this->errors[$unitName][] = $errorInfo;
175
        }
176
177
        private function addUnresolved(AbstractUnitObject $unit, $missingUnit) {
178
            $unitName = $unit->getName();
179
            if (!isset($this->unresolved[$unitName])) {
180
                $this->unresolved[$unitName] = array();
181
            }
182
            $this->unresolved[$unitName][] = $missingUnit;
183
            $this->project->registerForSaving($unit);
184
        }
185
186
        private function processExtends(AbstractUnitObject $unit, AbstractUnitObject $extends) {
187
            $this->project->registerForSaving($unit);
188
            $this->project->registerForSaving($extends);
189
190
            $extends->addExtender($unit);
191
            $unit->importExports($extends, 'parent');
192
193
            if ($extends->hasExtends()) {
194
                foreach($extends->getExtends() as $name) {
195
                    try {
196
                        $extendedUnit = $this->getUnitByName($name);
197
                        $this->processExtends($unit, $extendedUnit, $extendedUnit);
0 ignored issues
show
Unused Code introduced by Arne Blankerts
The call to TheSeer\phpDox\Collector...olver::processExtends() has too many arguments starting with $extendedUnit. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

197
                        $this->/** @scrutinizer ignore-call */ 
198
                               processExtends($unit, $extendedUnit, $extendedUnit);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
198
                    } catch (ProjectException $e) {
199
                        $this->addUnresolved($unit, $name);
200
                    }
201
                }
202
            }
203
204
            if ($extends->hasImplements()) {
205
                foreach($extends->getImplements() as $implements) {
206
                    try {
207
                        $implementsUnit = $this->getUnitByName($implements);
208
                        $this->processImplements($unit, $implementsUnit, $implementsUnit);
0 ignored issues
show
Unused Code introduced by Arne Blankerts
The call to TheSeer\phpDox\Collector...er::processImplements() has too many arguments starting with $implementsUnit. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

208
                        $this->/** @scrutinizer ignore-call */ 
209
                               processImplements($unit, $implementsUnit, $implementsUnit);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
209
                    } catch (ProjectException $e) {
210
                        $this->addUnresolved($unit, $implements);
211
                    }
212
                }
213
            }
214
215
            if ($extends->usesTraits()) {
216
                foreach($extends->getUsedTraits() as $traitName) {
217
                    try {
218
                        $traitUnit = $this->getUnitByName($traitName);
219
                        $this->processTraitUse(
220
                            $unit,
221
                            $extends->getTraitUse($traitName),
222
                            $traitUnit
223
                        );
224
                    } catch (ProjectException $e) {
225
                        $this->addUnresolved($unit, $traitName);
226
                    }
227
                }
228
            }
229
230
        }
231
232
        private function processImplements(AbstractUnitObject $unit, AbstractUnitObject $implements) {
233
            $this->project->registerForSaving($unit);
234
            $this->project->registerForSaving($implements);
235
236
            if (!$implements instanceof InterfaceObject) {
237
                $this->addError(
238
                    $unit,
239
                    sprintf(
240
                        'Trying to implement "%s" which is a %s',
241
                        $implements->getName(),
242
                        $implements->getType()
243
                    )
244
                );
245
                return;
246
            }
247
            $implements->addImplementor($unit);
248
            $unit->importExports($implements, 'interface');
249
250
            if ($implements->hasImplements()) {
251
                foreach($implements->getImplements() as $implementing) {
252
                    try {
253
                        $implementsUnit = $this->getUnitByName($implementing);
254
                        $this->processExtends($unit, $implementsUnit, $implementsUnit);
0 ignored issues
show
Unused Code introduced by Arne Blankerts
The call to TheSeer\phpDox\Collector...olver::processExtends() has too many arguments starting with $implementsUnit. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

254
                        $this->/** @scrutinizer ignore-call */ 
255
                               processExtends($unit, $implementsUnit, $implementsUnit);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
255
                    } catch (ProjectException $e) {
256
                        $this->addUnresolved($unit, $implementing);
257
                    }
258
                }
259
            }
260
        }
261
262
        private function processTraitUse(AbstractUnitObject $unit, TraitUseObject $use, AbstractUnitObject $trait) {
263
            $this->project->registerForSaving($unit);
264
            $this->project->registerForSaving($trait);
265
266
            $trait->addUser($unit);
0 ignored issues
show
Bug introduced by Arne Blankerts
The method addUser() does not exist on TheSeer\phpDox\Collector\AbstractUnitObject. It seems like you code against a sub-type of TheSeer\phpDox\Collector\AbstractUnitObject such as TheSeer\phpDox\Collector\TraitObject. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

266
            $trait->/** @scrutinizer ignore-call */ 
267
                    addUser($unit);
Loading history...
267
            $unit->importTraitExports($trait, $use);
268
269
            if ($trait->hasExtends()) {
270
                foreach($trait->getExtends() as $name) {
271
                    try {
272
                        $extendedUnit = $this->getUnitByName($name);
273
                        $this->processExtends($unit, $extendedUnit, $extendedUnit);
0 ignored issues
show
Unused Code introduced by Arne Blankerts
The call to TheSeer\phpDox\Collector...olver::processExtends() has too many arguments starting with $extendedUnit. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

273
                        $this->/** @scrutinizer ignore-call */ 
274
                               processExtends($unit, $extendedUnit, $extendedUnit);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
274
                    } catch (ProjectException $e) {
275
                        $this->addUnresolved($unit, $name);
276
                    }
277
                }
278
            }
279
280
            if ($trait->usesTraits()) {
281
                foreach($trait->getUsedTraits() as $traitName) {
282
                    try {
283
                        $traitUnit = $this->getUnitByName($traitName);
284
                        $this->processTraitUse(
285
                            $unit,
286
                            $trait->getTraitUse($traitName),
287
                            $traitUnit
288
                        );
289
                    } catch (ProjectException $e) {
290
                        $this->addUnresolved($unit, $traitName);
291
                    }
292
                }
293
            }
294
295
        }
296
297
        private function setupDependencies() {
298
            $this->dependencyStack = array(
299
                $this->project,
300
            );
301
302
            $publicOnlyMode = $this->config->isPublicOnlyMode();
303
            foreach($this->config->getDependencyDirectories() as $depDir) {
304
                $idxName = $depDir . '/index.xml';
305
                if (!file_exists($idxName)) {
306
                    $this->logger->log("'$idxName' not found - skipping dependency");
307
                    continue;
308
                }
309
                $dom = new fDOMDocument();
310
                $dom->load($idxName);
311
                $this->dependencyStack[] = new Dependency($dom, $this->project, $publicOnlyMode);
312
            }
313
        }
314
315
        /**
316
         * @param $name
317
         *
318
         * @return AbstractUnitObject
319
         * @throws ProjectException
320
         */
321
        private function getUnitByName($name) {
322
            foreach($this->dependencyStack as $dependency) {
323
                try {
324
                    return $dependency->getUnitByName($name);
325
                } catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by Arne Blankerts
Consider adding a comment why this CATCH block is empty.
Loading history...
326
            }
327
            throw new ProjectException("No unit with name '$name' found");
328
        }
329
330
    }
331
332
}
333