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: $this->name = $type->toString();
63: }
64:
65: /** @internal
66: * @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
67: * @return $this */
68: public function withOwner($owner)
69: {
70: $clone = clone $this;
71: $clone->owner = $owner;
72:
73: return $clone;
74: }
75:
76: /** @return non-empty-string */
77: public function getName(): string
78: {
79: return $this->name;
80: }
81:
82: /**
83: * Checks if it is a built-in type (i.e., it's not an object...)
84: *
85: * @see https://php.net/manual/en/reflectiontype.isbuiltin.php
86: */
87: public function isBuiltin(): bool
88: {
89: return array_key_exists(strtolower($this->name), self::BUILT_IN_TYPES);
90: }
91:
92: public function getClass(): ReflectionClass
93: {
94: if (! $this->isBuiltin()) {
95: return $this->reflector->reflectClass($this->name);
96: }
97:
98: if (
99: $this->owner instanceof ReflectionEnum
100: || $this->owner instanceof ReflectionFunction
101: || ($this->owner instanceof ReflectionParameter && $this->owner->getDeclaringFunction() instanceof ReflectionFunction)
102: ) {
103: throw new LogicException(sprintf('The type %s cannot be resolved to class', $this->name));
104: }
105:
106: $lowercaseName = strtolower($this->name);
107:
108: if ($lowercaseName === 'self') {
109: $class = $this->owner->getImplementingClass();
110: assert($class instanceof ReflectionClass);
111:
112: return $class;
113: }
114:
115: if ($lowercaseName === 'parent') {
116: $class = $this->owner->getDeclaringClass();
117: assert($class instanceof ReflectionClass);
118: $parentClass = $class->getParentClass();
119: assert($parentClass instanceof ReflectionClass);
120:
121: return $parentClass;
122: }
123:
124: if (
125: $this->owner instanceof ReflectionMethod
126: && $lowercaseName === 'static'
127: ) {
128: return $this->owner->getCurrentClass();
129: }
130:
131: throw new LogicException(sprintf('The type %s cannot be resolved to class', $this->name));
132: }
133:
134: public function allowsNull(): bool
135: {
136: switch (strtolower($this->name)) {
137: case 'mixed':
138: return true;
139: case 'null':
140: return true;
141: default:
142: return false;
143: }
144: }
145:
146: public function isIdentifier(): bool
147: {
148: return $this->type instanceof Identifier;
149: }
150:
151: /** @return non-empty-string */
152: public function __toString(): string
153: {
154: return $this->getName();
155: }
156: }
157: