1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection\Adapter;
6:
7: use ReflectionType as CoreReflectionType;
8: use PHPStan\BetterReflection\Reflection\ReflectionIntersectionType as BetterReflectionIntersectionType;
9: use PHPStan\BetterReflection\Reflection\ReflectionNamedType as BetterReflectionNamedType;
10: use PHPStan\BetterReflection\Reflection\ReflectionType as BetterReflectionType;
11: use PHPStan\BetterReflection\Reflection\ReflectionUnionType as BetterReflectionUnionType;
12:
13: use function array_filter;
14: use function array_values;
15: use function count;
16:
17: abstract class ReflectionType extends CoreReflectionType
18: {
19: /**
20: * @param BetterReflectionUnionType|BetterReflectionNamedType|BetterReflectionIntersectionType|null $betterReflectionType
21: * @return \PHPStan\BetterReflection\Reflection\Adapter\ReflectionUnionType|\PHPStan\BetterReflection\Reflection\Adapter\ReflectionNamedType|\PHPStan\BetterReflection\Reflection\Adapter\ReflectionIntersectionType|null
22: */
23: public static function fromTypeOrNull($betterReflectionType)
24: {
25: return $betterReflectionType !== null ? self::fromType($betterReflectionType) : null;
26: }
27:
28: /** @internal
29: * @param BetterReflectionNamedType|BetterReflectionUnionType|BetterReflectionIntersectionType $betterReflectionType
30: * @return \PHPStan\BetterReflection\Reflection\Adapter\ReflectionUnionType|\PHPStan\BetterReflection\Reflection\Adapter\ReflectionNamedType|\PHPStan\BetterReflection\Reflection\Adapter\ReflectionIntersectionType */
31: public static function fromType($betterReflectionType)
32: {
33: if ($betterReflectionType instanceof BetterReflectionUnionType) {
34: // php-src has this weird behavior where a union type composed of a single type `T`
35: // together with `null` means that a `ReflectionNamedType` for `?T` is produced,
36: // rather than `T|null`. This is done to keep BC compatibility with PHP 7.1 (which
37: // introduced nullable types), but at reflection level, this is mostly a nuisance.
38: // In order to keep parity with core, we stashed this weird behavior in here.
39: $nonNullTypes = array_values(array_filter($betterReflectionType->getTypes(), static function (BetterReflectionType $type) : bool {
40: return ! ($type instanceof BetterReflectionNamedType && $type->getName() === 'null');
41: }));
42:
43: if (
44: $betterReflectionType->allowsNull()
45: && count($nonNullTypes) === 1
46: && $nonNullTypes[0] instanceof BetterReflectionNamedType
47: ) {
48: return new ReflectionNamedType($nonNullTypes[0], true);
49: }
50:
51: return new ReflectionUnionType($betterReflectionType);
52: }
53:
54: if ($betterReflectionType instanceof BetterReflectionIntersectionType) {
55: return new ReflectionIntersectionType($betterReflectionType);
56: }
57:
58: return new ReflectionNamedType($betterReflectionType, $betterReflectionType->allowsNull());
59: }
60: }
61: