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(Reflector $reflector, $owner, IntersectionType $type)
25: {
26: /** @var non-empty-list<ReflectionNamedType> $types */
27: $types = array_map(static function ($type) use ($reflector, $owner): ReflectionNamedType {
28: $type = ReflectionType::createFromNode($reflector, $owner, $type);
29: assert($type instanceof ReflectionNamedType);
30:
31: return $type;
32: }, $type->types);
33: $this->types = $types;
34: }
35:
36: /**
37: * @return array<string, mixed>
38: */
39: public function exportToCache(): array
40: {
41: return [
42: 'types' => array_map(
43: static fn (ReflectionNamedType $type) => $type->exportToCache(),
44: $this->types,
45: ),
46: ];
47: }
48:
49: /**
50: * @param array<string, mixed> $data
51: * @param ReflectionParameter|ReflectionMethod|ReflectionFunction|ReflectionEnum|ReflectionProperty|ReflectionClassConstant $owner
52: */
53: public static function importFromCache(Reflector $reflector, array $data, $owner): self
54: {
55: $reflection = new CoreReflectionClass(self::class);
56: /** @var self $ref */
57: $ref = $reflection->newInstanceWithoutConstructor();
58: $ref->types = array_map(
59: static fn (array $typeData) => ReflectionNamedType::importFromCache($reflector, $typeData, $owner),
60: $data['types'],
61: );
62:
63: return $ref;
64: }
65:
66: /** @internal
67: * @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
68: * @return static */
69: public function withOwner($owner)
70: {
71: $clone = clone $this;
72:
73: $clone->types = array_map(static fn (ReflectionNamedType $type): ReflectionNamedType => $type->withOwner($owner), $clone->types);
74:
75: return $clone;
76: }
77:
78: /** @return non-empty-list<ReflectionNamedType> */
79: public function getTypes(): array
80: {
81: return $this->types;
82: }
83:
84: /**
85: * @return false
86: */
87: public function allowsNull(): bool
88: {
89: return false;
90: }
91:
92: /** @return non-empty-string */
93: public function __toString(): string
94: {
95: // @infection-ignore-all UnwrapArrayMap: It works without array_map() as well but this is less magical
96: return implode('&', array_map(static fn (ReflectionNamedType $type): string => $type->__toString(), $this->types));
97: }
98: }
99: