ReflectionObject   F
last analyzed

Complexity

Total Complexity 65

Size/Duplication

Total Lines 509
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 98.7%

Importance

Changes 0
Metric Value
wmc 65
lcom 1
cbo 7
dl 0
loc 509
ccs 152
cts 154
cp 0.987
rs 3.2
c 0
b 0
f 0

50 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A export() 0 12 2
A __toString() 0 4 1
A getName() 0 4 1
A isInternal() 0 4 1
A isUserDefined() 0 4 1
A isInstantiable() 0 4 1
A isCloneable() 0 4 1
A getFileName() 0 4 1
A getStartLine() 0 4 1
A getEndLine() 0 4 1
A getDocComment() 0 4 2
A getConstructor() 0 4 1
A hasMethod() 0 4 1
A getMethod() 0 4 1
A getMethodRealName() 0 12 1
A getMethods() 0 11 2
A hasProperty() 0 4 1
A getProperty() 0 10 2
A getProperties() 0 11 2
A hasConstant() 0 4 1
A getConstants() 0 4 1
A getConstant() 0 4 1
A getInterfaces() 0 11 2
A getInterfaceNames() 0 4 1
A isInterface() 0 4 1
A getTraits() 0 11 2
A getTraitNames() 0 4 1
A getTraitAliases() 0 4 1
A isTrait() 0 4 1
A isAbstract() 0 4 1
A isFinal() 0 4 1
A getModifiers() 0 4 1
A isInstance() 0 4 1
A newInstance() 0 4 1
A newInstanceWithoutConstructor() 0 4 1
A newInstanceArgs() 0 4 1
A getParentClass() 0 10 2
A isSubclassOf() 0 12 1
A getStaticProperties() 0 4 1
A getStaticPropertyValue() 0 24 5
A setStaticPropertyValue() 0 20 4
A getDefaultProperties() 0 4 1
A isIterateable() 0 4 1
A implementsInterface() 0 12 1
A getExtension() 0 4 1
A getExtensionName() 0 4 1
A inNamespace() 0 4 1
A getNamespaceName() 0 4 1
A getShortName() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like ReflectionObject 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 ReflectionObject, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\BetterReflection\Reflection\Adapter;
6
7
use ReflectionException as CoreReflectionException;
8
use ReflectionObject as CoreReflectionObject;
9
use Roave\BetterReflection\Reflection\ReflectionMethod as BetterReflectionMethod;
10
use Roave\BetterReflection\Reflection\ReflectionObject as BetterReflectionObject;
11
use function array_combine;
12
use function array_map;
13
use function func_num_args;
14
use function sprintf;
15
use function strtolower;
16
17
class ReflectionObject extends CoreReflectionObject
18
{
19
    /** @var BetterReflectionObject */
20
    private $betterReflectionObject;
21
22
    public function __construct(BetterReflectionObject $betterReflectionObject)
23 61
    {
24
        $this->betterReflectionObject = $betterReflectionObject;
25 61
    }
26 61
27
    /**
28
     * {@inheritDoc}
29
     *
30
     * @throws CoreReflectionException
31 1
     */
32
    public static function export($argument, $return = null)
33 1
    {
34
        $output = BetterReflectionObject::createFromInstance($argument)->__toString();
35
36
        if ($return) {
37
            return $output;
38
        }
39 1
40
        echo $output;
41 1
42
        return null;
43
    }
44
45
    /**
46
     * {@inheritDoc}
47 1
     */
48
    public function __toString()
49 1
    {
50
        return $this->betterReflectionObject->__toString();
51
    }
52
53
    /**
54
     * {@inheritDoc}
55 1
     */
56
    public function getName()
57 1
    {
58
        return $this->betterReflectionObject->getName();
59
    }
60
61
    /**
62
     * {@inheritDoc}
63 1
     */
64
    public function isInternal()
65 1
    {
66
        return $this->betterReflectionObject->isInternal();
67
    }
68
69
    /**
70
     * {@inheritDoc}
71 1
     */
72
    public function isUserDefined()
73 1
    {
74
        return $this->betterReflectionObject->isUserDefined();
75
    }
76
77
    /**
78
     * {@inheritDoc}
79 1
     */
80
    public function isInstantiable()
81 1
    {
82
        return $this->betterReflectionObject->isInstantiable();
83
    }
84
85
    /**
86
     * {@inheritDoc}
87 2
     */
88
    public function isCloneable()
89 2
    {
90
        return $this->betterReflectionObject->isCloneable();
91
    }
92
93
    /**
94
     * {@inheritDoc}
95 1
     */
96
    public function getFileName()
97 1
    {
98
        return $this->betterReflectionObject->getFileName() ?? false;
0 ignored issues
show
Comprehensibility Best Practice introduced by Jaroslav Hanslík
The expression $this->betterReflectionO...getFileName() ?? false; of type string|false adds false to the return on line 98 which is incompatible with the return type of the parent method ReflectionObject::getFileName of type string. It seems like you forgot to handle an error condition.
Loading history...
99
    }
100
101
    /**
102
     * {@inheritDoc}
103 1
     */
104
    public function getStartLine()
105 1
    {
106
        return $this->betterReflectionObject->getStartLine();
107
    }
108
109
    /**
110
     * {@inheritDoc}
111 2
     */
112
    public function getEndLine()
113 2
    {
114
        return $this->betterReflectionObject->getEndLine();
115
    }
116
117
    /**
118
     * {@inheritDoc}
119 1
     */
120
    public function getDocComment()
121 1
    {
122
        return $this->betterReflectionObject->getDocComment() ?: false;
0 ignored issues
show
Comprehensibility Best Practice introduced by Jaroslav Hanslík
The expression $this->betterReflectionO...tDocComment() ?: false; of type string|false adds false to the return on line 122 which is incompatible with the return type of the parent method ReflectionObject::getDocComment of type string. It seems like you forgot to handle an error condition.
Loading history...
123
    }
124
125
    /**
126
     * {@inheritDoc}
127 2
     */
128
    public function getConstructor()
129 2
    {
130
        return new ReflectionMethod($this->betterReflectionObject->getConstructor());
131
    }
132
133
    /**
134
     * {@inheritDoc}
135 2
     */
136
    public function hasMethod($name)
137 2
    {
138
        return $this->betterReflectionObject->hasMethod($this->getMethodRealName($name));
139
    }
140 4
141
    /**
142
     * {@inheritDoc}
143 2
     */
144 4
    public function getMethod($name)
145
    {
146
        return new ReflectionMethod($this->betterReflectionObject->getMethod($this->getMethodRealName($name)));
147 2
    }
148 4
149
    private function getMethodRealName(string $name) : string
150 4
    {
151
        $realMethodNames = array_map(static function (BetterReflectionMethod $method) : string {
152
            return $method->getName();
153
        }, $this->betterReflectionObject->getMethods());
154
155
        $methodNames = array_combine(array_map(static function (string $methodName) : string {
156 1
            return strtolower($methodName);
157
        }, $realMethodNames), $realMethodNames);
158 1
159
        return $methodNames[strtolower($name)] ?? $name;
160 1
    }
161 1
162 1
    /**
163
     * {@inheritDoc}
164
     */
165 1
    public function getMethods($filter = null)
166
    {
167
        $methods = $this->betterReflectionObject->getMethods();
168
169
        $wrappedMethods = [];
170
        foreach ($methods as $key => $method) {
171 1
            $wrappedMethods[$key] = new ReflectionMethod($method);
172
        }
173 1
174
        return $wrappedMethods;
175
    }
176
177
    /**
178
     * {@inheritDoc}
179 2
     */
180
    public function hasProperty($name)
181 2
    {
182
        return $this->betterReflectionObject->hasProperty($name);
183 2
    }
184 1
185
    /**
186
     * {@inheritDoc}
187 1
     */
188
    public function getProperty($name)
189
    {
190
        $property = $this->betterReflectionObject->getProperty($name);
191
192
        if ($property === null) {
193 1
            throw new CoreReflectionException(sprintf('Property "%s" does not exist', $name));
194
        }
195 1
196
        return new ReflectionProperty($property);
197 1
    }
198 1
199 1
    /**
200
     * {@inheritDoc}
201
     */
202 1
    public function getProperties($filter = null)
203
    {
204
        $properties = $this->betterReflectionObject->getProperties();
205
206
        $wrappedProperties = [];
207
        foreach ($properties as $key => $property) {
208 1
            $wrappedProperties[$key] = new ReflectionProperty($property);
209
        }
210 1
211
        return $wrappedProperties;
212
    }
213
214
    /**
215
     * {@inheritDoc}
216 1
     */
217
    public function hasConstant($name)
218 1
    {
219
        return $this->betterReflectionObject->hasConstant($name);
220
    }
221
222
    /**
223
     * {@inheritDoc}
224 1
     */
225
    public function getConstants()
226 1
    {
227
        return $this->betterReflectionObject->getConstants();
228
    }
229
230
    /**
231
     * {@inheritDoc}
232 1
     */
233
    public function getConstant($name)
234 1
    {
235
        return $this->betterReflectionObject->getConstant($name);
236 1
    }
237 1
238 1
    /**
239
     * {@inheritDoc}
240
     */
241 1
    public function getInterfaces()
242
    {
243
        $interfaces = $this->betterReflectionObject->getInterfaces();
244
245
        $wrappedInterfaces = [];
246
        foreach ($interfaces as $key => $interface) {
247 1
            $wrappedInterfaces[$key] = new ReflectionClass($interface);
248
        }
249 1
250
        return $wrappedInterfaces;
251
    }
252
253
    /**
254
     * {@inheritDoc}
255 1
     */
256
    public function getInterfaceNames()
257 1
    {
258
        return $this->betterReflectionObject->getInterfaceNames();
259
    }
260
261
    /**
262
     * {@inheritDoc}
263 1
     */
264
    public function isInterface()
265 1
    {
266
        return $this->betterReflectionObject->isInterface();
267 1
    }
268 1
269 1
    /**
270
     * {@inheritDoc}
271
     */
272 1
    public function getTraits()
273
    {
274
        $traits = $this->betterReflectionObject->getTraits();
275
276
        $wrappedTraits = [];
277
        foreach ($traits as $key => $trait) {
278 1
            $wrappedTraits[$key] = new ReflectionClass($trait);
279
        }
280 1
281
        return $wrappedTraits;
282
    }
283
284
    /**
285
     * {@inheritDoc}
286 1
     */
287
    public function getTraitNames()
288 1
    {
289
        return $this->betterReflectionObject->getTraitNames();
290
    }
291
292
    /**
293
     * {@inheritDoc}
294 1
     */
295
    public function getTraitAliases()
296 1
    {
297
        return $this->betterReflectionObject->getTraitAliases();
298
    }
299
300
    /**
301
     * {@inheritDoc}
302 1
     */
303
    public function isTrait()
304 1
    {
305
        return $this->betterReflectionObject->isTrait();
306
    }
307
308
    /**
309
     * {@inheritDoc}
310 1
     */
311
    public function isAbstract()
312 1
    {
313
        return $this->betterReflectionObject->isAbstract();
314
    }
315
316
    /**
317
     * {@inheritDoc}
318 1
     */
319
    public function isFinal()
320 1
    {
321
        return $this->betterReflectionObject->isFinal();
322
    }
323
324
    /**
325
     * {@inheritDoc}
326 1
     */
327
    public function getModifiers()
328 1
    {
329
        return $this->betterReflectionObject->getModifiers();
330
    }
331
332
    /**
333
     * {@inheritDoc}
334 1
     */
335
    public function isInstance($object)
336 1
    {
337
        return $this->betterReflectionObject->isInstance($object);
338
    }
339
340
    /**
341
     * {@inheritDoc}
342 1
     */
343
    public function newInstance($arg = null, ...$args)
0 ignored issues
show
Unused Code introduced by Jaroslav Hanslík
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
344 1
    {
345
        throw new Exception\NotImplemented('Not implemented');
346
    }
347
348
    /**
349
     * {@inheritDoc}
350 1
     */
351
    public function newInstanceWithoutConstructor()
352 1
    {
353
        throw new Exception\NotImplemented('Not implemented');
354
    }
355
356
    /**
357
     * {@inheritDoc}
358 2
     */
359
    public function newInstanceArgs(?array $args = null)
360 2
    {
361
        throw new Exception\NotImplemented('Not implemented');
362 2
    }
363 1
364
    /**
365
     * {@inheritDoc}
366 1
     */
367
    public function getParentClass()
368
    {
369
        $parentClass = $this->betterReflectionObject->getParentClass();
370
371
        if ($parentClass === null) {
372 2
            return false;
0 ignored issues
show
Bug Best Practice introduced by Jaroslav Hanslík
The return type of return false; (false) is incompatible with the return type of the parent method ReflectionObject::getParentClass of type object.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
373
        }
374 2
375
        return new ReflectionClass($parentClass);
376
    }
377 1
378 2
    /**
379
     * {@inheritDoc}
380 2
     */
381
    public function isSubclassOf($class)
382 2
    {
383
        $realParentClassNames = $this->betterReflectionObject->getParentClassNames();
384
385
        $parentClassNames = array_combine(array_map(static function (string $parentClassName) : string {
386
            return strtolower($parentClassName);
387
        }, $realParentClassNames), $realParentClassNames);
388 1
389
        $realParentClassName = $parentClassNames[strtolower($class)] ?? $class;
390 1
391
        return $this->betterReflectionObject->isSubclassOf($realParentClassName);
392
    }
393
394
    /**
395
     * {@inheritDoc}
396 4
     */
397
    public function getStaticProperties()
398 4
    {
399
        return $this->betterReflectionObject->getStaticProperties();
400 4
    }
401 2
402 1
    /**
403
     * {@inheritDoc}
404
     */
405 1
    public function getStaticPropertyValue($name, $default = null)
406
    {
407
        $betterReflectionProperty = $this->betterReflectionObject->getProperty($name);
408 2
409
        if ($betterReflectionProperty === null) {
410 2
            if (func_num_args() === 2) {
411 1
                return $default;
412
            }
413
414 1
            throw new CoreReflectionException(sprintf('Property "%s" does not exist', $name));
415
        }
416
417
        $property = new ReflectionProperty($betterReflectionProperty);
418 1
419
        if (! $property->isAccessible()) {
420
            throw new CoreReflectionException(sprintf('Property "%s" is not accessible', $name));
421
        }
422
423
        if (! $property->isStatic()) {
424 3
            throw new CoreReflectionException(sprintf('Property "%s" is not static', $name));
425
        }
426 3
427
        return $property->getValue();
428 3
    }
429 1
430
    /**
431
     * {@inheritDoc}
432 2
     */
433
    public function setStaticPropertyValue($name, $value)
434 2
    {
435 1
        $betterReflectionProperty = $this->betterReflectionObject->getProperty($name);
436
437
        if ($betterReflectionProperty === null) {
438 1
            throw new CoreReflectionException(sprintf('Property "%s" does not exist', $name));
439
        }
440
441
        $property = new ReflectionProperty($betterReflectionProperty);
442 1
443 1
        if (! $property->isAccessible()) {
444
            throw new CoreReflectionException(sprintf('Property "%s" is not accessible', $name));
445
        }
446
447
        if (! $property->isStatic()) {
448 1
            throw new CoreReflectionException(sprintf('Property "%s" is not static', $name));
449
        }
450 1
451
        $property->setValue($value);
452
    }
453
454
    /**
455
     * {@inheritDoc}
456 1
     */
457
    public function getDefaultProperties()
458 1
    {
459
        return $this->betterReflectionObject->getDefaultProperties();
460
    }
461
462
    /**
463
     * {@inheritDoc}
464 2
     */
465
    public function isIterateable()
466 2
    {
467
        return $this->betterReflectionObject->isIterateable();
468
    }
469 1
470 2
    /**
471
     * {@inheritDoc}
472 2
     */
473
    public function implementsInterface($interface)
474 2
    {
475
        $realInterfaceNames = $this->betterReflectionObject->getInterfaceNames();
476
477
        $interfaceNames = array_combine(array_map(static function (string $interfaceName) : string {
478
            return strtolower($interfaceName);
479
        }, $realInterfaceNames), $realInterfaceNames);
480 1
481
        $realInterfaceName = $interfaceNames[strtolower($interface)] ?? $interface;
482 1
483
        return $this->betterReflectionObject->implementsInterface($realInterfaceName);
484
    }
485
486
    /**
487
     * {@inheritDoc}
488 2
     */
489
    public function getExtension()
490 2
    {
491
        throw new Exception\NotImplemented('Not implemented');
492
    }
493
494
    /**
495
     * {@inheritDoc}
496 1
     */
497
    public function getExtensionName()
498 1
    {
499
        return $this->betterReflectionObject->getExtensionName() ?? false;
0 ignored issues
show
Bug Best Practice introduced by Jaroslav Hanslík
The return type of return $this->betterRefl...tensionName() ?? false; (false) is incompatible with the return type of the parent method ReflectionObject::getExtensionName of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
500
    }
501
502
    /**
503
     * {@inheritDoc}
504 1
     */
505
    public function inNamespace()
506 1
    {
507
        return $this->betterReflectionObject->inNamespace();
508
    }
509
510
    /**
511
     * {@inheritDoc}
512 1
     */
513
    public function getNamespaceName()
514 1
    {
515
        return $this->betterReflectionObject->getNamespaceName();
516
    }
517
518
    /**
519
     * {@inheritDoc}
520
     */
521
    public function getShortName()
522
    {
523
        return $this->betterReflectionObject->getShortName();
524
    }
525
}
526