These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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 | 9 | public function getConstant($name) |
|
197 | { |
||
198 | 9 | if ($this->hasConstant($name)) { |
|
199 | 9 | return $this->constants[$name]; |
|
200 | } |
||
201 | |||
202 | return false; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * {@inheritDoc} |
||
207 | */ |
||
208 | 37 | public function getConstants() |
|
209 | { |
||
210 | 37 | if (!isset($this->constants)) { |
|
211 | $this->constants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) { |
||
212 | 10 | $result += $instance->getConstants(); |
|
213 | 37 | }); |
|
214 | 37 | $this->collectSelfConstants(); |
|
215 | } |
||
216 | |||
217 | 37 | return $this->constants; |
|
218 | } |
||
219 | |||
220 | /** |
||
221 | * {@inheritDoc} |
||
222 | */ |
||
223 | 19 | public function getConstructor() |
|
224 | { |
||
225 | 19 | $constructor = $this->getMethod('__construct'); |
|
226 | 19 | if (!$constructor) { |
|
227 | 17 | 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 | 29 | public function getDefaultProperties() |
|
242 | { |
||
243 | 29 | $defaultValues = []; |
|
244 | 29 | $properties = $this->getProperties(); |
|
245 | 29 | $staticOrder = [true, false]; |
|
246 | 29 | foreach ($staticOrder as $shouldBeStatic) { |
|
247 | 29 | 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 | } elseif (!$isStaticProperty) { |
||
258 | // Internal reflection and dynamic property |
||
259 | $classProperties = $property->getDeclaringClass()->getDefaultProperties(); |
||
260 | 29 | $defaultValues[$propertyName] = $classProperties[$propertyName]; |
|
261 | } |
||
262 | } |
||
263 | } |
||
264 | |||
265 | 29 | return $defaultValues; |
|
266 | } |
||
267 | |||
268 | /** |
||
269 | * {@inheritDoc} |
||
270 | */ |
||
271 | 29 | public function getDocComment() |
|
272 | { |
||
273 | 29 | $docComment = $this->classLikeNode->getDocComment(); |
|
274 | |||
275 | 29 | return $docComment ? $docComment->getText() : false; |
|
276 | } |
||
277 | |||
278 | 29 | public function getEndLine() |
|
279 | { |
||
280 | 29 | return $this->classLikeNode->getAttribute('endLine'); |
|
281 | } |
||
282 | |||
283 | 29 | public function getExtension() |
|
284 | { |
||
285 | 29 | return null; |
|
286 | } |
||
287 | |||
288 | 29 | public function getExtensionName() |
|
289 | { |
||
290 | 29 | return false; |
|
291 | } |
||
292 | |||
293 | 18 | public function getFileName() |
|
294 | { |
||
295 | 18 | return $this->classLikeNode->getAttribute('fileName'); |
|
296 | } |
||
297 | |||
298 | /** |
||
299 | * {@inheritDoc} |
||
300 | */ |
||
301 | 29 | public function getInterfaceNames() |
|
302 | { |
||
303 | 29 | return array_keys($this->getInterfaces()); |
|
304 | } |
||
305 | |||
306 | /** |
||
307 | * {@inheritDoc} |
||
308 | */ |
||
309 | 58 | public function getInterfaces() |
|
310 | { |
||
311 | 58 | if (!isset($this->interfaceClasses)) { |
|
312 | $this->interfaceClasses = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance) { |
||
313 | 6 | if ($instance->isInterface()) { |
|
314 | 1 | $result[$instance->name] = $instance; |
|
315 | } |
||
316 | 6 | $result += $instance->getInterfaces(); |
|
317 | 29 | }); |
|
318 | } |
||
319 | |||
320 | 58 | return $this->interfaceClasses; |
|
321 | } |
||
322 | |||
323 | /** |
||
324 | * {@inheritdoc} |
||
325 | */ |
||
326 | 2016 | public function getMethod($name) |
|
327 | { |
||
328 | 2016 | $methods = $this->getMethods(); |
|
329 | 2016 | foreach ($methods as $method) { |
|
330 | 2007 | if ($method->getName() == $name) { |
|
331 | 2007 | return $method; |
|
332 | } |
||
333 | } |
||
334 | |||
335 | 17 | 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 | 2046 | public function getMethods($filter = null) |
|
346 | { |
||
347 | 2046 | if (!isset($this->methods)) { |
|
348 | 60 | $directMethods = ReflectionMethod::collectFromClassNode($this->classLikeNode, $this); |
|
349 | $parentMethods = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) { |
||
350 | 18 | $reflectionMethods = []; |
|
351 | 18 | foreach ($instance->getMethods() as $reflectionMethod) { |
|
352 | 12 | if (!$isParent || !$reflectionMethod->isPrivate()) { |
|
353 | 12 | $reflectionMethods[$reflectionMethod->name] = $reflectionMethod; |
|
354 | } |
||
355 | } |
||
356 | 18 | $result += $reflectionMethods; |
|
357 | 60 | }); |
|
358 | 60 | $methods = $directMethods + $parentMethods; |
|
359 | |||
360 | 60 | $this->methods = $methods; |
|
361 | } |
||
362 | 2046 | if (!isset($filter)) { |
|
363 | 2042 | return array_values($this->methods); |
|
364 | } |
||
365 | |||
366 | 5 | $methods = []; |
|
367 | 5 | foreach ($this->methods as $method) { |
|
368 | 4 | if (!($filter & $method->getModifiers())) { |
|
369 | 4 | continue; |
|
370 | } |
||
371 | 2 | $methods[] = $method; |
|
372 | } |
||
373 | |||
374 | 5 | 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 | 4 | public function getModifiers() |
|
387 | { |
||
388 | 4 | $modifiers = 0; |
|
389 | |||
390 | 4 | if ($this->isFinal()) { |
|
391 | 1 | $modifiers += \ReflectionClass::IS_FINAL; |
|
392 | } |
||
393 | |||
394 | 4 | if (PHP_VERSION_ID < 70000 && $this->isTrait()) { |
|
395 | $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT; |
||
396 | } |
||
397 | |||
398 | 4 | if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) { |
|
399 | 1 | $modifiers += \ReflectionClass::IS_EXPLICIT_ABSTRACT; |
|
400 | } |
||
401 | |||
402 | 4 | if ($this->isInterface()) { |
|
403 | 1 | $abstractMethods = $this->getMethods(); |
|
404 | } else { |
||
405 | 4 | $abstractMethods = $this->getMethods(\ReflectionMethod::IS_ABSTRACT); |
|
406 | } |
||
407 | 4 | if (!empty($abstractMethods)) { |
|
408 | 1 | $modifiers += \ReflectionClass::IS_IMPLICIT_ABSTRACT; |
|
409 | } |
||
410 | |||
411 | 4 | return $modifiers; |
|
412 | } |
||
413 | |||
414 | /** |
||
415 | * {@inheritDoc} |
||
416 | */ |
||
417 | 2966 | public function getName() |
|
418 | { |
||
419 | 2966 | $namespaceName = $this->namespaceName ? $this->namespaceName . '\\' : ''; |
|
420 | |||
421 | 2966 | return $namespaceName . $this->getShortName(); |
|
422 | } |
||
423 | |||
424 | /** |
||
425 | * {@inheritDoc} |
||
426 | */ |
||
427 | 47 | public function getNamespaceName() |
|
428 | { |
||
429 | 47 | return $this->namespaceName; |
|
430 | } |
||
431 | |||
432 | /** |
||
433 | * {@inheritDoc} |
||
434 | */ |
||
435 | 228 | public function getParentClass() |
|
436 | { |
||
437 | 228 | if (!isset($this->parentClass)) { |
|
438 | 88 | static $extendsField = 'extends'; |
|
439 | |||
440 | 88 | $parentClass = false; |
|
441 | 88 | $hasExtends = in_array($extendsField, $this->classLikeNode->getSubNodeNames()); |
|
442 | 88 | $extendsNode = $hasExtends ? $this->classLikeNode->$extendsField : null; |
|
443 | 88 | 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
|
|||
446 | } |
||
447 | 88 | $this->parentClass = $parentClass; |
|
448 | } |
||
449 | |||
450 | 228 | 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 | 317 | public function getProperties($filter = null) |
|
462 | { |
||
463 | 317 | if (!isset($this->properties)) { |
|
464 | 43 | $directProperties = ReflectionProperty::collectFromClassNode($this->classLikeNode, $this->getName()); |
|
465 | 43 | $parentProperties = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) { |
|
466 | 9 | $reflectionProperties = []; |
|
467 | 9 | foreach ($instance->getProperties() as $reflectionProperty) { |
|
468 | 3 | if (!$isParent || !$reflectionProperty->isPrivate()) { |
|
469 | 3 | $reflectionProperties[$reflectionProperty->name] = $reflectionProperty; |
|
470 | } |
||
471 | } |
||
472 | 9 | $result += $reflectionProperties; |
|
473 | 43 | }); |
|
474 | 43 | $properties = $directProperties + $parentProperties; |
|
475 | |||
476 | 43 | $this->properties = $properties; |
|
477 | } |
||
478 | |||
479 | // Without filter we can just return the full list |
||
480 | 317 | if (!isset($filter)) { |
|
481 | 288 | return array_values($this->properties); |
|
482 | } |
||
483 | |||
484 | 29 | $properties = []; |
|
485 | 29 | foreach ($this->properties as $property) { |
|
486 | 7 | if (!($filter & $property->getModifiers())) { |
|
487 | 5 | continue; |
|
488 | } |
||
489 | 5 | $properties[] = $property; |
|
490 | } |
||
491 | |||
492 | 29 | return $properties; |
|
493 | } |
||
494 | |||
495 | /** |
||
496 | * {@inheritdoc} |
||
497 | */ |
||
498 | 255 | public function getProperty($name) |
|
499 | { |
||
500 | 255 | $properties = $this->getProperties(); |
|
501 | 255 | foreach ($properties as $property) { |
|
502 | 255 | if ($property->getName() == $name) { |
|
503 | 255 | return $property; |
|
504 | } |
||
505 | } |
||
506 | |||
507 | return false; |
||
508 | } |
||
509 | |||
510 | /** |
||
511 | * {@inheritDoc} |
||
512 | */ |
||
513 | 2966 | public function getShortName() |
|
514 | { |
||
515 | 2966 | return $this->className; |
|
516 | } |
||
517 | |||
518 | 29 | public function getStartLine() |
|
519 | { |
||
520 | 29 | 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 | 33 | public function getTraitAliases() |
|
532 | { |
||
533 | 33 | $aliases = []; |
|
534 | 33 | $traits = $this->getTraits(); |
|
535 | 33 | foreach ($this->traitAdaptations as $adaptation) { |
|
536 | if ($adaptation instanceof TraitUseAdaptation\Alias) { |
||
537 | $methodName = $adaptation->method; |
||
538 | $traitName = null; |
||
539 | foreach ($traits as $trait) { |
||
540 | if ($trait->hasMethod($methodName)) { |
||
541 | $traitName = $trait->getName(); |
||
542 | break; |
||
543 | } |
||
544 | } |
||
545 | $aliases[$adaptation->newName] = $traitName . '::'. $methodName; |
||
546 | } |
||
547 | } |
||
548 | |||
549 | 33 | 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 | 29 | public function getTraitNames() |
|
560 | { |
||
561 | 29 | 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 | 224 | public function getTraits() |
|
572 | { |
||
573 | 224 | if (!isset($this->traits)) { |
|
574 | 88 | $traitAdaptations = []; |
|
575 | 88 | $this->traits = ReflectionClass::collectTraitsFromClassNode($this->classLikeNode, $traitAdaptations); |
|
576 | 88 | $this->traitAdaptations = $traitAdaptations; |
|
577 | } |
||
578 | |||
579 | 224 | return $this->traits; |
|
580 | } |
||
581 | |||
582 | /** |
||
583 | * {@inheritDoc} |
||
584 | */ |
||
585 | 10 | public function hasConstant($name) |
|
586 | { |
||
587 | 10 | $constants = $this->getConstants(); |
|
588 | 10 | $hasConstant = isset($constants[$name]) || array_key_exists($name, $constants); |
|
589 | |||
590 | 10 | return $hasConstant; |
|
591 | } |
||
592 | |||
593 | /** |
||
594 | * {@inheritdoc} |
||
595 | */ |
||
596 | 20 | public function hasMethod($name) |
|
597 | { |
||
598 | 20 | $methods = $this->getMethods(); |
|
599 | 20 | foreach ($methods as $method) { |
|
600 | 11 | if ($method->getName() == $name) { |
|
601 | 11 | return true; |
|
602 | } |
||
603 | } |
||
604 | |||
605 | 18 | 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 | 29 | public function implementsInterface($interfaceName) |
|
627 | { |
||
628 | 29 | $allInterfaces = $this->getInterfaces(); |
|
629 | |||
630 | 29 | return isset($allInterfaces[$interfaceName]); |
|
631 | } |
||
632 | |||
633 | /** |
||
634 | * {@inheritDoc} |
||
635 | */ |
||
636 | 29 | public function inNamespace() |
|
637 | { |
||
638 | 29 | return !empty($this->namespaceName); |
|
639 | } |
||
640 | |||
641 | /** |
||
642 | * {@inheritDoc} |
||
643 | */ |
||
644 | 75 | public function isAbstract() |
|
645 | { |
||
646 | 75 | if ($this->classLikeNode instanceof Class_ && $this->classLikeNode->isAbstract()) { |
|
647 | 12 | return true; |
|
648 | 63 | } elseif ($this->isInterface() && !empty($this->getMethods())) { |
|
649 | 2 | return true; |
|
650 | 61 | } elseif ($this->isTrait()) { |
|
651 | 3 | return PHP_VERSION_ID < 70000 ? true : false; |
|
652 | } |
||
653 | |||
654 | 58 | 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 | 29 | public function isCloneable() |
|
669 | { |
||
670 | 29 | if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) { |
|
671 | 10 | return false; |
|
672 | } |
||
673 | |||
674 | 19 | if ($this->hasMethod('__clone')) { |
|
675 | 1 | return $this->getMethod('__clone')->isPublic(); |
|
676 | } |
||
677 | |||
678 | 18 | return true; |
|
679 | } |
||
680 | |||
681 | /** |
||
682 | * {@inheritDoc} |
||
683 | */ |
||
684 | 33 | public function isFinal() |
|
685 | { |
||
686 | 33 | $isFinal = $this->classLikeNode instanceof Class_ && $this->classLikeNode->isFinal(); |
|
687 | |||
688 | 33 | 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
|
|||
703 | } |
||
704 | |||
705 | /** |
||
706 | * {@inheritDoc} |
||
707 | */ |
||
708 | 29 | public function isInstantiable() |
|
709 | { |
||
710 | 29 | if ($this->isInterface() || $this->isTrait() || $this->isAbstract()) { |
|
711 | 10 | return false; |
|
712 | } |
||
713 | |||
714 | 19 | if (null === ($constructor = $this->getConstructor())) { |
|
715 | 17 | return true; |
|
716 | } |
||
717 | |||
718 | 2 | return $constructor->isPublic(); |
|
719 | } |
||
720 | |||
721 | /** |
||
722 | * {@inheritDoc} |
||
723 | */ |
||
724 | 243 | public function isInterface() |
|
725 | { |
||
726 | 243 | return ($this->classLikeNode instanceof Interface_); |
|
727 | } |
||
728 | |||
729 | /** |
||
730 | * {@inheritDoc} |
||
731 | */ |
||
732 | 29 | public function isInternal() |
|
733 | { |
||
734 | // never can be an internal method |
||
735 | 29 | return false; |
|
736 | } |
||
737 | |||
738 | /** |
||
739 | * {@inheritDoc} |
||
740 | */ |
||
741 | 29 | public function isIterateable() |
|
742 | { |
||
743 | 29 | 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 | 106 | public function isTrait() |
|
777 | { |
||
778 | 106 | return ($this->classLikeNode instanceof Trait_); |
|
779 | } |
||
780 | |||
781 | /** |
||
782 | * {@inheritDoc} |
||
783 | */ |
||
784 | 29 | public function isUserDefined() |
|
785 | { |
||
786 | // always defined by user, because we parse the source code |
||
787 | 29 | 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 | 30 | public function getStaticProperties() |
|
798 | { |
||
799 | // In runtime static properties can be changed in any time |
||
800 | 30 | if ($this->isInitialized()) { |
|
801 | 1 | return forward_static_call('parent::getStaticProperties'); |
|
802 | } |
||
803 | |||
804 | 29 | $properties = []; |
|
805 | |||
806 | 29 | $reflectionProperties = $this->getProperties(ReflectionProperty::IS_STATIC); |
|
807 | 29 | foreach ($reflectionProperties as $reflectionProperty) { |
|
808 | 5 | if (!$reflectionProperty instanceof ReflectionProperty) { |
|
809 | 1 | if (!$reflectionProperty->isPublic()) { |
|
810 | 1 | $reflectionProperty->setAccessible(true); |
|
811 | } |
||
812 | } |
||
813 | 5 | $properties[$reflectionProperty->getName()] = $reflectionProperty->getValue(); |
|
814 | } |
||
815 | |||
816 | 29 | 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
|
|||
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 | 166 | private function recursiveCollect(\Closure $collector) |
|
905 | { |
||
906 | 166 | $result = []; |
|
907 | 166 | $isParent = true; |
|
908 | |||
909 | 166 | $traits = $this->getTraits(); |
|
910 | 166 | foreach ($traits as $trait) { |
|
911 | 8 | $collector($result, $trait, !$isParent); |
|
912 | } |
||
913 | |||
914 | 166 | $parentClass = $this->getParentClass(); |
|
915 | 166 | if ($parentClass) { |
|
916 | 33 | $collector($result, $parentClass, $isParent); |
|
917 | } |
||
918 | |||
919 | 166 | $interfaces = ReflectionClass::collectInterfacesFromClassNode($this->classLikeNode); |
|
920 | 166 | foreach ($interfaces as $interface) { |
|
921 | 7 | $collector($result, $interface, $isParent); |
|
922 | } |
||
923 | |||
924 | 166 | return $result; |
|
925 | } |
||
926 | |||
927 | /** |
||
928 | * Collects list of constants from the class itself |
||
929 | */ |
||
930 | 37 | private function collectSelfConstants() |
|
931 | { |
||
932 | 37 | $expressionSolver = new NodeExpressionResolver($this); |
|
933 | 37 | $localConstants = []; |
|
934 | |||
935 | // constants can be only top-level nodes in the class, so we can scan them directly |
||
936 | 37 | foreach ($this->classLikeNode->stmts as $classLevelNode) { |
|
937 | 33 | if ($classLevelNode instanceof ClassConst) { |
|
938 | 16 | $nodeConstants = $classLevelNode->consts; |
|
939 | 16 | if (!empty($nodeConstants)) { |
|
940 | 16 | foreach ($nodeConstants as $nodeConstant) { |
|
941 | 16 | $expressionSolver->process($nodeConstant->value); |
|
942 | 16 | $localConstants[$nodeConstant->name] = $expressionSolver->getValue(); |
|
943 | 33 | $this->constants = $localConstants + $this->constants; |
|
944 | } |
||
945 | } |
||
946 | } |
||
947 | } |
||
948 | 37 | } |
|
949 | } |
||
950 |
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.