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: abstract class ReflectionType
15: {
16: /** @internal
17: * @param \PHPStan\BetterReflection\Reflection\ReflectionParameter|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionEnum|\PHPStan\BetterReflection\Reflection\ReflectionProperty $owner
18: * @param \PhpParser\Node\Identifier|\PhpParser\Node\Name|\PhpParser\Node\NullableType|\PhpParser\Node\UnionType|\PhpParser\Node\IntersectionType $type
19: * @return \PHPStan\BetterReflection\Reflection\ReflectionNamedType|\PHPStan\BetterReflection\Reflection\ReflectionUnionType|\PHPStan\BetterReflection\Reflection\ReflectionIntersectionType */
20: public static function createFromNode(Reflector $reflector, $owner, $type, bool $allowsNull = false)
21: {
22: if ($type instanceof NullableType) {
23: $type = $type->type;
24: $allowsNull = true;
25: }
26: if ($type instanceof Identifier || $type instanceof Name) {
27: if (
28: $type->toLowerString() === 'null'
29: || $type->toLowerString() === 'mixed'
30: || ! $allowsNull
31: ) {
32: return new ReflectionNamedType($reflector, $owner, $type);
33: }
34:
35: return new ReflectionUnionType($reflector, $owner, new UnionType([$type, new Identifier('null')]));
36: }
37: if ($type instanceof IntersectionType) {
38: return new ReflectionIntersectionType($reflector, $owner, $type);
39: }
40: if (! $allowsNull) {
41: return new ReflectionUnionType($reflector, $owner, $type);
42: }
43: foreach ($type->types as $innerUnionType) {
44: /** @psalm-suppress RedundantConditionGivenDocblockType https://github.com/nikic/PHP-Parser/pull/889 */
45: if (
46: /** @phpstan-ignore-next-line https://github.com/nikic/PHP-Parser/pull/889 */
47: ($innerUnionType instanceof Identifier || $innerUnionType instanceof Name)
48: && $innerUnionType->toLowerString() === 'null'
49: ) {
50: return new ReflectionUnionType($reflector, $owner, $type);
51: }
52: }
53: $types = $type->types;
54: $types[] = new Identifier('null');
55: return new ReflectionUnionType($reflector, $owner, new UnionType($types));
56: }
57:
58: /**
59: * Does the type allow null?
60: */
61: abstract public function allowsNull(): bool;
62:
63: /**
64: * Convert this string type to a string
65: */
66: abstract public function __toString(): string;
67: }
68: