Completed
Push — master ( 6c5557...d84e1e )
by Alexander
03:46
created

ReflectionClassLikeTrait   D

Complexity

Total Complexity 129

Size/Duplication

Total Lines 798
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 11

Test Coverage

Coverage 67.07%

Importance

Changes 3
Bugs 1 Features 1
Metric Value
wmc 129
c 3
b 1
f 1
lcom 2
cbo 11
dl 0
loc 798
ccs 222
cts 331
cp 0.6707
rs 4

47 Methods

Rating   Name   Duplication   Size   Complexity  
D __toString() 0 89 16
A getConstant() 0 8 2
A getConstants() 0 14 2
A getConstructor() 0 9 2
A getDocComment() 0 4 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
A getMethods() 0 15 2
A getName() 0 6 2
A getNamespaceName() 0 4 1
B getParentClass() 0 17 5
C getProperties() 0 42 14
A getProperty() 0 11 3
A getShortName() 0 4 1
A getStartLine() 0 4 1
A getTraitNames() 0 4 1
A getTraits() 0 8 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 10 5
A isAnonymous() 0 4 1
A isCloneable() 0 12 4
A isFinal() 0 6 2
A isInstance() 0 10 3
A isInstantiable() 0 12 4
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
A recursiveCollect() 0 16 3
B findConstants() 0 20 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
25
/**
26
 * General class-like reflection
27
 */
28
trait ReflectionClassLikeTrait
29
{
30
    use InitializationTrait;
31
32
    /**
33
     * @var ClassLike
34
     */
35
    protected $classLikeNode;
36
37
    /**
38
     * Short name of the class, without namespace
39
     *
40
     * @var string
41
     */
42
    protected $className;
43
44
    /**
45
     * List of all constants from the class
46
     *
47
     * @var array
48
     */
49
    protected $constants;
50
51
    /**
52
     * Interfaces, empty array or null if not initialized yet
53
     *
54
     * @var \ReflectionClass[]|array|null
55
     */
56
    protected $interfaceClasses;
57
58
    /**
59
     * List of traits, empty array or null if not initialized yet
60
     *
61
     * @var  \ReflectionClass[]|array|null
62
     */
63
    protected $traits;
64
65
    /**
66
     * @var array|ReflectionMethod[]
67
     */
68
    protected $methods;
69
70
    /**
71
     * Namespace name
72
     *
73
     * @var string
74
     */
75
    protected $namespaceName = '';
76
77
    /**
78
     * Parent class, or false if not present, null if uninitialized yet
79
     *
80
     * @var \ReflectionClass|false|null
81
     */
82
    protected $parentClass;
83
84
    /**
85
     * @var array|ReflectionProperty[]
86
     */
87
    protected $properties;
88
89
    /**
90
     * Returns the string representation of the ReflectionClass object.
91
     *
92
     * @return string
93
     */
94
    public function __toString()
95
    {
96
        $isObject = $this instanceof \ReflectionObject;
97
98
        $staticProperties = $staticMethods = $defaultProperties = $dynamicProperties = $methods = [];
99
100
        $format  = "%s [ <user> %sclass %s%s%s ] {\n";
101
        $format .= "  @@ %s %d-%d\n\n";
102
        $format .= "  - Constants [%d] {%s\n  }\n\n";
103
        $format .= "  - Static properties [%d] {%s\n  }\n\n";
104
        $format .= "  - Static methods [%d] {%s\n  }\n\n";
105
        $format .= "  - Properties [%d] {%s\n  }\n\n";
106
        $format .= ($isObject ? "  - Dynamic properties [%d] {%s\n  }\n\n" : '%s%s');
107
        $format .= "  - Methods [%d] {%s\n  }\n";
108
        $format .= "}\n";
109
110
        foreach ($this->getProperties() as $property) {
111
            if ($property->isStatic()) {
112
                $staticProperties[] = $property;
113
            } elseif ($property->isDefault()) {
114
                $defaultProperties[] = $property;
115
            } else {
116
                $dynamicProperties[] = $property;
117
            }
118
        }
119
120
        foreach ($this->getMethods() as $method) {
121
            if ($method->isStatic()) {
122
                $staticMethods[] = $method;
123
            } else {
124
                $methods[] = $method;
125
            }
126
        }
127
128
        $buildString = function (array $items, $indentLevel = 4) {
129
            if (!count($items)) {
130
                return '';
131
            }
132
            $indent = "\n" . str_repeat(' ', $indentLevel);
133
            return $indent . implode($indent, explode("\n", implode("\n", $items)));
134
        };
135
        $buildConstants = function (array $items, $indentLevel = 4) {
136
            $str = '';
137
            foreach ($items as $name => $value) {
138
                $str .= "\n" . str_repeat(' ', $indentLevel);
139
                $str .= sprintf(
140
                    'Constant [ %s %s ] { %s }',
141
                    gettype($value),
142
                    $name,
143
                    $value
144
                );
145
            }
146
            return $str;
147
        };
148
        $interfaceNames = $this->getInterfaceNames();
149
        $parentClass    = $this->getParentClass();
150
        $modifiers      = '';
151
        if ($this->isAbstract()) {
152
            $modifiers = 'abstract ';
153
        } elseif ($this->isFinal()) {
154
            $modifiers = 'final ';
155
        };
156
157
        $string = sprintf(
158
            $format,
159
            ($isObject ? 'Object of class' : 'Class'),
160
            $modifiers,
161
            $this->getName(),
162
            false !== $parentClass ? (' extends ' . $parentClass->getName()) : '',
163
            $interfaceNames ? (' implements ' . implode(', ', $interfaceNames)) : '',
164
            $this->getFileName(),
165
            $this->getStartLine(),
166
            $this->getEndLine(),
167
            count($this->getConstants()),
168
            $buildConstants($this->getConstants()),
169
            count($staticProperties),
170
            $buildString($staticProperties),
171
            count($staticMethods),
172
            $buildString($staticMethods),
173
            count($defaultProperties),
174
            $buildString($defaultProperties),
175
            $isObject ? count($dynamicProperties) : '',
176
            $isObject ? $buildString($dynamicProperties) : '',
177
            count($methods),
178
            $buildString($methods)
179
        );
180
181
        return $string;
182
    }
183
184
185
    /**
186
     * {@inheritDoc}
187
     */
188 3
    public function getConstant($name)
189
    {
190 3
        if ($this->hasConstant($name)) {
191 3
            return $this->constants[$name];
192
        }
193
194
        return false;
195
    }
196
197
    /**
198
     * {@inheritDoc}
199
     */
200 3
    public function getConstants()
201
    {
202 3
        if (!isset($this->constants)) {
203 3
            $directConstants = $this->findConstants();
204
            $parentConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
205 2
                $result += $instance->getConstants();
206 3
            });
207 3
            $constants = $directConstants + $parentConstants;
208
209 3
            $this->constants = $constants;
210 3
        }
211
212 3
        return $this->constants;
213
    }
214
215
    /**
216
     * {@inheritDoc}
217
     */
218 2
    public function getConstructor()
219
    {
220 2
        $constructor = $this->getMethod('__construct');
221 2
        if (!$constructor) {
222 2
            return null;
223
        }
224
225 1
        return $constructor;
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231 2
    public function getDocComment()
232
    {
233 2
        return $this->classLikeNode->getDocComment() ?: false;
234
    }
235
236 2
    public function getEndLine()
237
    {
238 2
        return $this->classLikeNode->getAttribute('endLine');
239
    }
240
241 2
    public function getExtension()
242
    {
243 2
        return null;
244
    }
245
246 2
    public function getExtensionName()
247
    {
248 2
        return false;
249
    }
250
251 5
    public function getFileName()
252
    {
253 5
        return $this->classLikeNode->getAttribute('fileName');
254
    }
255
256
    /**
257
     * {@inheritDoc}
258
     */
259 2
    public function getInterfaceNames()
260
    {
261 2
        return array_keys($this->getInterfaces());
262
    }
263
264
    /**
265
     * {@inheritDoc}
266
     */
267 2
    public function getInterfaces()
268
    {
269 2
        if (!isset($this->interfaceClasses)) {
270
            $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
271 2
                if ($instance->isInterface()) {
272 1
                    $result[$instance->name] = $instance;
273 1
                }
274 2
                $result += $instance->getInterfaces();
275 2
            });
276 2
        }
277
278 2
        return $this->interfaceClasses;
279
    }
280
281
    /**
282
     * {@inheritdoc}
283
     */
284 5
    public function getMethod($name)
285
    {
286 5
        $methods = $this->getMethods();
287 5
        foreach ($methods as $method) {
288 4
            if ($method->getName() == $name) {
289 4
                return $method;
290
            }
291 3
        }
292
293 2
        return false;
294
    }
295
296
    /**
297
     * Returns list of reflection methods
298
     *
299
     * @return ReflectionMethod[]|array
300
     */
301 6
    public function getMethods($filter = null)
0 ignored issues
show
Unused Code introduced by
The parameter $filter 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...
302
    {
303 6
        if (!isset($this->methods)) {
304 6
            $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this->getName());
305
            $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
306 3
                $result = array_merge($result, $instance->getMethods());
307 6
            });
308 6
            $methods = array_merge($directMethods, $parentMethods);
309
310 6
            $this->methods = $methods;
311 6
        }
312
        // TODO: Implement filtration of methods
313
314 6
        return $this->methods;
315
    }
316
317
    /**
318
     * {@inheritDoc}
319
     */
320 13
    public function getName()
321
    {
322 13
        $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
323
324 13
        return $namespaceName . $this->getShortName();
325
    }
326
327
    /**
328
     * {@inheritDoc}
329
     */
330 5
    public function getNamespaceName()
331
    {
332 5
        return $this->namespaceName;
333
    }
334
335
    /**
336
     * {@inheritDoc}
337
     */
338 9
    public function getParentClass()
339
    {
340 9
        if (!isset($this->parentClass)) {
341 9
            static $extendsField = 'extends';
342
343 9
            $parentClass = false;
344 9
            $hasExtends  = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
345 9
            $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
346 9
            if ($extendsNode instanceof FullyQualified) {
347 3
                $extendsName = $extendsNode->toString();
348 3
                $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...
349 3
            }
350 9
            $this->parentClass = $parentClass;
351 9
        }
352
353 9
        return $this->parentClass;
354
    }
355
356
    /**
357
     * Retrieves reflected properties.
358
     *
359
     * @param int $filter The optional filter, for filtering desired property types.
360
     *                    It's configured using the ReflectionProperty constants, and defaults to all property types.
361
     *
362
     * @return array|\Go\ParserReflection\ReflectionProperty[]
363
     */
364 5
    public function getProperties($filter = null)
365
    {
366 5
        if (!isset($this->properties)) {
367 5
            $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
368 5
            $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
369 2
                $reflectionProperties = [];
370 2
                foreach ($instance->getProperties() as $reflectionProperty) {
371 1
                    if (!$reflectionProperty->isPrivate()) {
372 1
                        $reflectionProperties[] = $reflectionProperty;
373 1
                    }
374 2
                }
375 2
                $result = array_merge($result, $reflectionProperties);
376 5
            });
377 5
            $properties = array_merge($directProperties, $parentProperties);
378
379 5
            $this->properties = $properties;
380 5
        }
381
382
        // Without filter we can just return the full list
383 5
        if (!isset($filter)) {
384 3
            return $this->properties;
385
        }
386
387 2
        $properties = [];
388 2
        foreach ($this->properties as $property) {
389 1
            if (($filter & ReflectionProperty::IS_STATIC) && !($property->isStatic())) {
390 1
                continue;
391
            }
392 1
            if (($filter & ReflectionProperty::IS_PUBLIC) && !($property->isPublic())) {
393
                continue;
394
            }
395 1
            if (($filter & ReflectionProperty::IS_PROTECTED) && !($property->isProtected())) {
396
                continue;
397
            }
398 1
            if (($filter & ReflectionProperty::IS_PRIVATE) && !($property->isPrivate())) {
399
                continue;
400
            }
401 1
            $properties[] = $property;
402 2
        }
403
404 2
        return $properties;
405
    }
406
407
    /**
408
     * {@inheritdoc}
409
     */
410 3
    public function getProperty($name)
411
    {
412 3
        $properties = $this->getProperties();
413 3
        foreach ($properties as $property) {
414 3
            if ($property->getName() == $name) {
415 3
                return $property;
416
            }
417 3
        }
418
419
        return false;
420
    }
421
422
    /**
423
     * {@inheritDoc}
424
     */
425 13
    public function getShortName()
426
    {
427 13
        return $this->className;
428
    }
429
430 2
    public function getStartLine()
431
    {
432 2
        return $this->classLikeNode->getAttribute('startLine');
433
    }
434
435
    /**
436
     * Returns an array of names of traits used by this class
437
     *
438
     * @link http://php.net/manual/en/reflectionclass.gettraitnames.php
439
     *
440
     * @return array
441
     */
442 2
    public function getTraitNames()
443
    {
444 2
        return array_keys($this->getTraits());
445
    }
446
447
    /**
448
     * Returns an array of traits used by this class
449
     *
450
     * @link http://php.net/manual/en/reflectionclass.gettraits.php
451
     *
452
     * @return array|\ReflectionClass[]
453
     */
454 2
    public function getTraits()
455
    {
456 2
        if (!isset($this->traits)) {
457 2
            $this->traits = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode);
458 2
        }
459
460 2
        return $this->traits;
461
    }
462
463
    /**
464
     * {@inheritDoc}
465
     */
466 3
    public function hasConstant($name)
467
    {
468 3
        $constants   = $this->getConstants();
469 3
        $hasConstant = isset($constants[$name]) || array_key_exists($constants, $name);
470
471 3
        return $hasConstant;
472
    }
473
474
    /**
475
     * {@inheritdoc}
476
     */
477 4
    public function hasMethod($name)
478
    {
479 4
        $methods = $this->getMethods();
480 4
        foreach ($methods as $method) {
481 3
            if ($method->getName() == $name) {
482 3
                return true;
483
            }
484 2
        }
485
486 2
        return false;
487
    }
488
489
    /**
490
     * {@inheritdoc}
491
     */
492
    public function hasProperty($name)
493
    {
494
        $properties = $this->getProperties();
495
        foreach ($properties as $property) {
496
            if ($property->getName() == $name) {
497
                return true;
498
            }
499
        }
500
501
        return false;
502
    }
503
504
    /**
505
     * {@inheritDoc}
506
     */
507 2
    public function implementsInterface($interfaceName)
508
    {
509 2
        $allInterfaces = $this->getInterfaces();
510
511 2
        return isset($allInterfaces[$interfaceName]);
512
    }
513
514
    /**
515
     * {@inheritDoc}
516
     */
517 2
    public function inNamespace()
518
    {
519 2
        return !empty($this->namespaceName);
520
    }
521
522
    /**
523
     * {@inheritDoc}
524
     */
525 2
    public function isAbstract()
526
    {
527 2
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
528 1
            return true;
529 2
        } elseif ($this->isInterface() && !empty($this->getMethods())) {
530
            return true;
531
        }
532
533 2
        return false;
534
    }
535
536
    /**
537
     * Currently, anonymous classes aren't supported for parsed reflection
538
     */
539
    public function isAnonymous()
540
    {
541
        return false;
542
    }
543
544
    /**
545
     * {@inheritDoc}
546
     */
547 2
    public function isCloneable()
548
    {
549 2
        if ($this->isInterface() || $this->isAbstract()) {
550 1
            return false;
551
        }
552
553 2
        if ($this->hasMethod('__clone')) {
554 1
            return $this->getMethod('__clone')->isPublic();
555
        }
556
557 2
        return true;
558
    }
559
560
    /**
561
     * {@inheritDoc}
562
     */
563 2
    public function isFinal()
564
    {
565 2
        $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
566
567 2
        return $isFinal;
568
    }
569
570
    /**
571
     * {@inheritDoc}
572
     */
573
    public function isInstance($object)
574
    {
575
        if (!is_object($object)) {
576
            throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
577
        }
578
579
        $className = $this->getName();
580
581
        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...
582
    }
583
584
    /**
585
     * {@inheritDoc}
586
     */
587 2
    public function isInstantiable()
588
    {
589 2
        if ($this->isInterface() || $this->isAbstract()) {
590 1
            return false;
591
        }
592
593 2
        if (null === ($constructor = $this->getConstructor())) {
594 2
            return true;
595
        }
596
597 1
        return $constructor->isPublic();
598
    }
599
600
    /**
601
     * {@inheritDoc}
602
     */
603 2
    public function isInterface()
604
    {
605 2
        return ($this->classLikeNode instanceof Interface_);
606
    }
607
608
    /**
609
     * {@inheritDoc}
610
     */
611 2
    public function isInternal()
612
    {
613
        // never can be an internal method
614 2
        return false;
615
    }
616
617
    /**
618
     * {@inheritDoc}
619
     */
620 2
    public function isIterateable()
621
    {
622 2
        return $this->implementsInterface('Traversable');
623
    }
624
625
    /**
626
     * {@inheritDoc}
627
     */
628
    public function isSubclassOf($class)
629
    {
630
        if (is_object($class)) {
631
            if ($class instanceof ReflectionClass) {
632
                $class = $class->name;
633
            } else {
634
                $class = get_class($class);
635
            }
636
        }
637
638
        if (!$this->classLikeNode instanceof Class_) {
639
            return false;
640
        } else {
641
            $extends = $this->classLikeNode->extends;
642
            if ($extends && $extends->toString() == $class) {
643
                return true;
644
            }
645
        }
646
647
        $parent = $this->getParentClass();
648
649
        return false === $parent ? false : $parent->isSubclassOf($class);
650
    }
651
652
    /**
653
     * {@inheritDoc}
654
     */
655 2
    public function isTrait()
656
    {
657 2
        return ($this->classLikeNode instanceof Trait_);
658
    }
659
660
    /**
661
     * {@inheritDoc}
662
     */
663 2
    public function isUserDefined()
664
    {
665
        // always defined by user, because we parse the source code
666 2
        return true;
667
    }
668
669
    /**
670
     * Gets static properties
671
     *
672
     * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
673
     *
674
     * @return array
675
     */
676 3
    public function getStaticProperties()
677
    {
678
        // In runtime static properties can be changed in any time
679 3
        if ($this->isInitialized()) {
680 1
            return forward_static_call('parent::getStaticProperties');
681
        }
682
683 2
        $properties = [];
684
685 2
        $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
686 2
        foreach ($reflectionProperties as $reflectionProperty) {
687 1
            if (!$reflectionProperty instanceof ReflectionProperty) {
688 1
                if (!$reflectionProperty->isPublic()) {
689 1
                    $reflectionProperty->setAccessible(true);
690 1
                }
691 1
            }
692 1
            $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
693 2
        }
694
695 2
        return $properties;
696
    }
697
698
    /**
699
     * Gets static property value
700
     *
701
     * @param string $name    The name of the static property for which to return a value.
702
     * @param mixed  $default A default value to return in case the class does not declare
703
     *                        a static property with the given name
704
     *
705
     * @return mixed
706
     * @throws ReflectionException If there is no such property and no default value was given
707
     */
708 1
    public function getStaticPropertyValue($name, $default = null)
709
    {
710 1
        $properties     = $this->getStaticProperties();
711 1
        $propertyExists = array_key_exists($name, $properties);
712
713 1
        if (!$propertyExists && func_num_args() === 1) {
714
            throw new ReflectionException("Static property does not exist and no default value is given");
715
        }
716
717 1
        return $propertyExists ? $properties[$name] : $default;
718
    }
719
720
721
    /**
722
     * Creates a new class instance from given arguments.
723
     *
724
     * @link http://php.net/manual/en/reflectionclass.newinstance.php
725
     * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
726
     *
727
     * @return object
728
     */
729 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...
730
    {
731 1
        $this->initializeInternalReflection();
732
733 1
        return call_user_func_array('parent::newInstance', func_get_args());
734
    }
735
736
    /**
737
     * Creates a new class instance from given arguments.
738
     *
739
     * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
740
     *
741
     * @param array $args The parameters to be passed to the class constructor as an array.
742
     *
743
     * @return object
744
     */
745 1
    public function newInstanceArgs(array $args = [])
746
    {
747 1
        $function = __FUNCTION__;
748 1
        $this->initializeInternalReflection();
749
750 1
        return parent::$function($args);
751
    }
752
753
    /**
754
     * Creates a new class instance without invoking the constructor.
755
     *
756
     * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
757
     *
758
     * @return object
759
     */
760 1
    public function newInstanceWithoutConstructor($args = null)
761
    {
762 1
        $function = __FUNCTION__;
763 1
        $this->initializeInternalReflection();
764
765 1
        return parent::$function($args);
766
    }
767
768
    /**
769
     * Sets static property value
770
     *
771
     * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
772
     *
773
     * @param string $name Property name
774
     * @param mixed $value New property value
775
     */
776 1
    public function setStaticPropertyValue($name, $value)
777
    {
778 1
        $this->initializeInternalReflection();
779
780 1
        forward_static_call('parent::setStaticPropertyValue', $name, $value);
781 1
    }
782
783 9
    private function recursiveCollect(\Closure $collector)
784
    {
785 9
        $result = array();
786
787 9
        $parentClass = $this->getParentClass();
788 9
        if ($parentClass) {
789 3
            $collector($result, $parentClass);
790 3
        }
791
792 9
        $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
793 9
        foreach ($interfaces as $interface) {
794 1
            $collector($result, $interface);
795 9
        }
796
797 9
        return $result;
798
    }
799
800
    /**
801
     * Returns list of constants from the class
802
     *
803
     * @return array
804
     */
805 3
    private function findConstants()
806
    {
807 3
        $constants        = array();
808 3
        $expressionSolver = new NodeExpressionResolver($this);
809
810
        // constants can be only top-level nodes in the class, so we can scan them directly
811 3
        foreach ($this->classLikeNode->stmts as $classLevelNode) {
812 3
            if ($classLevelNode instanceof ClassConst) {
813 3
                $nodeConstants = $classLevelNode->consts;
814 3
                if (!empty($nodeConstants)) {
815 3
                    foreach ($nodeConstants as $nodeConstant) {
816 3
                        $expressionSolver->process($nodeConstant->value);
817 3
                        $constants[$nodeConstant->name] = $expressionSolver->getValue();
818 3
                    }
819 3
                }
820 3
            }
821 3
        }
822
823 3
        return $constants;
824
    }
825
}
826