Completed
Push — master ( 793727...6af734 )
by Alexander
02:36
created

ReflectionClassLikeTrait::getReflectionConstant()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
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\ReflectionClassConstant;
15
use Go\ParserReflection\ReflectionException;
16
use Go\ParserReflection\ReflectionMethod;
17
use Go\ParserReflection\ReflectionProperty;
18
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
19
use PhpParser\Node\Name\FullyQualified;
20
use PhpParser\Node\Stmt\Class_;
21
use PhpParser\Node\Stmt\ClassConst;
22
use PhpParser\Node\Stmt\ClassLike;
23
use PhpParser\Node\Stmt\Interface_;
24
use PhpParser\Node\Stmt\Trait_;
25
use PhpParser\Node\Stmt\TraitUseAdaptation;
26
27
/**
28
 * General class-like reflection
29
 */
30
trait ReflectionClassLikeTrait
31
{
32
    use InitializationTrait;
33
34
    /**
35
     * @var ClassLike
36
     */
37
    protected $classLikeNode;
38
39
    /**
40
     * Short name of the class, without namespace
41
     *
42
     * @var string
43
     */
44
    protected $className;
45
46
    /**
47
     * List of all constants from the class
48
     *
49
     * @var array
50
     */
51
    protected $constants;
52
53
    /**
54
     * Interfaces, empty array or null if not initialized yet
55
     *
56
     * @var \ReflectionClass[]|array|null
57
     */
58
    protected $interfaceClasses;
59
60
    /**
61
     * List of traits, empty array or null if not initialized yet
62
     *
63
     * @var  \ReflectionClass[]|array|null
64
     */
65
    protected $traits;
66
67
    /**
68
     * Additional list of trait adaptations
69
     *
70
     * @var TraitUseAdaptation[]|array
71
     */
72
    protected $traitAdaptations;
73
74
    /**
75
     * @var array|ReflectionMethod[]
76
     */
77
    protected $methods;
78
79
    /**
80
     * Namespace name
81
     *
82
     * @var string
83
     */
84
    protected $namespaceName = '';
85
86
    /**
87
     * Parent class, or false if not present, null if uninitialized yet
88
     *
89
     * @var \ReflectionClass|false|null
90
     */
91
    protected $parentClass;
92
93
    /**
94
     * @var array|ReflectionProperty[]
95
     */
96
    protected $properties;
97
98
    /**
99
     * @var array|ReflectionClassConstant[]
100
     */
101
    protected $classConstants;
102
103
    /**
104
     * Returns the string representation of the ReflectionClass object.
105
     *
106
     * @return string
107
     */
108
    public function __toString()
109
    {
110
        $isObject = $this instanceof \ReflectionObject;
111
112
        $staticProperties = $staticMethods = $defaultProperties = $dynamicProperties = $methods = [];
113
114
        $format  = "%s [ <user> %sclass %s%s%s ] {\n";
115
        $format .= "  @@ %s %d-%d\n\n";
116
        $format .= "  - Constants [%d] {%s\n  }\n\n";
117
        $format .= "  - Static properties [%d] {%s\n  }\n\n";
118
        $format .= "  - Static methods [%d] {%s\n  }\n\n";
119
        $format .= "  - Properties [%d] {%s\n  }\n\n";
120
        $format .= ($isObject ? "  - Dynamic properties [%d] {%s\n  }\n\n" : '%s%s');
121
        $format .= "  - Methods [%d] {%s\n  }\n";
122
        $format .= "}\n";
123
124
        foreach ($this->getProperties() as $property) {
125
            if ($property->isStatic()) {
126
                $staticProperties[] = $property;
127
            } elseif ($property->isDefault()) {
128
                $defaultProperties[] = $property;
129
            } else {
130
                $dynamicProperties[] = $property;
131
            }
132
        }
133
134
        foreach ($this->getMethods() as $method) {
135
            if ($method->isStatic()) {
136
                $staticMethods[] = $method;
137
            } else {
138
                $methods[] = $method;
139
            }
140
        }
141
142
        $buildString = function (array $items, $indentLevel = 4) {
143
            if (!count($items)) {
144
                return '';
145
            }
146
            $indent = "\n" . str_repeat(' ', $indentLevel);
147
            return $indent . implode($indent, explode("\n", implode("\n", $items)));
148
        };
149
        $buildConstants = function (array $items, $indentLevel = 4) {
150
            $str = '';
151
            foreach ($items as $name => $value) {
152
                $str .= "\n" . str_repeat(' ', $indentLevel);
153
                $str .= sprintf(
154
                    'Constant [ %s %s ] { %s }',
155
                    gettype($value),
156
                    $name,
157
                    $value
158
                );
159
            }
160
            return $str;
161
        };
162
        $interfaceNames = $this->getInterfaceNames();
163
        $parentClass    = $this->getParentClass();
164
        $modifiers      = '';
165
        if ($this->isAbstract()) {
166
            $modifiers = 'abstract ';
167
        } elseif ($this->isFinal()) {
168
            $modifiers = 'final ';
169
        };
170
171
        $string = sprintf(
172
            $format,
173
            ($isObject ? 'Object of class' : 'Class'),
174
            $modifiers,
175
            $this->getName(),
176
            false !== $parentClass ? (' extends ' . $parentClass->getName()) : '',
177
            $interfaceNames ? (' implements ' . implode(', ', $interfaceNames)) : '',
178
            $this->getFileName(),
179
            $this->getStartLine(),
180
            $this->getEndLine(),
181
            count($this->getConstants()),
182
            $buildConstants($this->getConstants()),
183
            count($staticProperties),
184
            $buildString($staticProperties),
185
            count($staticMethods),
186
            $buildString($staticMethods),
187
            count($defaultProperties),
188
            $buildString($defaultProperties),
189
            $isObject ? count($dynamicProperties) : '',
190
            $isObject ? $buildString($dynamicProperties) : '',
191
            count($methods),
192
            $buildString($methods)
193
        );
194
195
        return $string;
196
    }
197
198
199
    /**
200
     * {@inheritDoc}
201
     */
202 10
    public function getConstant($name)
203
    {
204 10
        if ($this->hasConstant($name)) {
205 10
            return $this->constants[$name];
206
        }
207
208
        return false;
209
    }
210
211
    /**
212
     * {@inheritDoc}
213
     */
214 38
    public function getConstants()
215
    {
216 38
        if (!isset($this->constants)) {
217
            $this->constants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
218 11
                $result += $instance->getConstants();
219 38
            });
220 38
            $this->collectSelfConstants();
221
        }
222
223 38
        return $this->constants;
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229 19
    public function getConstructor()
230
    {
231 19
        $constructor = $this->getMethod('__construct');
232 19
        if (!$constructor) {
233 17
            return null;
234
        }
235
236 2
        return $constructor;
237
    }
238
239
    /**
240
     * Gets default properties from a class (including inherited properties).
241
     *
242
     * @link http://php.net/manual/en/reflectionclass.getdefaultproperties.php
243
     *
244
     * @return array An array of default properties, with the key being the name of the property and the value being
245
     * the default value of the property or NULL if the property doesn't have a default value
246
     */
247 29
    public function getDefaultProperties()
248
    {
249 29
        $defaultValues = [];
250 29
        $properties    = $this->getProperties();
251 29
        $staticOrder   = [true, false];
252 29
        foreach ($staticOrder as $shouldBeStatic) {
253 29
            foreach ($properties as $property) {
254 7
                $isStaticProperty     = $property->isStatic();
255 7
                if ($shouldBeStatic !== $isStaticProperty) {
256 7
                    continue;
257
                }
258 7
                $propertyName         = $property->getName();
259 7
                $isInternalReflection = get_class($property) == \ReflectionProperty::class;
260
261 7
                if (!$isInternalReflection || $isStaticProperty) {
262 7
                    $defaultValues[$propertyName] = $property->getValue();
263
                } elseif (!$isStaticProperty) {
264
                    // Internal reflection and dynamic property
265
                    $classProperties = $property->getDeclaringClass()->getDefaultProperties();
266 29
                    $defaultValues[$propertyName] = $classProperties[$propertyName];
267
                }
268
            }
269
        }
270
271 29
        return $defaultValues;
272
    }
273
274
    /**
275
     * {@inheritDoc}
276
     */
277 29
    public function getDocComment()
278
    {
279 29
        $docComment = $this->classLikeNode->getDocComment();
280
281 29
        return $docComment ? $docComment->getText() : false;
282
    }
283
284 29
    public function getEndLine()
285
    {
286 29
        return $this->classLikeNode->getAttribute('endLine');
287
    }
288
289 29
    public function getExtension()
290
    {
291 29
        return null;
292
    }
293
294 29
    public function getExtensionName()
295
    {
296 29
        return false;
297
    }
298
299 21
    public function getFileName()
300
    {
301 21
        return $this->classLikeNode->getAttribute('fileName');
302
    }
303
304
    /**
305
     * {@inheritDoc}
306
     */
307 30
    public function getInterfaceNames()
308
    {
309 30
        return array_keys($this->getInterfaces());
310
    }
311
312
    /**
313
     * {@inheritDoc}
314
     */
315 59
    public function getInterfaces()
316
    {
317 59
        if (!isset($this->interfaceClasses)) {
318
            $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) {
319 7
                if ($instance->isInterface()) {
320 1
                    $result[$instance->name] = $instance;
321
                }
322 7
                $result += $instance->getInterfaces();
323 30
            });
324
        }
325
326 59
        return $this->interfaceClasses;
327
    }
328
329
    /**
330
     * {@inheritdoc}
331
     * @param string $name
332
     */
333 2047
    public function getMethod($name)
334
    {
335 2047
        $methods = $this->getMethods();
336 2047
        foreach ($methods as $method) {
337 2038
            if ($method->getName() == $name) {
338 2038
                return $method;
339
            }
340
        }
341
342 17
        return false;
343
    }
344
345
    /**
346
     * Returns list of reflection methods
347
     *
348
     * @param null|integer $filter Optional filter
349
     *
350
     * @return array|\ReflectionMethod[]
351
     */
352 2077
    public function getMethods($filter = null)
353
    {
354 2077
        if (!isset($this->methods)) {
355 60
            $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this);
356
            $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
357 18
                $reflectionMethods = [];
358 18
                foreach ($instance->getMethods() as $reflectionMethod) {
359 12
                    if (!$isParent || !$reflectionMethod->isPrivate()) {
360 12
                        $reflectionMethods[$reflectionMethod->name] = $reflectionMethod;
361
                    }
362
                }
363 18
                $result += $reflectionMethods;
364 60
            });
365 60
            $methods = $directMethods + $parentMethods;
366
367 60
            $this->methods = $methods;
368
        }
369 2077
        if (!isset($filter)) {
370 2073
            return array_values($this->methods);
371
        }
372
373 5
        $methods = [];
374 5
        foreach ($this->methods as $method) {
375 4
            if (!($filter & $method->getModifiers())) {
376 4
                continue;
377
            }
378 2
            $methods[] = $method;
379
        }
380
381 5
        return $methods;
382
    }
383
384
    /**
385
     * Returns a bitfield of the access modifiers for this class.
386
     *
387
     * @link http://php.net/manual/en/reflectionclass.getmodifiers.php
388
     *
389
     * NB: this method is not fully compatible with original value because of hidden internal constants
390
     *
391
     * @return int
392
     */
393 4
    public function getModifiers()
394
    {
395 4
        $modifiers = 0;
396
397 4
        if ($this->isFinal()) {
398 1
            $modifiers += \ReflectionClass::IS_FINAL;
399
        }
400
401 4
        if (PHP_VERSION_ID < 70000 && $this->isTrait()) {
402
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
403
        }
404
405 4
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
406 1
            $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT;
407
        }
408
409 4
        if ($this->isInterface()) {
410 1
            $abstractMethods = $this->getMethods();
411
        } else {
412 4
            $abstractMethods = $this->getMethods(\ReflectionMethod::IS_ABSTRACT);
413
        }
414 4
        if (!empty($abstractMethods)) {
415 1
            $modifiers += \ReflectionClass::IS_IMPLICIT_ABSTRACT;
416
        }
417
418 4
        return $modifiers;
419
    }
420
421
    /**
422
     * {@inheritDoc}
423
     */
424 3004
    public function getName()
425
    {
426 3004
        $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : '';
427
428 3004
        return $namespaceName . $this->getShortName();
429
    }
430
431
    /**
432
     * {@inheritDoc}
433
     */
434 50
    public function getNamespaceName()
435
    {
436 50
        return $this->namespaceName;
437
    }
438
439
    /**
440
     * {@inheritDoc}
441
     */
442 238
    public function getParentClass()
443
    {
444 238
        if (!isset($this->parentClass)) {
445 97
            static $extendsField = 'extends';
446
447 97
            $parentClass = false;
448 97
            $hasExtends  = in_array($extendsField, $this->classLikeNode->getSubNodeNames());
449 97
            $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null;
450 97
            if ($extendsNode instanceof FullyQualified) {
451 26
                $extendsName = $extendsNode->toString();
452 26
                $parentClass = $this->createReflectionForClass($extendsName);
453
            }
454 97
            $this->parentClass = $parentClass;
455
        }
456
457 238
        return $this->parentClass;
458
    }
459
460
    /**
461
     * Retrieves reflected properties.
462
     *
463
     * @param int $filter The optional filter, for filtering desired property types.
464
     *                    It's configured using the ReflectionProperty constants, and defaults to all property types.
465
     *
466
     * @return array|\Go\ParserReflection\ReflectionProperty[]
467
     */
468 318
    public function getProperties($filter = null)
469
    {
470 318
        if (!isset($this->properties)) {
471 44
            $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName());
472
            $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
473 10
                $reflectionProperties = [];
474 10
                foreach ($instance->getProperties() as $reflectionProperty) {
475 4
                    if (!$isParent || !$reflectionProperty->isPrivate()) {
476 4
                        $reflectionProperties[$reflectionProperty->name] = $reflectionProperty;
477
                    }
478
                }
479 10
                $result += $reflectionProperties;
480 44
            });
481 44
            $properties = $directProperties + $parentProperties;
482
483 44
            $this->properties = $properties;
484
        }
485
486
        // Without filter we can just return the full list
487 318
        if (!isset($filter)) {
488 288
            return array_values($this->properties);
489
        }
490
491 30
        $properties = [];
492 30
        foreach ($this->properties as $property) {
493 8
            if (!($filter & $property->getModifiers())) {
494 5
                continue;
495
            }
496 6
            $properties[] = $property;
497
        }
498
499 30
        return $properties;
500
    }
501
502
    /**
503
     * {@inheritdoc}
504
     */
505 255
    public function getProperty($name)
506
    {
507 255
        $properties = $this->getProperties();
508 255
        foreach ($properties as $property) {
509 255
            if ($property->getName() == $name) {
510 255
                return $property;
511
            }
512
        }
513
514
        return false;
515
    }
516
517
    /**
518
     * @inheritDoc
519
     */
520 2
    public function getReflectionConstant($name)
521
    {
522 2
        $classConstants = $this->getReflectionConstants();
523 2
        foreach ($classConstants as $classConstant) {
524 2
            if ($classConstant->getName() == $name) {
525 2
                return $classConstant;
526
            }
527
        }
528
529 1
        return false;
530
    }
531
532
    /**
533
     * @inheritDoc
534
     */
535 7
    public function getReflectionConstants()
536
    {
537 7
        if (!isset($this->classConstants)) {
538 7
            $directClassConstants = ReflectionClassConstant::collectFromClassNode($this->classLikeNode, $this->getName());
539 7
            $parentClassConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
540 2
                $reflectionClassConstants = [];
541 2
                foreach ($instance->getReflectionConstants() as $reflectionClassConstant) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ReflectionClass as the method getReflectionConstants() does only exist in the following sub-classes of ReflectionClass: Go\ParserReflection\ReflectionClass. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
542 2
                    if (!$isParent || !$reflectionClassConstant->isPrivate()) {
543 2
                        $reflectionClassConstants[$reflectionClassConstant->name] = $reflectionClassConstant;
544
                    }
545
                }
546 2
                $result += $reflectionClassConstants;
547 7
            });
548 7
            $classConstants = $directClassConstants + $parentClassConstants;
549
550 7
            $this->classConstants = $classConstants;
551
        }
552
553 7
        return array_values($this->classConstants);
554
    }
555
556
    /**
557
     * {@inheritDoc}
558
     */
559 3004
    public function getShortName()
560
    {
561 3004
        return $this->className;
562
    }
563
564 29
    public function getStartLine()
565
    {
566 29
        return $this->classLikeNode->getAttribute('startLine');
567
    }
568
569
    /**
570
     * Returns an array of trait aliases
571
     *
572
     * @link http://php.net/manual/en/reflectionclass.gettraitaliases.php
573
     *
574
     * @return array|null an array with new method names in keys and original names (in the format "TraitName::original") in
575
     * values.
576
     */
577 33
    public function getTraitAliases()
578
    {
579 33
        $aliases = [];
580 33
        $traits  = $this->getTraits();
581 33
        foreach ($this->traitAdaptations as $adaptation) {
582
            if ($adaptation instanceof TraitUseAdaptation\Alias) {
583
                $methodName = $adaptation->method;
584
                $traitName  = null;
585
                foreach ($traits as $trait) {
586
                    if ($trait->hasMethod($methodName)) {
587
                        $traitName = $trait->getName();
588
                        break;
589
                    }
590
                }
591
                $aliases[$adaptation->newName] = $traitName . '::'. $methodName;
592
            }
593
        }
594
595 33
        return $aliases;
596
    }
597
598
    /**
599
     * Returns an array of names of traits used by this class
600
     *
601
     * @link http://php.net/manual/en/reflectionclass.gettraitnames.php
602
     *
603
     * @return array
604
     */
605 29
    public function getTraitNames()
606
    {
607 29
        return array_keys($this->getTraits());
608
    }
609
610
    /**
611
     * Returns an array of traits used by this class
612
     *
613
     * @link http://php.net/manual/en/reflectionclass.gettraits.php
614
     *
615
     * @return array|\ReflectionClass[]
616
     */
617 233
    public function getTraits()
618
    {
619 233
        if (!isset($this->traits)) {
620 97
            $traitAdaptations = [];
621 97
            $this->traits     = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode, $traitAdaptations);
622 97
            $this->traitAdaptations = $traitAdaptations;
623
        }
624
625 233
        return $this->traits;
626
    }
627
628
    /**
629
     * {@inheritDoc}
630
     */
631 11
    public function hasConstant($name)
632
    {
633 11
        $constants   = $this->getConstants();
634 11
        $hasConstant = isset($constants[$name]) || array_key_exists($name, $constants);
635
636 11
        return $hasConstant;
637
    }
638
639
    /**
640
     * {@inheritdoc}
641
     * @param string $name
642
     */
643 20
    public function hasMethod($name)
644
    {
645 20
        $methods = $this->getMethods();
646 20
        foreach ($methods as $method) {
647 11
            if ($method->getName() == $name) {
648 11
                return true;
649
            }
650
        }
651
652 18
        return false;
653
    }
654
655
    /**
656
     * {@inheritdoc}
657
     */
658
    public function hasProperty($name)
659
    {
660
        $properties = $this->getProperties();
661
        foreach ($properties as $property) {
662
            if ($property->getName() == $name) {
663
                return true;
664
            }
665
        }
666
667
        return false;
668
    }
669
670
    /**
671
     * {@inheritDoc}
672
     * @param string $interfaceName
673
     */
674 29
    public function implementsInterface($interfaceName)
675
    {
676 29
        $allInterfaces = $this->getInterfaces();
677
678 29
        return isset($allInterfaces[$interfaceName]);
679
    }
680
681
    /**
682
     * {@inheritDoc}
683
     */
684 29
    public function inNamespace()
685
    {
686 29
        return !empty($this->namespaceName);
687
    }
688
689
    /**
690
     * {@inheritDoc}
691
     */
692 75
    public function isAbstract()
693
    {
694 75
        if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) {
695 12
            return true;
696 63
        } elseif ($this->isInterface() && !empty($this->getMethods())) {
697 2
            return true;
698 61
        } elseif ($this->isTrait()) {
699 3
            return PHP_VERSION_ID < 70000 ? true : false;
700
        }
701
702 58
        return false;
703
    }
704
705
    /**
706
     * Currently, anonymous classes aren't supported for parsed reflection
707
     */
708
    public function isAnonymous()
709
    {
710
        return false;
711
    }
712
713
    /**
714
     * {@inheritDoc}
715
     */
716 29
    public function isCloneable()
717
    {
718 29
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
719 10
            return false;
720
        }
721
722 19
        if ($this->hasMethod('__clone')) {
723 1
            return $this->getMethod('__clone')->isPublic();
724
        }
725
726 18
        return true;
727
    }
728
729
    /**
730
     * {@inheritDoc}
731
     */
732 33
    public function isFinal()
733
    {
734 33
        $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal();
735
736 33
        return $isFinal;
737
    }
738
739
    /**
740
     * {@inheritDoc}
741
     */
742
    public function isInstance($object)
743
    {
744
        if (!is_object($object)) {
745
            throw new \RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)));
746
        }
747
748
        $className = $this->getName();
749
        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...
750
    }
751
752
    /**
753
     * {@inheritDoc}
754
     */
755 29
    public function isInstantiable()
756
    {
757 29
        if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) {
758 10
            return false;
759
        }
760
761 19
        if (null === ($constructor = $this->getConstructor())) {
762 17
            return true;
763
        }
764
765 2
        return $constructor->isPublic();
766
    }
767
768
    /**
769
     * {@inheritDoc}
770
     */
771 246
    public function isInterface()
772
    {
773 246
        return ($this->classLikeNode instanceof Interface_);
774
    }
775
776
    /**
777
     * {@inheritDoc}
778
     */
779 29
    public function isInternal()
780
    {
781
        // never can be an internal method
782 29
        return false;
783
    }
784
785
    /**
786
     * {@inheritDoc}
787
     */
788 29
    public function isIterateable()
789
    {
790 29
        return $this->implementsInterface('Traversable');
791
    }
792
793
    /**
794
     * {@inheritDoc}
795
     */
796
    public function isSubclassOf($class)
797
    {
798
        if (is_object($class)) {
799
            if ($class instanceof ReflectionClass) {
800
                $class = $class->name;
801
            } else {
802
                $class = get_class($class);
803
            }
804
        }
805
806
        if (!$this->classLikeNode instanceof Class_) {
807
            return false;
808
        } else {
809
            $extends = $this->classLikeNode->extends;
810
            if ($extends && $extends->toString() == $class) {
811
                return true;
812
            }
813
        }
814
815
        $parent = $this->getParentClass();
816
817
        return false === $parent ? false : $parent->isSubclassOf($class);
818
    }
819
820
    /**
821
     * {@inheritDoc}
822
     */
823 106
    public function isTrait()
824
    {
825 106
        return ($this->classLikeNode instanceof Trait_);
826
    }
827
828
    /**
829
     * {@inheritDoc}
830
     */
831 29
    public function isUserDefined()
832
    {
833
        // always defined by user, because we parse the source code
834 29
        return true;
835
    }
836
837
    /**
838
     * Gets static properties
839
     *
840
     * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php
841
     *
842
     * @return array
843
     */
844 30
    public function getStaticProperties()
845
    {
846
        // In runtime static properties can be changed in any time
847 30
        if ($this->isInitialized()) {
848 1
            return parent::getStaticProperties();
849
        }
850
851 30
        $properties = [];
852
853 30
        $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC);
854 30
        foreach ($reflectionProperties as $reflectionProperty) {
855 6
            if (!$reflectionProperty instanceof ReflectionProperty) {
856 2
                if (!$reflectionProperty->isPublic()) {
857 2
                    $reflectionProperty->setAccessible(true);
858
                }
859
            }
860 6
            $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue();
861
        }
862
863 30
        return $properties;
864
    }
865
866
    /**
867
     * Gets static property value
868
     *
869
     * @param string $name    The name of the static property for which to return a value.
870
     * @param mixed  $default A default value to return in case the class does not declare
871
     *                        a static property with the given name
872
     *
873
     * @return mixed
874
     * @throws ReflectionException If there is no such property and no default value was given
875
     */
876 1
    public function getStaticPropertyValue($name, $default = null)
877
    {
878 1
        $properties     = $this->getStaticProperties();
879 1
        $propertyExists = array_key_exists($name, $properties);
880
881 1
        if (!$propertyExists && func_num_args() === 1) {
882
            throw new ReflectionException("Static property does not exist and no default value is given");
883
        }
884
885 1
        return $propertyExists ? $properties[$name] : $default;
886
    }
887
888
889
    /**
890
     * Creates a new class instance from given arguments.
891
     *
892
     * @link http://php.net/manual/en/reflectionclass.newinstance.php
893
     *
894
     * Signature was hacked to support both 5.6, 7.1.x and 7.2.0 versions
895
     * @see https://3v4l.org/hW9O9
896
     * @see https://3v4l.org/sWT3j
897
     * @see https://3v4l.org/eeVf8
898
     *
899
     * @param mixed $arg First argument
900
     * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor
901
     *
902
     * @return object
903
     */
904 1
    public function newInstance($arg = null, ...$args)
905
    {
906 1
        $args = array_slice(array_merge([$arg], $args), 0, \func_num_args());
907 1
        $this->initializeInternalReflection();
908
909 1
        return parent::newInstance(...$args);
910
    }
911
912
    /**
913
     * Creates a new class instance from given arguments.
914
     *
915
     * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php
916
     *
917
     * @param array $args The parameters to be passed to the class constructor as an array.
918
     *
919
     * @return object
920
     */
921 1
    public function newInstanceArgs(array $args = [])
922
    {
923 1
        $function = __FUNCTION__;
924 1
        $this->initializeInternalReflection();
925
926 1
        return parent::$function($args);
927
    }
928
929
    /**
930
     * Creates a new class instance without invoking the constructor.
931
     *
932
     * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php
933
     *
934
     * @return object
935
     */
936 1
    public function newInstanceWithoutConstructor($args = null)
937
    {
938 1
        $function = __FUNCTION__;
939 1
        $this->initializeInternalReflection();
940
941 1
        return parent::$function($args);
942
    }
943
944
    /**
945
     * Sets static property value
946
     *
947
     * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php
948
     *
949
     * @param string $name Property name
950
     * @param mixed $value New property value
951
     */
952 1
    public function setStaticPropertyValue($name, $value)
953
    {
954 1
        $this->initializeInternalReflection();
955
956 1
        parent::setStaticPropertyValue($name, $value);
957 1
    }
958
959 175
    private function recursiveCollect(\Closure $collector)
960
    {
961 175
        $result   = [];
962 175
        $isParent = true;
963
964 175
        $traits = $this->getTraits();
965 175
        foreach ($traits as $trait) {
966 9
            $collector($result, $trait, !$isParent);
967
        }
968
969 175
        $parentClass = $this->getParentClass();
970 175
        if ($parentClass) {
971 37
            $collector($result, $parentClass, $isParent);
972
        }
973
974 175
        $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode);
975 175
        foreach ($interfaces as $interface) {
976 8
            $collector($result, $interface, $isParent);
977
        }
978
979 175
        return $result;
980
    }
981
982
    /**
983
     * Collects list of constants from the class itself
984
     */
985 38
    private function collectSelfConstants()
986
    {
987 38
        $expressionSolver = new NodeExpressionResolver($this);
988 38
        $localConstants   = [];
989
990
        // constants can be only top-level nodes in the class, so we can scan them directly
991 38
        foreach ($this->classLikeNode->stmts as $classLevelNode) {
992 34
            if ($classLevelNode instanceof ClassConst) {
993 17
                $nodeConstants = $classLevelNode->consts;
994 17
                if (!empty($nodeConstants)) {
995 17
                    foreach ($nodeConstants as $nodeConstant) {
996 17
                        $expressionSolver->process($nodeConstant->value);
997 17
                        $localConstants[$nodeConstant->name->toString()] = $expressionSolver->getValue();
998 34
                        $this->constants = $localConstants + $this->constants;
999
                    }
1000
                }
1001
            }
1002
        }
1003 38
    }
1004
1005
    /**
1006
     * Create a ReflectionClass for a given class name.
1007
     *
1008
     * @param string $className
1009
     *     The name of the class to create a reflection for.
1010
     *
1011
     * @return ReflectionClass
1012
     *     The apropriate reflection object.
1013
     */
1014
    abstract protected function createReflectionForClass($className);
1015
}
1016