1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection;
6:
7: use PhpParser\Node;
8: use PhpParser\Node\IntersectionType;
9: use ReflectionClass as CoreReflectionClass;
10: use PHPStan\BetterReflection\Reflector\Reflector;
11:
12: use function array_map;
13: use function assert;
14: use function implode;
15:
16: /** @psalm-immutable */
17: class ReflectionIntersectionType extends ReflectionType
18: {
19: /** @var non-empty-list<ReflectionNamedType> */
20: private array $types;
21:
22: /** @internal
23: * @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 */
24: public function __construct(
25: Reflector $reflector,
26: $owner,
27: IntersectionType $type
28: ) {
29: /** @var non-empty-list<ReflectionNamedType> $types */
30: $types = array_map(static function ($type) use ($reflector, $owner): ReflectionNamedType {
31: $type = ReflectionType::createFromNode($reflector, $owner, $type);
32: assert($type instanceof ReflectionNamedType);
33:
34: return $type;
35: }, $type->types);
36:
37: $this->types = $types;
38: }
39:
40: /**
41: * @return array<string, mixed>
42: */
43: public function exportToCache(): array
44: {
45: return [
46: 'types' => array_map(
47: static fn (ReflectionNamedType $type) => $type->exportToCache(),
48: $this->types,
49: ),
50: ];
51: }
52:
53: /**
54: * @param array<string, mixed> $data
55: * @param ReflectionParameter|ReflectionMethod|ReflectionFunction|ReflectionEnum|ReflectionProperty|ReflectionClassConstant $owner
56: */
57: public static function importFromCache(Reflector $reflector, array $data, $owner): self
58: {
59: $reflection = new CoreReflectionClass(self::class);
60: /** @var self $ref */
61: $ref = $reflection->newInstanceWithoutConstructor();
62: $ref->types = array_map(
63: static fn (array $typeData) => ReflectionNamedType::importFromCache($reflector, $typeData, $owner),
64: $data['types'],
65: );
66:
67: return $ref;
68: }
69:
70: /** @internal
71: * @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
72: * @return static */
73: public function withOwner($owner)
74: {
75: $clone = clone $this;
76:
77: $clone->types = array_map(static fn (ReflectionNamedType $type): ReflectionNamedType => $type->withOwner($owner), $clone->types);
78:
79: return $clone;
80: }
81:
82: /** @return non-empty-list<ReflectionNamedType> */
83: public function getTypes(): array
84: {
85: return $this->types;
86: }
87:
88: /**
89: * @return false
90: */
91: public function allowsNull(): bool
92: {
93: return false;
94: }
95:
96: /** @return non-empty-string */
97: public function __toString(): string
98: {
99: // @infection-ignore-all UnwrapArrayMap: It works without array_map() as well but this is less magical
100: return implode('&', array_map(static fn (ReflectionNamedType $type): string => $type->__toString(), $this->types));
101: }
102: }
103: