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