Completed
Push — master ( ab5fb4...579c67 )
by Alexander
10s
created

ReflectionClassLikeTrait   F

Complexity

Total Complexity 154

Size/Duplication

Total Lines 921
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 12

Test Coverage

Coverage 77.08%

Importance

Changes 0
Metric Value
wmc 154
lcom 2
cbo 12
dl 0
loc 921
ccs 259
cts 336
cp 0.7708
rs 3.4285
c 0
b 0
f 0

50 Methods

Rating   Name   Duplication   Size   Complexity  
A getConstant() 0 8 2
D __toString() 0 89 16
A getConstants() 0 11 2
A getConstructor() 0 9 2
C getDefaultProperties() 0 26 7
A getDocComment() 0 6 2
A getEndLine() 0 4 1
A getExtension() 0 4 1
A getExtensionName() 0 4 1
A getFileName() 0 4 1
A getInterfaceNames() 0 4 1
A getInterfaces() 0 13 3
A getMethod() 0 11 3
C getMethods() 0 31 8
C getModifiers() 0 27 8
A getName() 0 6 2
A getNamespaceName() 0 4 1
B getParentClass() 0 17 5
C getProperties() 0 33 8
A getProperty() 0 11 3
A getShortName() 0 4 1
A getStartLine() 0 4 1
B getTraitAliases() 0 20 5
A getTraitNames() 0 4 1
A getTraits() 0 10 2
A hasConstant() 0 7 2
A hasMethod() 0 11 3
A hasProperty() 0 11 3
A implementsInterface() 0 6 1
A inNamespace() 0 4 1
B isAbstract() 0 12 7
A isAnonymous() 0 4 1
B isCloneable() 0 12 5
A isFinal() 0 6 2
A isInstance() 0 10 3
B isInstantiable() 0 12 5
A isInterface() 0 4 1
A isInternal() 0 5 1
A isIterateable() 0 4 1
C isSubclassOf() 0 23 7
A isTrait() 0 4 1
A isUserDefined() 0 5 1
B getStaticProperties() 0 21 5
A getStaticPropertyValue() 0 11 4
A newInstance() 0 6 1
A newInstanceArgs() 0 7 1
A newInstanceWithoutConstructor() 0 7 1
A setStaticPropertyValue() 0 6 1
B recursiveCollect() 0 22 4
B collectSelfConstants() 0 19 5

How to fix   Complexity   

Complex Class

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

1
<?php
2
/**
3
 * Parser Reflection API
4
 *
5
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\ParserReflection\Traits;
12
13
use Go\ParserReflection\ReflectionClass;
14
use Go\ParserReflection\ReflectionException;
15
use Go\ParserReflection\ReflectionMethod;
16
use Go\ParserReflection\ReflectionProperty;
17
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
18
use PhpParser\Node\Name\FullyQualified;
19
use PhpParser\Node\Stmt\Class_;
20
use PhpParser\Node\Stmt\ClassConst;
21
use PhpParser\Node\Stmt\ClassLike;
22
use PhpParser\Node\Stmt\Interface_;
23
use PhpParser\Node\Stmt\Trait_;
24
use PhpParser\Node\Stmt\TraitUseAdaptation;
25
26
/**
27
 * General class-like reflection
28
 */
29
trait ReflectionClassLikeTrait
30
{
31
    use InitializationTrait;
32
33
    /**
34
     * @var ClassLike
35
     */
36
    protected $classLikeNode;
37
38
    /**
39
     * Short name of the class, without namespace
40
     *
41
     * @var string
42
     */
43
    protected $className;
44
45
    /**
46
     * List of all constants from the class
47
     *
48
     * @var array
49
     */
50
    protected $constants;
51
52
    /**
53
     * Interfaces, empty array or null if not initialized yet
54
     *
55
     * @var \ReflectionClass[]|array|null
56
     */
57
    protected $interfaceClasses;
58
59
    /**
60
     * List of traits, empty array or null if not initialized yet
61
     *
62
     * @var  \ReflectionClass[]|array|null
63
     */
64
    protected $traits;
65
66
    /**
67
     * Additional list of trait adaptations
68
     *
69
     * @var TraitUseAdaptation[]|array
70
     */
71
    protected $traitAdaptations;
72
73
    /**
74
     * @var array|ReflectionMethod[]
75
     */
76
    protected $methods;
77
78
    /**
79
     * Namespace name
80
     *
81
     * @var string
82
     */
83
    protected $namespaceName = '';
84
85
    /**
86
     * Parent class, or false if not present, null if uninitialized yet
87
     *
88
     * @var \ReflectionClass|false|null
89
     */
90
    protected $parentClass;
91
92
    /**
93
     * @var array|ReflectionProperty[]
94
     */
95
    protected $properties;
96
97
    /**
98
     * Returns the string representation of the ReflectionClass object.
99
     *
100
     * @return string
101
     */
102
    public function __toString()
103
    {
104
        $isObject = $this instanceof \ReflectionObject;
105
106
        $staticProperties = $staticMethods = $defaultProperties = $dynamicProperties = $methods = [];
107
108
        $format  = "%s [ <user> %sclass %s%s%s ] {\n";
109
        $format .= "  @@ %s %d-%d\n\n";
110
        $format .= "  - Constants [%d] {%s\n  }\n\n";
111
        $format .= "  - Static properties [%d] {%s\n  }\n\n";
112
        $format .= "  - Static methods [%d] {%s\n  }\n\n";
113
        $format .= "  - Properties [%d] {%s\n  }\n\n";
114
        $format .= ($isObject ? "  - Dynamic properties [%d] {%s\n  }\n\n" : '%s%s');
115
        $format .= "  - Methods [%d] {%s\n  }\n";
116
        $format .= "}\n";
117
118
        foreach ($this->getProperties() as $property) {
119
            if ($property->isStatic()) {
120
                $staticProperties[] = $property;
121
            } elseif ($property->isDefault()) {
122
                $defaultProperties[] = $property;
123
            } else {
124
                $dynamicProperties[] = $property;
125
            }
126
        }
127
128
        foreach ($this->getMethods() as $method) {
129
            if ($method->isStatic()) {
130
                $staticMethods[] = $method;
131
            } else {
132
                $methods[] = $method;
133
            }
134
        }
135
136
        $buildString = function (array $items, $indentLevel = 4) {
137
            if (!count($items)) {
138
                return '';
139
            }
140
            $indent = "\n" . str_repeat(' ', $indentLevel);
141
            return $indent . implode($indent, explode("\n", implode("\n", $items)));
142
        };
143
        $buildConstants = function (array $items, $indentLevel = 4) {
144
            $str = '';
145
            foreach ($items as $name => $value) {
146
                $str .= "\n" . str_repeat(' ', $indentLevel);
147
                $str .= sprintf(
148
                    'Constant [ %s %s ] { %s }',
149
                    gettype($value),
150
                    $name,
151
                    $value
152
                );
153
            }
154
            return $str;
155
        };
156
        $interfaceNames = $this->getInterfaceNames();
157
        $parentClass    = $this->getParentClass();
158
        $modifiers      = '';
159
        if ($this->isAbstract()) {
160
            $modifiers = 'abstract ';
161
        } elseif ($this->isFinal()) {
162
            $modifiers = 'final ';
163
        };
164
165
        $string = sprintf(
166
            $format,
167
            ($isObject ? 'Object of class' : 'Class'),
168
            $modifiers,
169
            $this->getName(),
170
            false !== $parentClass ? (' extends ' . $parentClass->getName()) : '',
171
            $interfaceNames ? (' implements ' . implode(', ', $interfaceNames)) : '',
172
            $this->getFileName(),
173
            $this->getStartLine(),
174
            $this->getEndLine(),
175
            count($this->getConstants()),
176
            $buildConstants($this->getConstants()),
177
            count($staticProperties),
178
            $buildString($staticProperties),
179
            count($staticMethods),
180
            $buildString($staticMethods),
181
            count($defaultProperties),
182
            $buildString($defaultProperties),
183
            $isObject ? count($dynamicProperties) : '',
184
            $isObject ? $buildString($dynamicProperties) : '',
185
            count($methods),
186
            $buildString($methods)
187
        );
188
189
        return $string;
190
    }
191
192
193
    /**
194
     * {@inheritDoc}
195
     */
196 6
    public function getConstant($name)
197
    {
198 6
        if ($this->hasConstant($name)) {
199 6
            return $this->constants[$name];
200
        }
201
202
        return false;
203
    }
204
205
    /**
206
     * {@inheritDoc}
207
     */
208 26
    public function getConstants()
209
    {
210 26
        if (!isset($this->constants)) {
211
            $this->constants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
212 11
                $result += $instance->getConstants();
213 26
            });
214 26
            $this->collectSelfConstants();
215
        }
216
217 26
        return $this->constants;
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223 13
    public function getConstructor()
224
    {
225 13
        $constructor = $this->getMethod('__construct');
226 13
        if (!$constructor) {
227 11
            return null;
228
        }
229
230 2
        return $constructor;
231
    }
232
233
    /**
234
     * Gets default properties from a class (including inherited properties).
235
     *
236
     * @link http://php.net/manual/en/reflectionclass.getdefaultproperties.php
237
     *
238
     * @return array An array of default properties, with the key being the name of the property and the value being
239
     * the default value of the property or NULL if the property doesn't have a default value
240
     */
241 22
    public function getDefaultProperties()
242
    {
243 22
        $defaultValues = [];
244 22
        $properties    = $this->getProperties();
245 22
        $staticOrder   = [true, false];
246 22
        foreach ($staticOrder as $shouldBeStatic) {
247 22
            foreach ($properties as $property) {
248 7
                $isStaticProperty     = $property->isStatic();
249 7
                if ($shouldBeStatic !== $isStaticProperty) {
250 7
                    continue;
251
                }
252 7
                $propertyName         = $property->getName();
253 7
                $isInternalReflection = get_class($property) == \ReflectionProperty::class;
254
255 7
                if (!$isInternalReflection || $isStaticProperty) {
256 7
                    $defaultValues[$propertyName] = $property->getValue();
257 1
                } elseif (!$isStaticProperty) {
258
                    // Internal reflection and dynamic property
259 1
                    $classProperties = $property->getDeclaringClass()->getDefaultProperties();
260 22
                    $defaultValues[$propertyName] = $classProperties[$propertyName];
261
                }
262
            }
263
        }
264
265 22
        return $defaultValues;
266
    }
267
268
    /**
269
     * {@inheritDoc}
270
     */
271 22
    public function getDocComment()
272
    {
273 22
        $docComment = $this->classLikeNode->getDocComment();
274
275 22
        return $docComment ? $docComment->getText() : false;
276
    }
277
278 22
    public function getEndLine()
279
    {
280 22
        return $this->classLikeNode->getAttribute('endLine');
281
    }
282
283 22
    public function getExtension()
284
    {
285 22
        return null;
286
    }
287
288 22
    public function getExtensionName()
289
    {
290 22
        return false;
291
    }
292
293 11
    public function getFileName()
294
    {
295 11
        return $this->classLikeNode->getAttribute('fileName');
296
    }
297
298
    /**
299
     * {@inheritDoc}
300
     */
301 22
    public function getInterfaceNames()
302
    {
303 22
        return array_keys($this->getInterfaces());
304
    }
305
306
    /**
307
     * {@inheritDoc}
308
     */
309 22
    public function getInterfaces()
310
    {
311 22
        if (!isset($this->interfaceClasses)) {
312
            $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
313 9
                if ($instance->isInterface()) {
314 2
                    $result[$instance->name] = $instance;
315
                }
316 9
                $result += $instance->getInterfaces();
317 22
            });
318
        }
319
320 22
        return $this->interfaceClasses;
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326 25
    public function getMethod($name)
327
    {
328 25
        $methods = $this->getMethods();
329 25
        foreach ($methods as $method) {
330 19
            if ($method->getName() == $name) {
331 19
                return $method;
332
            }
333
        }
334
335 11
        return false;
336
    }
337
338
    /**
339
     * Returns list of reflection methods
340
     *
341
     * @param null|integer $filter Optional filter
342
     *
343
     * @return array|\ReflectionMethod[]
344
     */
345 70
    public function getMethods($filter = null)
346
    {
347 70
        if (!isset($this->methods)) {
348 70
            $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this);
349
            $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
350 30
                $reflectionMethods = [];
351 30
                foreach ($instance->getMethods() as $reflectionMethod) {
352 20
                    if (!$isParent || !$reflectionMethod->isPrivate()) {
353 20
                        $reflectionMethods[$reflectionMethod->name] = $reflectionMethod;
354
                    }
355
                }
356 30
                $result += $reflectionMethods;
357 70
            });
358 70
            $methods = $directMethods + $parentMethods;
359
360 70
            $this->methods = $methods;
361
        }
362 70
        if (!isset($filter)) {
363 51
            return array_values($this->methods);
364
        }
365
366 19
        $methods = [];
367 19
        foreach ($this->methods as $method) {
368 12
            if (!($filter & $method->getModifiers())) {
369 10
                continue;
370
            }
371 4
            $methods[] = $method;
372
        }
373
374 19
        return $methods;
375
    }
376
377
    /**
378
     * Returns a bitfield of the access modifiers for this class.
379
     *
380
     * @link http://php.net/manual/en/reflectionclass.getmodifiers.php
381
     *
382
     * NB: this method is not fully compatible with original value because of hidden internal constants
383
     *
384
     * @return int
385
     */
386 21
    public function getModifiers()
387
    {
388 21
        $modifiers = 0;
389
390 21
        if ($this->isFinal()) {
391 1
            $modifiers += \ReflectionClass::IS_FINAL;
392
        }
393
394 21
        if (PHP_VERSION_ID < 70000 && $this->isTrait()) {
395
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
396
        }
397
398 21
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
399 4
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
400
        }
401
402 21
        if ($this->isInterface()) {
403 3
            $abstractMethods = $this->getMethods();
404
        } else {
405 18
            $abstractMethods = $this->getMethods(\ReflectionMethod::IS_ABSTRACT);
406
        }
407 21
        if (!empty($abstractMethods)) {
408 5
            $modifiers += \ReflectionClass::IS_IMPLICIT_ABSTRACT;
409
        }
410
411 21
        return $modifiers;
412
    }
413
414
    /**
415
     * {@inheritDoc}
416
     */
417 80
    public function getName()
418
    {
419 80
        $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
420
421 80
        return $namespaceName . $this->getShortName();
422
    }
423
424
    /**
425
     * {@inheritDoc}
426
     */
427 29
    public function getNamespaceName()
428
    {
429 29
        return $this->namespaceName;
430
    }
431
432
    /**
433
     * {@inheritDoc}
434
     */
435 102
    public function getParentClass()
436
    {
437 102
        if (!isset($this->parentClass)) {
438 102
            static $extendsField = 'extends';
439
440 102
            $parentClass = false;
441 102
            $hasExtends  = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
442 102
            $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
443 102
            if ($extendsNode instanceof FullyQualified) {
444 22
                $extendsName = $extendsNode->toString();
445 22
                $parentClass = class_exists($extendsName, false) ? new parent($extendsName) : new static($extendsName);
0 ignored issues
show
Unused Code introduced by
The call to ReflectionClassLikeTrait::__construct() has too many arguments starting with $extendsName.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
446
            }
447 102
            $this->parentClass = $parentClass;
448
        }
449
450 102
        return $this->parentClass;
451
    }
452
453
    /**
454
     * Retrieves reflected properties.
455
     *
456
     * @param int $filter The optional filter, for filtering desired property types.
457
     *                    It's configured using the ReflectionProperty constants, and defaults to all property types.
458
     *
459
     * @return array|\Go\ParserReflection\ReflectionProperty[]
460
     */
461 46
    public function getProperties($filter = null)
462
    {
463 46
        if (!isset($this->properties)) {
464 46
            $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
465 46
            $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
466 17
                $reflectionProperties = [];
467 17
                foreach ($instance->getProperties() as $reflectionProperty) {
468 4
                    if (!$isParent || !$reflectionProperty->isPrivate()) {
469 4
                        $reflectionProperties[$reflectionProperty->name] = $reflectionProperty;
470
                    }
471
                }
472 17
                $result += $reflectionProperties;
473 46
            });
474 46
            $properties = $directProperties + $parentProperties;
475
476 46
            $this->properties = $properties;
477
        }
478
479
        // Without filter we can just return the full list
480 46
        if (!isset($filter)) {
481 46
            return array_values($this->properties);
482
        }
483
484 22
        $properties = [];
485 22
        foreach ($this->properties as $property) {
486 7
            if (!($filter & $property->getModifiers())) {
487 5
                continue;
488
            }
489 4
            $properties[] = $property;
490
        }
491
492 22
        return $properties;
493
    }
494
495
    /**
496
     * {@inheritdoc}
497
     */
498 3
    public function getProperty($name)
499
    {
500 3
        $properties = $this->getProperties();
501 3
        foreach ($properties as $property) {
502 3
            if ($property->getName() == $name) {
503 3
                return $property;
504
            }
505
        }
506
507
        return false;
508
    }
509
510
    /**
511
     * {@inheritDoc}
512
     */
513 80
    public function getShortName()
514
    {
515 80
        return $this->className;
516
    }
517
518 22
    public function getStartLine()
519
    {
520 22
        return $this->classLikeNode->getAttribute('startLine');
521
    }
522
523
    /**
524
     * Returns an array of trait aliases
525
     *
526
     * @link http://php.net/manual/en/reflectionclass.gettraitaliases.php
527
     *
528
     * @return array|null an array with new method names in keys and original names (in the format "TraitName::original") in
529
     * values.
530
     */
531 41
    public function getTraitAliases()
532
    {
533 41
        $aliases = [];
534 41
        $traits  = $this->getTraits();
535 41
        foreach ($this->traitAdaptations as $adaptation) {
536 2
            if ($adaptation instanceof TraitUseAdaptation\Alias) {
537 2
                $methodName = $adaptation->method;
538 2
                $traitName  = null;
539 2
                foreach ($traits as $trait) {
540 2
                    if ($trait->hasMethod($methodName)) {
541 2
                        $traitName = $trait->getName();
542 2
                        break;
543
                    }
544
                }
545 2
                $aliases[$adaptation->newName] = $traitName . '::'. $methodName;
546
            }
547
        }
548
549 41
        return $aliases;
550
    }
551
552
    /**
553
     * Returns an array of names of traits used by this class
554
     *
555
     * @link http://php.net/manual/en/reflectionclass.gettraitnames.php
556
     *
557
     * @return array
558
     */
559 22
    public function getTraitNames()
560
    {
561 22
        return array_keys($this->getTraits());
562
    }
563
564
    /**
565
     * Returns an array of traits used by this class
566
     *
567
     * @link http://php.net/manual/en/reflectionclass.gettraits.php
568
     *
569
     * @return array|\ReflectionClass[]
570
     */
571 102
    public function getTraits()
572
    {
573 102
        if (!isset($this->traits)) {
574 102
            $traitAdaptations = [];
575 102
            $this->traits     = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode, $traitAdaptations);
576 102
            $this->traitAdaptations = $traitAdaptations;
577
        }
578
579 102
        return $this->traits;
580
    }
581
582
    /**
583
     * {@inheritDoc}
584
     */
585 7
    public function hasConstant($name)
586
    {
587 7
        $constants   = $this->getConstants();
588 7
        $hasConstant = isset($constants[$name]) || array_key_exists($name, $constants);
589
590 7
        return $hasConstant;
591
    }
592
593
    /**
594
     * {@inheritdoc}
595
     */
596 15
    public function hasMethod($name)
597
    {
598 15
        $methods = $this->getMethods();
599 15
        foreach ($methods as $method) {
600 9
            if ($method->getName() == $name) {
601 9
                return true;
602
            }
603
        }
604
605 12
        return false;
606
    }
607
608
    /**
609
     * {@inheritdoc}
610
     */
611
    public function hasProperty($name)
612
    {
613
        $properties = $this->getProperties();
614
        foreach ($properties as $property) {
615
            if ($property->getName() == $name) {
616
                return true;
617
            }
618
        }
619
620
        return false;
621
    }
622
623
    /**
624
     * {@inheritDoc}
625
     */
626 22
    public function implementsInterface($interfaceName)
627
    {
628 22
        $allInterfaces = $this->getInterfaces();
629
630 22
        return isset($allInterfaces[$interfaceName]);
631
    }
632
633
    /**
634
     * {@inheritDoc}
635
     */
636 22
    public function inNamespace()
637
    {
638 22
        return !empty($this->namespaceName);
639
    }
640
641
    /**
642
     * {@inheritDoc}
643
     */
644 22
    public function isAbstract()
645
    {
646 22
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
647 4
            return true;
648 18
        } elseif ($this->isInterface() && !empty($this->getMethods())) {
649 2
            return true;
650 16
        } elseif ($this->isTrait()) {
651 2
            return PHP_VERSION_ID < 70000 ? true : false;
652
        }
653
654 14
        return false;
655
    }
656
657
    /**
658
     * Currently, anonymous classes aren't supported for parsed reflection
659
     */
660
    public function isAnonymous()
661
    {
662
        return false;
663
    }
664
665
    /**
666
     * {@inheritDoc}
667
     */
668 22
    public function isCloneable()
669
    {
670 22
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
671 9
            return false;
672
        }
673
674 13
        if ($this->hasMethod('__clone')) {
675 1
            return $this->getMethod('__clone')->isPublic();
676
        }
677
678 12
        return true;
679
    }
680
681
    /**
682
     * {@inheritDoc}
683
     */
684 43
    public function isFinal()
685
    {
686 43
        $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
687
688 43
        return $isFinal;
689
    }
690
691
    /**
692
     * {@inheritDoc}
693
     */
694
    public function isInstance($object)
695
    {
696
        if (!is_object($object)) {
697
            throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
698
        }
699
700
        $className = $this->getName();
701
702
        return $className === get_class($object) || is_subclass_of($object, $className);
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $className can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
703
    }
704
705
    /**
706
     * {@inheritDoc}
707
     */
708 22
    public function isInstantiable()
709
    {
710 22
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
711 9
            return false;
712
        }
713
714 13
        if (null === ($constructor = $this->getConstructor())) {
715 11
            return true;
716
        }
717
718 2
        return $constructor->isPublic();
719
    }
720
721
    /**
722
     * {@inheritDoc}
723
     */
724 43
    public function isInterface()
725
    {
726 43
        return ($this->classLikeNode instanceof Interface_);
727
    }
728
729
    /**
730
     * {@inheritDoc}
731
     */
732 22
    public function isInternal()
733
    {
734
        // never can be an internal method
735 22
        return false;
736
    }
737
738
    /**
739
     * {@inheritDoc}
740
     */
741 22
    public function isIterateable()
742
    {
743 22
        return $this->implementsInterface('Traversable');
744
    }
745
746
    /**
747
     * {@inheritDoc}
748
     */
749
    public function isSubclassOf($class)
750
    {
751
        if (is_object($class)) {
752
            if ($class instanceof ReflectionClass) {
753
                $class = $class->name;
754
            } else {
755
                $class = get_class($class);
756
            }
757
        }
758
759
        if (!$this->classLikeNode instanceof Class_) {
760
            return false;
761
        } else {
762
            $extends = $this->classLikeNode->extends;
763
            if ($extends && $extends->toString() == $class) {
764
                return true;
765
            }
766
        }
767
768
        $parent = $this->getParentClass();
769
770
        return false === $parent ? false : $parent->isSubclassOf($class);
771
    }
772
773
    /**
774
     * {@inheritDoc}
775
     */
776 22
    public function isTrait()
777
    {
778 22
        return ($this->classLikeNode instanceof Trait_);
779
    }
780
781
    /**
782
     * {@inheritDoc}
783
     */
784 22
    public function isUserDefined()
785
    {
786
        // always defined by user, because we parse the source code
787 22
        return true;
788
    }
789
790
    /**
791
     * Gets static properties
792
     *
793
     * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
794
     *
795
     * @return array
796
     */
797 23
    public function getStaticProperties()
798
    {
799
        // In runtime static properties can be changed in any time
800 23
        if ($this->isInitialized()) {
801 1
            return forward_static_call('parent::getStaticProperties');
802
        }
803
804 22
        $properties = [];
805
806 22
        $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
807 22
        foreach ($reflectionProperties as $reflectionProperty) {
808 4
            if (!$reflectionProperty instanceof ReflectionProperty) {
809 1
                if (!$reflectionProperty->isPublic()) {
810 1
                    $reflectionProperty->setAccessible(true);
811
                }
812
            }
813 4
            $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
814
        }
815
816 22
        return $properties;
817
    }
818
819
    /**
820
     * Gets static property value
821
     *
822
     * @param string $name    The name of the static property for which to return a value.
823
     * @param mixed  $default A default value to return in case the class does not declare
824
     *                        a static property with the given name
825
     *
826
     * @return mixed
827
     * @throws ReflectionException If there is no such property and no default value was given
828
     */
829 1
    public function getStaticPropertyValue($name, $default = null)
830
    {
831 1
        $properties     = $this->getStaticProperties();
832 1
        $propertyExists = array_key_exists($name, $properties);
833
834 1
        if (!$propertyExists && func_num_args() === 1) {
835
            throw new ReflectionException("Static property does not exist and no default value is given");
836
        }
837
838 1
        return $propertyExists ? $properties[$name] : $default;
839
    }
840
841
842
    /**
843
     * Creates a new class instance from given arguments.
844
     *
845
     * @link http://php.net/manual/en/reflectionclass.newinstance.php
846
     * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
847
     *
848
     * @return object
849
     */
850 1
    public function newInstance($args = null)
0 ignored issues
show
Unused Code introduced by
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...
851
    {
852 1
        $this->initializeInternalReflection();
853
854 1
        return call_user_func_array('parent::newInstance', func_get_args());
855
    }
856
857
    /**
858
     * Creates a new class instance from given arguments.
859
     *
860
     * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
861
     *
862
     * @param array $args The parameters to be passed to the class constructor as an array.
863
     *
864
     * @return object
865
     */
866 1
    public function newInstanceArgs(array $args = [])
867
    {
868 1
        $function = __FUNCTION__;
869 1
        $this->initializeInternalReflection();
870
871 1
        return parent::$function($args);
872
    }
873
874
    /**
875
     * Creates a new class instance without invoking the constructor.
876
     *
877
     * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
878
     *
879
     * @return object
880
     */
881 1
    public function newInstanceWithoutConstructor($args = null)
882
    {
883 1
        $function = __FUNCTION__;
884 1
        $this->initializeInternalReflection();
885
886 1
        return parent::$function($args);
887
    }
888
889
    /**
890
     * Sets static property value
891
     *
892
     * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
893
     *
894
     * @param string $name Property name
895
     * @param mixed $value New property value
896
     */
897 1
    public function setStaticPropertyValue($name, $value)
898
    {
899 1
        $this->initializeInternalReflection();
900
901 1
        forward_static_call('parent::setStaticPropertyValue', $name, $value);
902 1
    }
903
904 102
    private function recursiveCollect(\Closure $collector)
905
    {
906 102
        $result   = [];
907 102
        $isParent = true;
908
909 102
        $traits = $this->getTraits();
910 102
        foreach ($traits as $trait) {
911 14
            $collector($result, $trait, !$isParent);
912
        }
913
914 102
        $parentClass = $this->getParentClass();
915 102
        if ($parentClass) {
916 22
            $collector($result, $parentClass, $isParent);
917
        }
918
919 102
        $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
920 102
        foreach ($interfaces as $interface) {
921 8
            $collector($result, $interface, $isParent);
922
        }
923
924 102
        return $result;
925
    }
926
927
    /**
928
     * Collects list of constants from the class itself
929
     */
930 26
    private function collectSelfConstants()
931
    {
932 26
        $expressionSolver = new NodeExpressionResolver($this);
933 26
        $localConstants   = [];
934
935
        // constants can be only top-level nodes in the class, so we can scan them directly
936 26
        foreach ($this->classLikeNode->stmts as $classLevelNode) {
937 22
            if ($classLevelNode instanceof ClassConst) {
938 8
                $nodeConstants = $classLevelNode->consts;
939 8
                if (!empty($nodeConstants)) {
940 8
                    foreach ($nodeConstants as $nodeConstant) {
941 8
                        $expressionSolver->process($nodeConstant->value);
942 8
                        $localConstants[$nodeConstant->name] = $expressionSolver->getValue();
943 22
                        $this->constants = $localConstants + $this->constants;
944
                    }
945
                }
946
            }
947
        }
948 26
    }
949
}
950