1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection;
6:
7: use LogicException;
8: use PhpParser\Node\Identifier;
9: use PhpParser\Node\Name;
10: use PHPStan\BetterReflection\Reflector\Reflector;
11:
12: use function array_key_exists;
13: use function assert;
14: use function sprintf;
15: use function strtolower;
16:
17: class ReflectionNamedType extends ReflectionType
18: {
19: private const BUILT_IN_TYPES = [
20: 'int' => null,
21: 'float' => null,
22: 'string' => null,
23: 'bool' => null,
24: 'callable' => null,
25: 'self' => null,
26: 'parent' => null,
27: 'array' => null,
28: 'iterable' => null,
29: 'object' => null,
30: 'void' => null,
31: 'mixed' => null,
32: 'static' => null,
33: 'null' => null,
34: 'never' => null,
35: 'false' => null,
36: 'true' => null,
37: ];
38:
39: /**
40: * @var string
41: */
42: private $name;
43: /**
44: * @var \PHPStan\BetterReflection\Reflector\Reflector
45: */
46: private $reflector;
47: /**
48: * @var \PHPStan\BetterReflection\Reflection\ReflectionParameter|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionEnum|\PHPStan\BetterReflection\Reflection\ReflectionProperty
49: */
50: private $owner;
51: /** @internal
52: * @param \PHPStan\BetterReflection\Reflection\ReflectionParameter|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionEnum|\PHPStan\BetterReflection\Reflection\ReflectionProperty $owner
53: * @param \PhpParser\Node\Identifier|\PhpParser\Node\Name $type */
54: public function __construct(Reflector $reflector, $owner, $type)
55: {
56: $this->reflector = $reflector;
57: $this->owner = $owner;
58: $this->name = $type->toString();
59: }
60:
61: /** @internal
62: * @param \PHPStan\BetterReflection\Reflection\ReflectionParameter|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionEnum|\PHPStan\BetterReflection\Reflection\ReflectionProperty $owner
63: * @return $this */
64: public function withOwner($owner)
65: {
66: $clone = clone $this;
67: $clone->owner = $owner;
68:
69: return $clone;
70: }
71:
72: public function getName(): string
73: {
74: return $this->name;
75: }
76:
77: /**
78: * Checks if it is a built-in type (i.e., it's not an object...)
79: *
80: * @see https://php.net/manual/en/reflectiontype.isbuiltin.php
81: */
82: public function isBuiltin(): bool
83: {
84: return array_key_exists(strtolower($this->name), self::BUILT_IN_TYPES);
85: }
86:
87: public function getClass(): ReflectionClass
88: {
89: if (! $this->isBuiltin()) {
90: return $this->reflector->reflectClass($this->name);
91: }
92:
93: if (
94: $this->owner instanceof ReflectionEnum
95: || $this->owner instanceof ReflectionFunction
96: || ($this->owner instanceof ReflectionParameter && $this->owner->getDeclaringFunction() instanceof ReflectionFunction)
97: ) {
98: throw new LogicException(sprintf('The type %s cannot be resolved to class', $this->name));
99: }
100:
101: $lowercaseName = strtolower($this->name);
102:
103: if ($lowercaseName === 'self') {
104: $class = $this->owner->getImplementingClass();
105: assert($class instanceof ReflectionClass);
106:
107: return $class;
108: }
109:
110: if ($lowercaseName === 'parent') {
111: $class = $this->owner->getDeclaringClass();
112: assert($class instanceof ReflectionClass);
113: $parentClass = $class->getParentClass();
114: assert($parentClass instanceof ReflectionClass);
115:
116: return $parentClass;
117: }
118:
119: if (
120: $this->owner instanceof ReflectionMethod
121: && $lowercaseName === 'static'
122: ) {
123: return $this->owner->getCurrentClass();
124: }
125:
126: throw new LogicException(sprintf('The type %s cannot be resolved to class', $this->name));
127: }
128:
129: public function allowsNull(): bool
130: {
131: switch (strtolower($this->name)) {
132: case 'mixed':
133: return true;
134: case 'null':
135: return true;
136: default:
137: return false;
138: }
139: }
140:
141: public function __toString(): string
142: {
143: return $this->getName();
144: }
145: }
146: