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