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: $clone->types = array_map(static fn ($type) => $type->withOwner($owner), $clone->types);
46:
47: return $clone;
48: }
49:
50: /** @return non-empty-list<ReflectionNamedType|ReflectionIntersectionType> */
51: public function getTypes(): array
52: {
53: return $this->types;
54: }
55:
56: public function allowsNull(): bool
57: {
58: foreach ($this->types as $type) {
59: if ($type->allowsNull()) {
60: return true;
61: }
62: }
63:
64: return false;
65: }
66:
67: /** @return non-empty-string */
68: public function __toString(): string
69: {
70: return implode('|', array_map(static function (ReflectionType $type): string {
71: if ($type instanceof ReflectionIntersectionType) {
72: return sprintf('(%s)', $type->__toString());
73: }
74:
75: return $type->__toString();
76: }, $this->types));
77: }
78: }
79: