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(Reflector $reflector, $owner, $type, bool $allowsNull = false)
26: {
27: if ($type instanceof NullableType) {
28: $type = $type->type;
29: $allowsNull = true;
30: }
31: if ($type instanceof Identifier || $type instanceof Name) {
32: if (
33: $type->toLowerString() === 'null'
34: || $type->toLowerString() === 'mixed'
35: || ! $allowsNull
36: ) {
37: return new ReflectionNamedType($reflector, $owner, $type);
38: }
39:
40: return new ReflectionUnionType(
41: $reflector,
42: $owner,
43: new UnionType([$type, new Identifier('null')]),
44: );
45: }
46: if ($type instanceof IntersectionType) {
47: return new ReflectionIntersectionType($reflector, $owner, $type);
48: }
49: if (! $allowsNull) {
50: return new ReflectionUnionType($reflector, $owner, $type);
51: }
52: foreach ($type->types as $innerUnionType) {
53: if (
54: ($innerUnionType instanceof Identifier || $innerUnionType instanceof Name)
55: && $innerUnionType->toLowerString() === 'null'
56: ) {
57: return new ReflectionUnionType($reflector, $owner, $type);
58: }
59: }
60: $types = $type->types;
61: $types[] = new Identifier('null');
62: return new ReflectionUnionType($reflector, $owner, new UnionType($types));
63: }
64:
65: /**
66: * Does the type allow null?
67: */
68: abstract public function allowsNull(): bool;
69:
70: /**
71: * Convert this string type to a string
72: *
73: * @return non-empty-string
74: */
75: abstract public function __toString(): string;
76: }
77: