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\UnionType;
11: use PHPStan\BetterReflection\Reflector\Reflector;
12:
13: use function array_map;
14: use function assert;
15: use function implode;
16: use function sprintf;
17:
18: /** @psalm-immutable */
19: class ReflectionUnionType extends ReflectionType
20: {
21: /** @var non-empty-list<ReflectionNamedType|ReflectionIntersectionType> */
22: private array $types;
23:
24: /** @internal
25: * @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 */
26: public function __construct(Reflector $reflector, $owner, UnionType $type)
27: {
28: /** @var non-empty-list<ReflectionNamedType|ReflectionIntersectionType> $types */
29: $types = array_map(static function ($type) use ($reflector, $owner) {
30: $type = ReflectionType::createFromNode($reflector, $owner, $type);
31: assert($type instanceof ReflectionNamedType || $type instanceof ReflectionIntersectionType);
32:
33: return $type;
34: }, $type->types);
35: $this->types = $types;
36: }
37:
38: /** @internal
39: * @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
40: * @return static */
41: public function withOwner($owner)
42: {
43: $clone = clone $this;
44:
45: foreach ($clone->types as $typeNo => $innerType) {
46: $clone->types[$typeNo] = $innerType->withOwner($owner);
47: }
48:
49: return $clone;
50: }
51:
52: /** @return non-empty-list<ReflectionNamedType|ReflectionIntersectionType> */
53: public function getTypes(): array
54: {
55: return $this->types;
56: }
57:
58: public function allowsNull(): bool
59: {
60: foreach ($this->types as $type) {
61: if ($type->allowsNull()) {
62: return true;
63: }
64: }
65:
66: return false;
67: }
68:
69: /** @return non-empty-string */
70: public function __toString(): string
71: {
72: return implode('|', array_map(static function (ReflectionType $type): string {
73: if ($type instanceof ReflectionIntersectionType) {
74: return sprintf('(%s)', $type->__toString());
75: }
76:
77: return $type->__toString();
78: }, $this->types));
79: }
80: }
81: