1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection;
6:
7: use PhpParser\Node\Identifier;
8: use PhpParser\Node\IntersectionType;
9: use PhpParser\Node\Name;
10: use PhpParser\Node\NullableType;
11: use PhpParser\Node\UnionType;
12: use PHPStan\BetterReflection\Reflector\Reflector;
13:
14: /** @psalm-immutable */
15: abstract class ReflectionType
16: {
17: /**
18: * @internal
19: *
20: * @psalm-pure
21: * @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
22: * @param \PhpParser\Node\Identifier|\PhpParser\Node\Name|\PhpParser\Node\NullableType|\PhpParser\Node\UnionType|\PhpParser\Node\IntersectionType $type
23: * @return \PHPStan\BetterReflection\Reflection\ReflectionNamedType|\PHPStan\BetterReflection\Reflection\ReflectionUnionType|\PHPStan\BetterReflection\Reflection\ReflectionIntersectionType
24: */
25: public static function createFromNode(
26: Reflector $reflector,
27: $owner,
28: $type,
29: bool $allowsNull = false
30: ) {
31: if ($type instanceof NullableType) {
32: $type = $type->type;
33: $allowsNull = true;
34: }
35:
36: if ($type instanceof Identifier || $type instanceof Name) {
37: if (
38: $type->toLowerString() === 'null'
39: || $type->toLowerString() === 'mixed'
40: || ! $allowsNull
41: ) {
42: return new ReflectionNamedType($reflector, $owner, $type);
43: }
44:
45: return new ReflectionUnionType(
46: $reflector,
47: $owner,
48: new UnionType([$type, new Identifier('null')]),
49: );
50: }
51:
52: if ($type instanceof IntersectionType) {
53: return new ReflectionIntersectionType($reflector, $owner, $type);
54: }
55:
56: if (! $allowsNull) {
57: return new ReflectionUnionType($reflector, $owner, $type);
58: }
59:
60: foreach ($type->types as $innerUnionType) {
61: if (
62: ($innerUnionType instanceof Identifier || $innerUnionType instanceof Name)
63: && $innerUnionType->toLowerString() === 'null'
64: ) {
65: return new ReflectionUnionType($reflector, $owner, $type);
66: }
67: }
68:
69: $types = $type->types;
70: $types[] = new Identifier('null');
71:
72: return new ReflectionUnionType($reflector, $owner, new UnionType($types));
73: }
74:
75: /**
76: * Does the type allow null?
77: */
78: abstract public function allowsNull(): bool;
79:
80: /**
81: * Convert this string type to a string
82: *
83: * @return non-empty-string
84: */
85: abstract public function __toString(): string;
86: }
87: