| 1: | <?php | 
| 2: |  | 
| 3: | declare(strict_types=1); | 
| 4: |  | 
| 5: | namespace PHPStan\BetterReflection\Reflection; | 
| 6: |  | 
| 7: | use InvalidArgumentException; | 
| 8: | use PhpParser\Builder\Property as PropertyNodeBuilder; | 
| 9: | use PhpParser\Node\Stmt\Property as PropertyNode; | 
| 10: | use ReflectionException; | 
| 11: | use ReflectionObject as CoreReflectionObject; | 
| 12: | use ReflectionProperty as CoreReflectionProperty; | 
| 13: | use PHPStan\BetterReflection\BetterReflection; | 
| 14: | use PHPStan\BetterReflection\Reflection\Adapter\ReflectionProperty as ReflectionPropertyAdapter; | 
| 15: | use PHPStan\BetterReflection\Reflector\DefaultReflector; | 
| 16: | use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound; | 
| 17: | use PHPStan\BetterReflection\Reflector\Reflector; | 
| 18: | use PHPStan\BetterReflection\SourceLocator\Located\LocatedSource; | 
| 19: | use PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator; | 
| 20: | use PHPStan\BetterReflection\SourceLocator\Type\AnonymousClassObjectSourceLocator; | 
| 21: |  | 
| 22: | use function array_filter; | 
| 23: | use function array_map; | 
| 24: | use function array_merge; | 
| 25: | use function preg_match; | 
| 26: |  | 
| 27: |  | 
| 28: | class ReflectionObject extends ReflectionClass | 
| 29: | { | 
| 30: |  | 
| 31: |  | 
| 32: |  | 
| 33: | private $reflector; | 
| 34: |  | 
| 35: |  | 
| 36: |  | 
| 37: | private $reflectionClass; | 
| 38: |  | 
| 39: |  | 
| 40: |  | 
| 41: | private $object; | 
| 42: | protected function __construct(Reflector $reflector, ReflectionClass $reflectionClass, object $object) | 
| 43: | { | 
| 44: | $this->reflector = $reflector; | 
| 45: | $this->reflectionClass = $reflectionClass; | 
| 46: | $this->object = $object; | 
| 47: | } | 
| 48: |  | 
| 49: |  | 
| 50: |  | 
| 51: |  | 
| 52: |  | 
| 53: |  | 
| 54: |  | 
| 55: | public static function createFromInstance(object $instance): ReflectionClass | 
| 56: | { | 
| 57: | $className = get_class($instance); | 
| 58: |  | 
| 59: | $betterReflection = new BetterReflection(); | 
| 60: |  | 
| 61: | if (preg_match(ReflectionClass::ANONYMOUS_CLASS_NAME_PREFIX_REGEXP, $className) === 1) { | 
| 62: | $reflector = new DefaultReflector(new AggregateSourceLocator([ | 
| 63: | $betterReflection->sourceLocator(), | 
| 64: | new AnonymousClassObjectSourceLocator($instance, $betterReflection->phpParser()), | 
| 65: | ])); | 
| 66: | } else { | 
| 67: | $reflector = $betterReflection->reflector(); | 
| 68: | } | 
| 69: |  | 
| 70: | return new self($reflector, $reflector->reflectClass($className), $instance); | 
| 71: | } | 
| 72: |  | 
| 73: |  | 
| 74: |  | 
| 75: |  | 
| 76: |  | 
| 77: |  | 
| 78: |  | 
| 79: |  | 
| 80: |  | 
| 81: |  | 
| 82: | private function getRuntimeProperties(int $filter = 0): array | 
| 83: | { | 
| 84: | if (! $this->reflectionClass->isInstance($this->object)) { | 
| 85: | throw new InvalidArgumentException('Cannot reflect runtime properties of a separate class'); | 
| 86: | } | 
| 87: |  | 
| 88: | if ($filter !== 0 && ! ($filter & CoreReflectionProperty::IS_PUBLIC)) { | 
| 89: | return []; | 
| 90: | } | 
| 91: |  | 
| 92: |  | 
| 93: | $this->reflectionClass->getProperties(); | 
| 94: |  | 
| 95: |  | 
| 96: |  | 
| 97: | $reflectionProperties = (new CoreReflectionObject($this->object))->getProperties(); | 
| 98: | $runtimeProperties    = []; | 
| 99: |  | 
| 100: | foreach ($reflectionProperties as $property) { | 
| 101: | $propertyName = $property->getName(); | 
| 102: |  | 
| 103: | if ($this->reflectionClass->hasProperty($propertyName)) { | 
| 104: | continue; | 
| 105: | } | 
| 106: |  | 
| 107: | $propertyNode = $this->createPropertyNodeFromRuntimePropertyReflection($property, $this->object); | 
| 108: |  | 
| 109: | $runtimeProperties[$propertyName] = ReflectionProperty::createFromNode($this->reflector, $propertyNode, $propertyNode->props[0], $this, $this, false, false); | 
| 110: | } | 
| 111: |  | 
| 112: | return $runtimeProperties; | 
| 113: | } | 
| 114: |  | 
| 115: |  | 
| 116: |  | 
| 117: |  | 
| 118: |  | 
| 119: |  | 
| 120: |  | 
| 121: | private function createPropertyNodeFromRuntimePropertyReflection(CoreReflectionProperty $property, object $instance): PropertyNode | 
| 122: | { | 
| 123: | $builder = new PropertyNodeBuilder($property->getName()); | 
| 124: | $builder->setDefault($property->getValue($instance)); | 
| 125: | $builder->makePublic(); | 
| 126: |  | 
| 127: | return $builder->getNode(); | 
| 128: | } | 
| 129: |  | 
| 130: | public function getShortName(): string | 
| 131: | { | 
| 132: | return $this->reflectionClass->getShortName(); | 
| 133: | } | 
| 134: |  | 
| 135: | public function getName(): string | 
| 136: | { | 
| 137: | return $this->reflectionClass->getName(); | 
| 138: | } | 
| 139: |  | 
| 140: | public function getNamespaceName(): ?string | 
| 141: | { | 
| 142: | return $this->reflectionClass->getNamespaceName(); | 
| 143: | } | 
| 144: |  | 
| 145: | public function inNamespace(): bool | 
| 146: | { | 
| 147: | return $this->reflectionClass->inNamespace(); | 
| 148: | } | 
| 149: |  | 
| 150: |  | 
| 151: | public function getExtensionName(): ?string | 
| 152: | { | 
| 153: | return $this->reflectionClass->getExtensionName(); | 
| 154: | } | 
| 155: |  | 
| 156: |  | 
| 157: |  | 
| 158: |  | 
| 159: | public function getMethods(int $filter = 0): array | 
| 160: | { | 
| 161: | return $this->reflectionClass->getMethods($filter); | 
| 162: | } | 
| 163: |  | 
| 164: |  | 
| 165: |  | 
| 166: |  | 
| 167: | public function getImmediateMethods(int $filter = 0): array | 
| 168: | { | 
| 169: | return $this->reflectionClass->getImmediateMethods($filter); | 
| 170: | } | 
| 171: |  | 
| 172: |  | 
| 173: | public function getMethod(string $methodName): ?\PHPStan\BetterReflection\Reflection\ReflectionMethod | 
| 174: | { | 
| 175: | return $this->reflectionClass->getMethod($methodName); | 
| 176: | } | 
| 177: |  | 
| 178: |  | 
| 179: | public function hasMethod(string $methodName): bool | 
| 180: | { | 
| 181: | return $this->reflectionClass->hasMethod($methodName); | 
| 182: | } | 
| 183: |  | 
| 184: |  | 
| 185: |  | 
| 186: |  | 
| 187: | public function getImmediateConstants(int $filter = 0): array | 
| 188: | { | 
| 189: | return $this->reflectionClass->getImmediateConstants($filter); | 
| 190: | } | 
| 191: |  | 
| 192: |  | 
| 193: |  | 
| 194: |  | 
| 195: | public function getConstants(int $filter = 0): array | 
| 196: | { | 
| 197: | return $this->reflectionClass->getConstants($filter); | 
| 198: | } | 
| 199: |  | 
| 200: | public function hasConstant(string $name): bool | 
| 201: | { | 
| 202: | return $this->reflectionClass->hasConstant($name); | 
| 203: | } | 
| 204: |  | 
| 205: | public function getConstant(string $name): ?\PHPStan\BetterReflection\Reflection\ReflectionClassConstant | 
| 206: | { | 
| 207: | return $this->reflectionClass->getConstant($name); | 
| 208: | } | 
| 209: |  | 
| 210: | public function getConstructor(): ?\PHPStan\BetterReflection\Reflection\ReflectionMethod | 
| 211: | { | 
| 212: | return $this->reflectionClass->getConstructor(); | 
| 213: | } | 
| 214: |  | 
| 215: |  | 
| 216: |  | 
| 217: |  | 
| 218: | public function getProperties(int $filter = 0): array | 
| 219: | { | 
| 220: | return array_merge($this->reflectionClass->getProperties($filter), $this->getRuntimeProperties($filter)); | 
| 221: | } | 
| 222: |  | 
| 223: |  | 
| 224: |  | 
| 225: |  | 
| 226: | public function getImmediateProperties(int $filter = 0): array | 
| 227: | { | 
| 228: | return array_merge($this->reflectionClass->getImmediateProperties($filter), $this->getRuntimeProperties($filter)); | 
| 229: | } | 
| 230: |  | 
| 231: | public function getProperty(string $name): ?\PHPStan\BetterReflection\Reflection\ReflectionProperty | 
| 232: | { | 
| 233: | $runtimeProperties = $this->getRuntimeProperties(); | 
| 234: |  | 
| 235: | if (isset($runtimeProperties[$name])) { | 
| 236: | return $runtimeProperties[$name]; | 
| 237: | } | 
| 238: |  | 
| 239: | return $this->reflectionClass->getProperty($name); | 
| 240: | } | 
| 241: |  | 
| 242: | public function hasProperty(string $name): bool | 
| 243: | { | 
| 244: | $runtimeProperties = $this->getRuntimeProperties(); | 
| 245: |  | 
| 246: | return isset($runtimeProperties[$name]) || $this->reflectionClass->hasProperty($name); | 
| 247: | } | 
| 248: |  | 
| 249: |  | 
| 250: |  | 
| 251: |  | 
| 252: | public function getDefaultProperties(): array | 
| 253: | { | 
| 254: | return array_map(static function (ReflectionProperty $property) { | 
| 255: | return $property->getDefaultValue(); | 
| 256: | }, array_filter($this->getProperties(), static function (ReflectionProperty $property) : bool { | 
| 257: | return $property->isDefault(); | 
| 258: | })); | 
| 259: | } | 
| 260: |  | 
| 261: |  | 
| 262: | public function getFileName(): ?string | 
| 263: | { | 
| 264: | return $this->reflectionClass->getFileName(); | 
| 265: | } | 
| 266: |  | 
| 267: | public function getLocatedSource(): LocatedSource | 
| 268: | { | 
| 269: | return $this->reflectionClass->getLocatedSource(); | 
| 270: | } | 
| 271: |  | 
| 272: | public function getStartLine(): int | 
| 273: | { | 
| 274: | return $this->reflectionClass->getStartLine(); | 
| 275: | } | 
| 276: |  | 
| 277: | public function getEndLine(): int | 
| 278: | { | 
| 279: | return $this->reflectionClass->getEndLine(); | 
| 280: | } | 
| 281: |  | 
| 282: | public function getStartColumn(): int | 
| 283: | { | 
| 284: | return $this->reflectionClass->getStartColumn(); | 
| 285: | } | 
| 286: |  | 
| 287: | public function getEndColumn(): int | 
| 288: | { | 
| 289: | return $this->reflectionClass->getEndColumn(); | 
| 290: | } | 
| 291: |  | 
| 292: | public function getParentClass(): ?\PHPStan\BetterReflection\Reflection\ReflectionClass | 
| 293: | { | 
| 294: | return $this->reflectionClass->getParentClass(); | 
| 295: | } | 
| 296: |  | 
| 297: |  | 
| 298: |  | 
| 299: |  | 
| 300: | public function getParentClassNames(): array | 
| 301: | { | 
| 302: | return $this->reflectionClass->getParentClassNames(); | 
| 303: | } | 
| 304: |  | 
| 305: |  | 
| 306: | public function getDocComment(): ?string | 
| 307: | { | 
| 308: | return $this->reflectionClass->getDocComment(); | 
| 309: | } | 
| 310: |  | 
| 311: | public function isAnonymous(): bool | 
| 312: | { | 
| 313: | return $this->reflectionClass->isAnonymous(); | 
| 314: | } | 
| 315: |  | 
| 316: | public function isInternal(): bool | 
| 317: | { | 
| 318: | return $this->reflectionClass->isInternal(); | 
| 319: | } | 
| 320: |  | 
| 321: | public function isUserDefined(): bool | 
| 322: | { | 
| 323: | return $this->reflectionClass->isUserDefined(); | 
| 324: | } | 
| 325: |  | 
| 326: | public function isDeprecated(): bool | 
| 327: | { | 
| 328: | return $this->reflectionClass->isDeprecated(); | 
| 329: | } | 
| 330: |  | 
| 331: | public function isAbstract(): bool | 
| 332: | { | 
| 333: | return $this->reflectionClass->isAbstract(); | 
| 334: | } | 
| 335: |  | 
| 336: | public function isFinal(): bool | 
| 337: | { | 
| 338: | return $this->reflectionClass->isFinal(); | 
| 339: | } | 
| 340: |  | 
| 341: | public function isReadOnly(): bool | 
| 342: | { | 
| 343: | return $this->reflectionClass->isReadOnly(); | 
| 344: | } | 
| 345: |  | 
| 346: | public function getModifiers(): int | 
| 347: | { | 
| 348: | return $this->reflectionClass->getModifiers(); | 
| 349: | } | 
| 350: |  | 
| 351: | public function isTrait(): bool | 
| 352: | { | 
| 353: | return $this->reflectionClass->isTrait(); | 
| 354: | } | 
| 355: |  | 
| 356: | public function isInterface(): bool | 
| 357: | { | 
| 358: | return $this->reflectionClass->isInterface(); | 
| 359: | } | 
| 360: |  | 
| 361: |  | 
| 362: |  | 
| 363: |  | 
| 364: | public function getTraits(): array | 
| 365: | { | 
| 366: | return $this->reflectionClass->getTraits(); | 
| 367: | } | 
| 368: |  | 
| 369: |  | 
| 370: |  | 
| 371: |  | 
| 372: | public function getTraitNames(): array | 
| 373: | { | 
| 374: | return $this->reflectionClass->getTraitNames(); | 
| 375: | } | 
| 376: |  | 
| 377: |  | 
| 378: |  | 
| 379: |  | 
| 380: | public function getTraitAliases(): array | 
| 381: | { | 
| 382: | return $this->reflectionClass->getTraitAliases(); | 
| 383: | } | 
| 384: |  | 
| 385: |  | 
| 386: |  | 
| 387: |  | 
| 388: | public function getInterfaces(): array | 
| 389: | { | 
| 390: | return $this->reflectionClass->getInterfaces(); | 
| 391: | } | 
| 392: |  | 
| 393: |  | 
| 394: |  | 
| 395: |  | 
| 396: | public function getImmediateInterfaces(): array | 
| 397: | { | 
| 398: | return $this->reflectionClass->getImmediateInterfaces(); | 
| 399: | } | 
| 400: |  | 
| 401: |  | 
| 402: |  | 
| 403: |  | 
| 404: | public function getInterfaceNames(): array | 
| 405: | { | 
| 406: | return $this->reflectionClass->getInterfaceNames(); | 
| 407: | } | 
| 408: |  | 
| 409: | public function isInstance(object $object): bool | 
| 410: | { | 
| 411: | return $this->reflectionClass->isInstance($object); | 
| 412: | } | 
| 413: |  | 
| 414: | public function isSubclassOf(string $className): bool | 
| 415: | { | 
| 416: | return $this->reflectionClass->isSubclassOf($className); | 
| 417: | } | 
| 418: |  | 
| 419: | public function implementsInterface(string $interfaceName): bool | 
| 420: | { | 
| 421: | return $this->reflectionClass->implementsInterface($interfaceName); | 
| 422: | } | 
| 423: |  | 
| 424: | public function isInstantiable(): bool | 
| 425: | { | 
| 426: | return $this->reflectionClass->isInstantiable(); | 
| 427: | } | 
| 428: |  | 
| 429: | public function isCloneable(): bool | 
| 430: | { | 
| 431: | return $this->reflectionClass->isCloneable(); | 
| 432: | } | 
| 433: |  | 
| 434: | public function isIterateable(): bool | 
| 435: | { | 
| 436: | return $this->reflectionClass->isIterateable(); | 
| 437: | } | 
| 438: |  | 
| 439: | public function isEnum(): bool | 
| 440: | { | 
| 441: | return $this->reflectionClass->isEnum(); | 
| 442: | } | 
| 443: |  | 
| 444: |  | 
| 445: |  | 
| 446: |  | 
| 447: | public function getStaticProperties(): array | 
| 448: | { | 
| 449: | return $this->reflectionClass->getStaticProperties(); | 
| 450: | } | 
| 451: |  | 
| 452: |  | 
| 453: |  | 
| 454: |  | 
| 455: | public function setStaticPropertyValue(string $propertyName, $value): void | 
| 456: | { | 
| 457: | $this->reflectionClass->setStaticPropertyValue($propertyName, $value); | 
| 458: | } | 
| 459: |  | 
| 460: |  | 
| 461: |  | 
| 462: |  | 
| 463: | public function getStaticPropertyValue(string $propertyName) | 
| 464: | { | 
| 465: | return $this->reflectionClass->getStaticPropertyValue($propertyName); | 
| 466: | } | 
| 467: |  | 
| 468: |  | 
| 469: | public function getAttributes(): array | 
| 470: | { | 
| 471: | return $this->reflectionClass->getAttributes(); | 
| 472: | } | 
| 473: |  | 
| 474: |  | 
| 475: | public function getAttributesByName(string $name): array | 
| 476: | { | 
| 477: | return $this->reflectionClass->getAttributesByName($name); | 
| 478: | } | 
| 479: |  | 
| 480: |  | 
| 481: |  | 
| 482: |  | 
| 483: |  | 
| 484: |  | 
| 485: | public function getAttributesByInstance(string $className): array | 
| 486: | { | 
| 487: | return $this->reflectionClass->getAttributesByInstance($className); | 
| 488: | } | 
| 489: | } | 
| 490: |  |