Clazz   C
last analyzed

Complexity

Total Complexity 54

Size/Duplication

Total Lines 669
Duplicated Lines 1.05 %

Coupling/Cohesion

Components 6
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 54
lcom 6
cbo 4
dl 7
loc 669
c 0
b 0
f 0
ccs 150
cts 150
cp 1
rs 5.1719

41 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setClassName() 7 16 2
A getClassName() 0 6 2
A getNamespaceName() 0 4 1
A getShortClassName() 0 4 1
A getDirectory() 0 4 1
A setDirectory() 0 8 1
A getFileName() 0 8 2
A setFileName() 0 8 1
A resetFileName() 0 6 1
A getFilePath() 0 4 1
A setFilePath() 0 9 1
A getParentClass() 0 4 1
A setParentClass() 0 8 1
A hasParentClass() 0 4 1
A removeParentClass() 0 6 1
A getImplementedInterfaces() 0 4 1
A hasImplementedInterfaces() 0 4 1
A setImplementedInterfaces() 0 8 1
A addImplementedInterfaces() 0 8 2
A addImplementedInterface() 0 8 1
A removeImplementedInterface() 0 6 1
A clearImplementedInterfaces() 0 6 1
A getImports() 0 4 1
A hasImports() 0 4 1
A setImports() 0 8 1
A addImports() 0 8 2
B addImport() 0 22 4
A removeImport() 0 12 3
A clearImports() 0 6 1
A getMethods() 0 4 1
A getMethod() 0 11 2
A hasMethods() 0 4 1
A hasMethod() 0 4 1
A setMethods() 0 8 1
A addMethods() 0 8 2
A addMethod() 0 15 2
A removeMethod() 0 6 1
A clearMethods() 0 6 1
A getDescription() 0 4 1
A setDescription() 0 8 1

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 Clazz 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 Clazz, 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\Api\Php;
13
14
use OutOfBoundsException;
15
use RuntimeException;
16
use Webmozart\Assert\Assert;
17
use Webmozart\PathUtil\Path;
18
19
/**
20
 * A model of a class.
21
 *
22
 * @since  1.0
23
 *
24
 * @author Bernhard Schussek <[email protected]>
25
 */
26
class Clazz
27
{
28
    /**
29
     * @var string
30
     */
31
    private $namespaceName;
32
33
    /**
34
     * @var string
35
     */
36
    private $shortClassName;
37
38
    /**
39
     * @var string
40
     */
41
    private $directory;
42
43
    /**
44
     * @var string
45
     */
46
    private $fileName;
47
48
    /**
49
     * @var string
50
     */
51
    private $parentClass;
52
53
    /**
54
     * @var bool[]
55
     */
56
    private $implementedInterfaces = array();
57
58
    /**
59
     * @var Import[]
60
     */
61
    private $imports = array();
62
63
    /**
64
     * @var string[]
65
     */
66
    private $importedSymbols = array();
67
68
    /**
69
     * @var Method[]
70
     */
71
    private $methods = array();
72
73
    /**
74
     * @var string
75
     */
76
    private $description;
77
78
    /**
79
     * Creates a new factory class.
80
     *
81
     * @param string $className The fully-qualified class name.
82
     */
83 208
    public function __construct($className)
84
    {
85 208
        $this->setClassName($className);
86 208
    }
87
88
    /**
89
     * Sets the fully-qualified name of the factory class.
90
     *
91
     * @param string $className The fully-qualified class name.
92
     *
93
     * @return static The current instance.
94
     */
95 208
    public function setClassName($className)
96
    {
97 208
        Assert::stringNotEmpty($className, 'The class name must be a non-empty string. Got: %s');
98
99 208
        $pos = strrpos($className, '\\');
100
101 208 View Code Duplication
        if (false === $pos) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
102 67
            $this->namespaceName = '';
103 67
            $this->shortClassName = $className;
104
        } else {
105 144
            $this->namespaceName = substr($className, 0, $pos);
106 144
            $this->shortClassName = substr($className, $pos + 1);
107
        }
108
109 208
        return $this;
110
    }
111
112
    /**
113
     * Returns the fully-qualified name of the factory class.
114
     *
115
     * @return string The fully-qualified class name.
116
     */
117 9
    public function getClassName()
118
    {
119 9
        return $this->namespaceName
120 5
            ? $this->namespaceName.'\\'.$this->shortClassName
121 9
            : $this->shortClassName;
122
    }
123
124
    /**
125
     * Returns the namespace of the factory class.
126
     *
127
     * @return string The namespace or an empty string if the class is in the
128
     *                global namespace.
129
     */
130 53
    public function getNamespaceName()
131
    {
132 53
        return $this->namespaceName;
133
    }
134
135
    /**
136
     * Returns the short class name.
137
     *
138
     * @return string The short name of the factory class.
139
     */
140 53
    public function getShortClassName()
141
    {
142 53
        return $this->shortClassName;
143
    }
144
145
    /**
146
     * Returns the path to the directory holding the factory class file.
147
     *
148
     * @return string The absolute directory path.
149
     */
150 91
    public function getDirectory()
151
    {
152 91
        return $this->directory;
153
    }
154
155
    /**
156
     * Sets the path to the directory holding the factory class file.
157
     *
158
     * @param string $directory The absolute directory path.
159
     *
160
     * @return static The current instance.
161
     */
162 118
    public function setDirectory($directory)
163
    {
164 118
        Assert::stringNotEmpty($directory, 'The factory directory must be a non-empty string. Got: %s');
165
166 115
        $this->directory = Path::canonicalize($directory);
167
168 115
        return $this;
169
    }
170
171
    /**
172
     * Returns the name of the factory class file.
173
     *
174
     * If no file name was set, the file is named after the short class name
175
     * suffixed with ".php".
176
     *
177
     * @return string The file name.
178
     */
179 60
    public function getFileName()
180
    {
181 60
        if (!$this->fileName) {
182 30
            return $this->shortClassName.'.php';
183
        }
184
185 30
        return $this->fileName;
186
    }
187
188
    /**
189
     * Sets the name of the factory class file.
190
     *
191
     * @param string $fileName The file name.
192
     *
193
     * @return static The current instance.
194
     */
195 90
    public function setFileName($fileName)
196
    {
197 90
        Assert::stringNotEmpty($fileName, 'The factory file name must be a non-empty string. Got: %s');
198
199 87
        $this->fileName = $fileName;
200
201 87
        return $this;
202
    }
203
204
    /**
205
     * Resets the name of the factory class file to the default value.
206
     *
207
     * By default, the file is named after the short class name suffixed with
208
     * ".php".
209
     *
210
     * @return static The current instance.
211
     */
212 1
    public function resetFileName()
213
    {
214 1
        $this->fileName = null;
215
216 1
        return $this;
217
    }
218
219
    /**
220
     * Returns the absolute file path of the factory class file.
221
     *
222
     * @return string The absolute file path.
223
     */
224 51
    public function getFilePath()
225
    {
226 51
        return $this->directory.'/'.$this->getFileName();
227
    }
228
229
    /**
230
     * Sets the absolute file path of the factory class file.
231
     *
232
     * @param string $filePath The absolute file path.
233
     *
234
     * @return static The current instance.
235
     */
236 86
    public function setFilePath($filePath)
237
    {
238 86
        Assert::stringNotEmpty($filePath, 'The factory file path must be a non-empty string. Got: %s');
239
240 83
        $this->setDirectory(Path::getDirectory($filePath));
241 83
        $this->setFileName(Path::getFilename($filePath));
242
243 83
        return $this;
244
    }
245
246
    /**
247
     * Returns the parent class name.
248
     *
249
     * @return string|null The parent class name or `null` if the class has no
250
     *                     parent class.
251
     */
252 4
    public function getParentClass()
253
    {
254 4
        return $this->parentClass;
255
    }
256
257
    /**
258
     * Sets the parent class name.
259
     *
260
     * If you don't pass a fully-qualified name, make sure to import the name
261
     * with {@link addImport()}.
262
     *
263
     * @param string $parentClass The parent class name.
264
     *
265
     * @return static The current instance.
266
     */
267 8
    public function setParentClass($parentClass)
268
    {
269 8
        Assert::stringNotEmpty($parentClass, 'The parent class name must be a non-empty string. Got: %s');
270
271 5
        $this->parentClass = $parentClass;
272
273 5
        return $this;
274
    }
275
276
    /**
277
     * Returns whether the class extends another class.
278
     *
279
     * @return bool Returns `true` if the class has a parent class and `false`
280
     *              otherwise.
281
     */
282 51
    public function hasParentClass()
283
    {
284 51
        return null !== $this->parentClass;
285
    }
286
287
    /**
288
     * Removes the parent class.
289
     *
290
     * @return static The current instance.
291
     */
292 2
    public function removeParentClass()
293
    {
294 2
        $this->parentClass = null;
295
296 2
        return $this;
297
    }
298
299
    /**
300
     * Returns the interfaces that the class implements.
301
     *
302
     * @return string[] The names of the implemented interfaces.
0 ignored issues
show
Documentation introduced by
Should the return type not be integer[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
303
     */
304 8
    public function getImplementedInterfaces()
305
    {
306 8
        return array_keys($this->implementedInterfaces);
307
    }
308
309
    /**
310
     * Returns whether the class implements any interfaces.
311
     *
312
     * @return bool Returns `true` if the class implements any interfaces and
313
     *              `false` otherwise.
314
     */
315 51
    public function hasImplementedInterfaces()
316
    {
317 51
        return count($this->implementedInterfaces) > 0;
318
    }
319
320
    /**
321
     * Sets the interfaces that the class implements.
322
     *
323
     * Previously added interfaces are overwritten.
324
     *
325
     * If you don't pass fully-qualified names, make sure to import the names
326
     * with {@link addImport()}.
327
     *
328
     * @param string[] $interfaceNames The names of the implemented interfaces.
329
     *
330
     * @return static The current instance.
331
     */
332 1
    public function setImplementedInterfaces(array $interfaceNames)
333
    {
334 1
        $this->implementedInterfaces = array();
335
336 1
        $this->addImplementedInterfaces($interfaceNames);
337
338 1
        return $this;
339
    }
340
341
    /**
342
     * Adds implemented interfaces to the class definition.
343
     *
344
     * Previously added interfaces are kept.
345
     *
346
     * If you don't pass fully-qualified names, make sure to import the names
347
     * with {@link addImport()}.
348
     *
349
     * @param string[] $interfaceNames The names of the added interfaces.
350
     *
351
     * @return static The current instance.
352
     */
353 2
    public function addImplementedInterfaces(array $interfaceNames)
354
    {
355 2
        foreach ($interfaceNames as $interfaceName) {
356 2
            $this->addImplementedInterface($interfaceName);
357
        }
358
359 2
        return $this;
360
    }
361
362
    /**
363
     * Adds an implemented interface to the class definition.
364
     *
365
     * If you don't pass a fully-qualified name, make sure to import the name
366
     * with {@link addImport()}.
367
     *
368
     * @param string $interfaceName The name of the added interfaces.
369
     *
370
     * @return static The current instance.
371
     */
372 12
    public function addImplementedInterface($interfaceName)
373
    {
374 12
        Assert::stringNotEmpty($interfaceName, 'The interface name must be a non-empty string. Got: %s');
375
376 9
        $this->implementedInterfaces[$interfaceName] = true;
377
378 9
        return $this;
379
    }
380
381
    /**
382
     * Removes an implemented interface from the class definition.
383
     *
384
     * @param string $interfaceName The name of the removed interfaces.
385
     *
386
     * @return static The current instance.
387
     */
388 3
    public function removeImplementedInterface($interfaceName)
389
    {
390 3
        unset($this->implementedInterfaces[$interfaceName]);
391
392 3
        return $this;
393
    }
394
395
    /**
396
     * Removes all implemented interfaces from the class definition.
397
     *
398
     * @return static The current instance.
399
     */
400 1
    public function clearImplementedInterfaces()
401
    {
402 1
        $this->implementedInterfaces = array();
403
404 1
        return $this;
405
    }
406
407
    /**
408
     * Returns the import statements of the class file.
409
     *
410
     * @return Import[] The imported fully-qualified class names.
411
     */
412 40
    public function getImports()
413
    {
414 40
        return $this->imports;
415
    }
416
417
    /**
418
     * Returns whether the class file imports any class name.
419
     *
420
     * @return bool Returns `true` if the class file imports class names and
421
     *              `false` otherwise.
422
     */
423 51
    public function hasImports()
424
    {
425 51
        return count($this->imports) > 0;
426
    }
427
428
    /**
429
     * Sets the import statements of the class file.
430
     *
431
     * Previously added imports are overwritten.
432
     *
433
     * @param Import[] $imports The imported fully-qualified class names.
434
     *
435
     * @return static The current instance.
436
     */
437 1
    public function setImports(array $imports)
438
    {
439 1
        $this->imports = array();
440
441 1
        $this->addImports($imports);
442
443 1
        return $this;
444
    }
445
446
    /**
447
     * Adds import statements to the class file.
448
     *
449
     * Previously added imports are kept.
450
     *
451
     * @param Import[] $imports The imported fully-qualified class names.
452
     *
453
     * @return static The current instance.
454
     */
455 2
    public function addImports(array $imports)
456
    {
457 2
        foreach ($imports as $import) {
458 2
            $this->addImport($import);
459
        }
460
461 2
        return $this;
462
    }
463
464
    /**
465
     * Adds an import statement to the class file.
466
     *
467
     * @param Import $import The imported fully-qualified class name.
468
     *
469
     * @return static The current instance.
470
     */
471 72
    public function addImport(Import $import)
472
    {
473 72
        if (isset($this->imports[$import->getClassName()])) {
474 29
            return $this;
475
        }
476
477 72
        $symbol = $import->getAlias() ?: $import->getShortClassName();
478
479 72
        if (isset($this->importedSymbols[$symbol])) {
480 3
            throw new RuntimeException(sprintf(
481 3
                'The symbol "%s" was imported already.',
482 3
                $import->getShortClassName()
483
            ));
484
        }
485
486 72
        $this->imports[$import->getClassName()] = $import;
487 72
        $this->importedSymbols[$symbol] = true;
488
489 72
        ksort($this->imports);
490
491 72
        return $this;
492
    }
493
494
    /**
495
     * Removes an import statement from the class file.
496
     *
497
     * If the import statement is not found, this method does nothing.
498
     *
499
     * @param string $className The removed imported class name.
500
     *
501
     * @return static The current instance.
502
     */
503 2
    public function removeImport($className)
504
    {
505 2
        if (isset($this->imports[$className])) {
506 2
            $import = $this->imports[$className];
507 2
            $symbol = $import->getAlias() ?: $import->getShortClassName();
508
509 2
            unset($this->imports[$className]);
510 2
            unset($this->importedSymbols[$symbol]);
511
        }
512
513 2
        return $this;
514
    }
515
516
    /**
517
     * Removes all import statements from the class file.
518
     *
519
     * @return static The current instance.
520
     */
521 1
    public function clearImports()
522
    {
523 1
        $this->imports = array();
524
525 1
        return $this;
526
    }
527
528
    /**
529
     * Returns the methods of the factory class.
530
     *
531
     * @return Method[] The methods indexed by their names.
532
     */
533 56
    public function getMethods()
534
    {
535 56
        return $this->methods;
536
    }
537
538
    /**
539
     * Returns the method with the given name.
540
     *
541
     * @param string $name The name of the method.
542
     *
543
     * @return Method The method.
544
     *
545
     * @throws OutOfBoundsException If the method with the given name does not
546
     *                              exist.
547
     */
548 2
    public function getMethod($name)
549
    {
550 2
        if (!isset($this->methods[$name])) {
551 1
            throw new OutOfBoundsException(sprintf(
552 1
                'The method "%s" does not exist.',
553
                $name
554
            ));
555
        }
556
557 1
        return $this->methods[$name];
558
    }
559
560
    /**
561
     * Returns whether the class contains any methods.
562
     *
563
     * @return bool Returns `true` if the class contains methods and `false`
564
     *              otherwise.
565
     */
566 1
    public function hasMethods()
567
    {
568 1
        return count($this->methods) > 0;
569
    }
570
571
    /**
572
     * Returns whether the class contains a method with the given name.
573
     *
574
     * @param string $name The name of the method.
575
     *
576
     * @return bool Returns `true` if the method with the given name exists and
577
     *              `false` otherwise.
578
     */
579 2
    public function hasMethod($name)
580
    {
581 2
        return isset($this->methods[$name]);
582
    }
583
584
    /**
585
     * Sets the methods of the class.
586
     *
587
     * Previously added methods are overwritten.
588
     *
589
     * @param Method[] $methods The methods of the class.
590
     *
591
     * @return static The current instance.
592
     */
593 1
    public function setMethods(array $methods)
594
    {
595 1
        $this->methods = array();
596
597 1
        $this->addMethods($methods);
598
599 1
        return $this;
600
    }
601
602
    /**
603
     * Adds methods to the class.
604
     *
605
     * Previously added methods are kept.
606
     *
607
     * @param Method[] $methods The methods to add to the class.
608
     *
609
     * @return static The current instance.
610
     */
611 2
    public function addMethods(array $methods)
612
    {
613 2
        foreach ($methods as $method) {
614 2
            $this->addMethod($method);
615
        }
616
617 2
        return $this;
618
    }
619
620
    /**
621
     * Adds a method to the class.
622
     *
623
     * @param Method $method The method to add to the class.
624
     *
625
     * @return static The current instance.
626
     */
627 110
    public function addMethod(Method $method)
628
    {
629 110
        if (isset($this->methods[$method->getName()])) {
630 1
            throw new RuntimeException(sprintf(
631 1
                'The method "%s" exists already.',
632 1
                $method->getName()
633
            ));
634
        }
635
636 110
        $this->methods[$method->getName()] = $method;
637
638 110
        $method->setClass($this);
639
640 110
        return $this;
641
    }
642
643
    /**
644
     * Removes a method from the class.
645
     *
646
     * @param string $name The name of the removed method.
647
     *
648
     * @return static The current instance.
649
     */
650 4
    public function removeMethod($name)
651
    {
652 4
        unset($this->methods[$name]);
653
654 4
        return $this;
655
    }
656
657
    /**
658
     * Removes all methods from the class.
659
     *
660
     * @return static The current instance.
661
     */
662 1
    public function clearMethods()
663
    {
664 1
        $this->methods = array();
665
666 1
        return $this;
667
    }
668
669
    /**
670
     * Returns the description of the class.
671
     *
672
     * @return string The class description.
673
     */
674 51
    public function getDescription()
675
    {
676 51
        return $this->description;
677
    }
678
679
    /**
680
     * Sets the description of the class.
681
     *
682
     * @param string $description The class description.
683
     *
684
     * @return static The current instance.
685
     */
686 33
    public function setDescription($description)
687
    {
688 33
        Assert::stringNotEmpty($description, 'The class description must be a non-empty string. Got: %s');
689
690 30
        $this->description = $description;
691
692 30
        return $this;
693
    }
694
}
695