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 $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($reflector, $owner, new UnionType([$type, new Identifier('null')]));
41: }
42: if ($type instanceof IntersectionType) {
43: return new ReflectionIntersectionType($reflector, $owner, $type);
44: }
45: if (! $allowsNull) {
46: return new ReflectionUnionType($reflector, $owner, $type);
47: }
48: foreach ($type->types as $innerUnionType) {
49: if (
50: ($innerUnionType instanceof Identifier || $innerUnionType instanceof Name)
51: && $innerUnionType->toLowerString() === 'null'
52: ) {
53: return new ReflectionUnionType($reflector, $owner, $type);
54: }
55: }
56: $types = $type->types;
57: $types[] = new Identifier('null');
58: return new ReflectionUnionType($reflector, $owner, new UnionType($types));
59: }
60:
61: /**
62: * Does the type allow null?
63: */
64: abstract public function allowsNull(): bool;
65:
66: /**
67: * Convert this string type to a string
68: *
69: * @return non-empty-string
70: */
71: abstract public function __toString(): string;
72: }
73: