Failed Conditions
Push — master ( d49c03...6343d0 )
by Bernhard
05:03
created

RootModuleFileManagerImpl   C

Complexity

Total Complexity 75

Size/Duplication

Total Lines 486
Duplicated Lines 34.77 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 98.47%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 169
loc 486
wmc 75
lcom 1
cbo 6
ccs 193
cts 196
cp 0.9847
rs 5.5056

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getConfig() 0 4 1
A getContext() 0 4 1
A getModuleFile() 0 4 1
A getModuleName() 0 4 1
A setModuleName() 18 18 3
A addPluginClass() 21 21 3
A removePluginClass() 0 18 3
B removePluginClasses() 24 24 5
A clearPluginClasses() 0 4 1
A hasPluginClass() 0 4 1
A hasPluginClasses() 14 14 4
A getPluginClasses() 0 4 1
A findPluginClasses() 0 12 3
B setExtraKey() 0 23 4
D setExtraKeys() 0 35 9
A removeExtraKey() 18 18 3
B removeExtraKeys() 24 24 5
A clearExtraKeys() 18 18 3
A hasExtraKey() 0 4 1
A hasExtraKeys() 14 14 4
A getExtraKey() 0 4 1
A getExtraKeys() 0 4 1
A findExtraKeys() 0 12 3
A migrate() 18 18 3
A saveConfigFile() 0 4 1
C validatePluginClass() 0 42 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like RootModuleFileManagerImpl 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 RootModuleFileManagerImpl, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the puli/manager package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Puli\Manager\Module;
13
14
use Exception;
15
use InvalidArgumentException;
16
use Puli\Manager\Api\Context\ProjectContext;
17
use Puli\Manager\Api\Module\RootModuleFile;
18
use Puli\Manager\Api\Module\RootModuleFileManager;
19
use Puli\Manager\Config\AbstractConfigManager;
20
use ReflectionClass;
21
use ReflectionException;
22
use Webmozart\Expression\Expr;
23
use Webmozart\Expression\Expression;
24
25
/**
26
 * Manages changes to the root module file.
27
 *
28
 * Use this class to make persistent changes to the puli.json of a project.
29
 * Whenever you call methods in this class, the changes will be written to disk.
30
 *
31
 * @since  1.0
32
 *
33
 * @author Bernhard Schussek <[email protected]>
34
 */
35
class RootModuleFileManagerImpl extends AbstractConfigManager implements RootModuleFileManager
36
{
37
    /**
38
     * @var ProjectContext
39
     */
40
    private $context;
41
42
    /**
43
     * @var RootModuleFile
44
     */
45
    private $rootModuleFile;
46
47
    /**
48
     * @var ModuleFileStorage
49
     */
50
    private $moduleFileStorage;
51
52
    /**
53
     * Creates a new module file manager.
54
     *
55
     * @param ProjectContext    $context           The project context
56
     * @param ModuleFileStorage $moduleFileStorage The module file storage.
57
     */
58 80
    public function __construct(ProjectContext $context, ModuleFileStorage $moduleFileStorage)
59
    {
60 80
        $this->context = $context;
61 80
        $this->rootModuleFile = $context->getRootModuleFile();
62 80
        $this->moduleFileStorage = $moduleFileStorage;
63 80
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68 17
    public function getConfig()
69
    {
70 17
        return $this->rootModuleFile->getConfig();
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 1
    public function getContext()
77
    {
78 1
        return $this->context;
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84
    public function getModuleFile()
85
    {
86
        return $this->rootModuleFile;
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92 4
    public function getModuleName()
93
    {
94 4
        return $this->rootModuleFile->getModuleName();
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100 3 View Code Duplication
    public function setModuleName($moduleName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
    {
102 3
        if ($moduleName === $this->rootModuleFile->getModuleName()) {
103 1
            return;
104
        }
105
106 2
        $previousName = $this->rootModuleFile->getModuleName();
107
108 2
        $this->rootModuleFile->setModuleName($moduleName);
109
110
        try {
111 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
112 1
        } catch (Exception $e) {
113 1
            $this->rootModuleFile->setModuleName($previousName);
114
115 1
            throw $e;
116
        }
117 1
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122 8 View Code Duplication
    public function addPluginClass($pluginClass)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
    {
124 8
        if ($this->rootModuleFile->hasPluginClass($pluginClass)) {
125
            // Already installed locally
126 1
            return;
127
        }
128
129 7
        $this->validatePluginClass($pluginClass);
130
131 2
        $previousClasses = $this->rootModuleFile->getPluginClasses();
132
133 2
        $this->rootModuleFile->addPluginClass($pluginClass);
134
135
        try {
136 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
137 1
        } catch (Exception $e) {
138 1
            $this->rootModuleFile->setPluginClasses($previousClasses);
139
140 1
            throw $e;
141
        }
142 1
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 3
    public function removePluginClass($pluginClass)
148
    {
149 3
        if (!$this->rootModuleFile->hasPluginClass($pluginClass)) {
150 1
            return;
151
        }
152
153 2
        $previousClasses = $this->rootModuleFile->getPluginClasses();
154
155 2
        $this->rootModuleFile->removePluginClass($pluginClass);
156
157
        try {
158 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
159 1
        } catch (Exception $e) {
160 1
            $this->rootModuleFile->setPluginClasses($previousClasses);
161
162 1
            throw $e;
163
        }
164 1
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169 7 View Code Duplication
    public function removePluginClasses(Expression $expr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
    {
171 7
        $save = false;
172 7
        $previousClasses = $this->rootModuleFile->getPluginClasses();
173
174 7
        foreach ($previousClasses as $pluginClass) {
175 6
            if ($expr->evaluate($pluginClass)) {
176 5
                $this->rootModuleFile->removePluginClass($pluginClass);
177 6
                $save = true;
178
            }
179
        }
180
181 7
        if (!$save) {
182 2
            return;
183
        }
184
185
        try {
186 5
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
187 2
        } catch (Exception $e) {
188 2
            $this->rootModuleFile->setPluginClasses($previousClasses);
189
190 2
            throw $e;
191
        }
192 3
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197 3
    public function clearPluginClasses()
198
    {
199 3
        $this->removePluginClasses(Expr::true());
200 2
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205 1
    public function hasPluginClass($pluginClass)
206
    {
207 1
        return $this->rootModuleFile->hasPluginClass($pluginClass);
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213 1 View Code Duplication
    public function hasPluginClasses(Expression $expr = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
214
    {
215 1
        if (!$expr) {
216 1
            return $this->rootModuleFile->hasPluginClasses();
217
        }
218
219 1
        foreach ($this->rootModuleFile->getPluginClasses() as $pluginClass) {
220 1
            if ($expr->evaluate($pluginClass)) {
221 1
                return true;
222
            }
223
        }
224
225 1
        return false;
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     */
231 14
    public function getPluginClasses()
232
    {
233 14
        return $this->rootModuleFile->getPluginClasses();
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239 1
    public function findPluginClasses(Expression $expr)
240
    {
241 1
        $pluginClasses = array();
242
243 1
        foreach ($this->rootModuleFile->getPluginClasses() as $pluginClass) {
244 1
            if ($expr->evaluate($pluginClass)) {
245 1
                $pluginClasses[] = $pluginClass;
246
            }
247
        }
248
249 1
        return $pluginClasses;
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255 4
    public function setExtraKey($key, $value)
256
    {
257 4
        $previouslySet = $this->rootModuleFile->hasExtraKey($key);
258 4
        $previousValue = $this->rootModuleFile->getExtraKey($key);
259
260 4
        if ($value === $previousValue) {
261 1
            return;
262
        }
263
264 3
        $this->rootModuleFile->setExtraKey($key, $value);
265
266
        try {
267 3
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
268 2
        } catch (Exception $e) {
269 2
            if ($previouslySet) {
270 1
                $this->rootModuleFile->setExtraKey($key, $previousValue);
271
            } else {
272 1
                $this->rootModuleFile->removeExtraKey($key);
273
            }
274
275 2
            throw $e;
276
        }
277 1
    }
278
279
    /**
280
     * {@inheritdoc}
281
     */
282 3
    public function setExtraKeys(array $values)
283
    {
284 3
        $previousValues = array();
285 3
        $previouslyUnset = array();
286
287 3
        foreach ($values as $key => $value) {
288 3
            if ($this->rootModuleFile->hasExtraKey($key)) {
289 2
                if ($value !== $previous = $this->rootModuleFile->getExtraKey($key)) {
290 2
                    $previousValues[$key] = $previous;
291
                }
292
            } else {
293 3
                $previouslyUnset[$key] = true;
294
            }
295
        }
296
297 3
        if (!$previousValues && !$previouslyUnset) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $previousValues of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $previouslyUnset of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
298 1
            return;
299
        }
300
301 2
        $this->rootModuleFile->setExtraKeys($values);
302
303
        try {
304 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
305 1
        } catch (Exception $e) {
306 1
            foreach ($values as $key => $value) {
307 1
                if (isset($previouslyUnset[$key])) {
308 1
                    $this->rootModuleFile->removeExtraKey($key);
309
                } else {
310 1
                    $this->rootModuleFile->setExtraKey($key, $previousValues[$key]);
311
                }
312
            }
313
314 1
            throw $e;
315
        }
316 1
    }
317
318
    /**
319
     * {@inheritdoc}
320
     */
321 3 View Code Duplication
    public function removeExtraKey($key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
    {
323 3
        if (!$this->rootModuleFile->hasExtraKey($key)) {
324 1
            return;
325
        }
326
327 2
        $previousValue = $this->rootModuleFile->getExtraKey($key);
328
329 2
        $this->rootModuleFile->removeExtraKey($key);
330
331
        try {
332 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
333 1
        } catch (Exception $e) {
334 1
            $this->rootModuleFile->setExtraKey($key, $previousValue);
335
336 1
            throw $e;
337
        }
338 1
    }
339
340
    /**
341
     * {@inheritdoc}
342
     */
343 3 View Code Duplication
    public function removeExtraKeys(Expression $expr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
344
    {
345 3
        $previousValues = $this->rootModuleFile->getExtraKeys();
346 3
        $save = false;
347
348 3
        foreach ($this->rootModuleFile->getExtraKeys() as $key => $value) {
349 2
            if ($expr->evaluate($key)) {
350 2
                $this->rootModuleFile->removeExtraKey($key);
351 2
                $save = true;
352
            }
353
        }
354
355 3
        if (!$save) {
356 1
            return;
357
        }
358
359
        try {
360 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
361 1
        } catch (Exception $e) {
362 1
            $this->rootModuleFile->setExtraKeys($previousValues);
363
364 1
            throw $e;
365
        }
366 1
    }
367
368
    /**
369
     * {@inheritdoc}
370
     */
371 3 View Code Duplication
    public function clearExtraKeys()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
    {
373 3
        $previousValues = $this->rootModuleFile->getExtraKeys();
374
375 3
        if (!$previousValues) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $previousValues of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
376 1
            return;
377
        }
378
379 2
        $this->rootModuleFile->clearExtraKeys();
380
381
        try {
382 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
383 1
        } catch (Exception $e) {
384 1
            $this->rootModuleFile->setExtraKeys($previousValues);
385
386 1
            throw $e;
387
        }
388 1
    }
389
390
    /**
391
     * {@inheritdoc}
392
     */
393 1
    public function hasExtraKey($key)
394
    {
395 1
        return $this->rootModuleFile->hasExtraKey($key);
396
    }
397
398
    /**
399
     * {@inheritdoc}
400
     */
401 1 View Code Duplication
    public function hasExtraKeys(Expression $expr = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
402
    {
403 1
        if (!$expr) {
404 1
            return $this->rootModuleFile->hasExtraKeys();
405
        }
406
407 1
        foreach ($this->rootModuleFile->getExtraKeys() as $key => $value) {
408 1
            if ($expr->evaluate($key)) {
409 1
                return true;
410
            }
411
        }
412
413 1
        return false;
414
    }
415
416
    /**
417
     * {@inheritdoc}
418
     */
419 15
    public function getExtraKey($key, $default = null)
420
    {
421 15
        return $this->rootModuleFile->getExtraKey($key, $default);
422
    }
423
424
    /**
425
     * {@inheritdoc}
426
     */
427 1
    public function getExtraKeys()
428
    {
429 1
        return $this->rootModuleFile->getExtraKeys();
430
    }
431
432
    /**
433
     * {@inheritdoc}
434
     */
435 1
    public function findExtraKeys(Expression $expr)
436
    {
437 1
        $values = array();
438
439 1
        foreach ($this->rootModuleFile->getExtraKeys() as $key => $value) {
440 1
            if ($expr->evaluate($key)) {
441 1
                $values[$key] = $value;
442
            }
443
        }
444
445 1
        return $values;
446
    }
447
448
    /**
449
     * {@inheritdoc}
450
     */
451 2 View Code Duplication
    public function migrate($targetVersion)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
452
    {
453 2
        $previousVersion = $this->rootModuleFile->getVersion();
454
455 2
        if ($previousVersion === $targetVersion) {
456
            return;
457
        }
458
459 2
        $this->rootModuleFile->setVersion($targetVersion);
460
461
        try {
462 2
            $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
463 1
        } catch (Exception $e) {
464 1
            $this->rootModuleFile->setVersion($previousVersion);
465
466 1
            throw $e;
467
        }
468 1
    }
469
470
    /**
471
     * {@inheritdoc}
472
     */
473 4
    protected function saveConfigFile()
474
    {
475 4
        $this->moduleFileStorage->saveRootModuleFile($this->rootModuleFile);
476 4
    }
477
478 7
    private function validatePluginClass($pluginClass)
479
    {
480
        try {
481 7
            $reflClass = new ReflectionClass($pluginClass);
482 1
        } catch (ReflectionException $e) {
483 1
            throw new InvalidArgumentException(sprintf(
484 1
                'The plugin class %s does not exist.',
485
                $pluginClass
486 1
            ), 0, $e);
487
        }
488
489 6
        if ($reflClass->isInterface()) {
490 1
            throw new InvalidArgumentException(sprintf(
491 1
                'The plugin class %s should be a class, but is an interface.',
492
                $pluginClass
493
            ));
494
        }
495
496 5
        if (version_compare(PHP_VERSION, '5.4.0', '>=') && $reflClass->isTrait()) {
497 1
            throw new InvalidArgumentException(sprintf(
498 1
                'The plugin class %s should be a class, but is a trait.',
499
                $pluginClass
500
            ));
501
        }
502
503 4
        if (!$reflClass->implementsInterface('\Puli\Manager\Api\PuliPlugin')) {
504 1
            throw new InvalidArgumentException(sprintf(
505 1
                'The plugin class %s must implement PuliPlugin.',
506
                $pluginClass
507
            ));
508
        }
509
510 3
        $constructor = $reflClass->getConstructor();
511
512 3
        if (null !== $constructor && $constructor->getNumberOfRequiredParameters() > 0) {
513 1
            throw new InvalidArgumentException(sprintf(
514
                'The constructor of the plugin class %s must not have required '.
515 1
                'parameters.',
516
                $pluginClass
517
            ));
518
        }
519 2
    }
520
}
521