1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\TrinaryLogic;
6: use PHPStan\Type\Generic\TemplateTypeMap;
7: use function count;
8:
9: /** @api */
10: class BenevolentUnionType extends UnionType
11: {
12:
13: /**
14: * @api
15: * @param list<Type> $types
16: */
17: public function __construct(array $types, bool $normalized = false)
18: {
19: parent::__construct($types, $normalized);
20: }
21:
22: public function filterTypes(callable $filterCb): Type
23: {
24: $result = parent::filterTypes($filterCb);
25: if (!$result instanceof self && $result instanceof UnionType) {
26: return TypeUtils::toBenevolentUnion($result);
27: }
28:
29: return $result;
30: }
31:
32: public function tryRemove(Type $typeToRemove): ?Type
33: {
34: $result = parent::tryRemove($typeToRemove);
35: if ($result === null) {
36: return null;
37: }
38:
39: return TypeUtils::toBenevolentUnion($result);
40: }
41:
42: public function describe(VerbosityLevel $level): string
43: {
44: return '(' . parent::describe($level) . ')';
45: }
46:
47: protected function unionTypes(callable $getType): Type
48: {
49: $resultTypes = [];
50: foreach ($this->getTypes() as $type) {
51: $result = $getType($type);
52: if ($result instanceof ErrorType) {
53: continue;
54: }
55:
56: $resultTypes[] = $result;
57: }
58:
59: if (count($resultTypes) === 0) {
60: return new ErrorType();
61: }
62:
63: return TypeUtils::toBenevolentUnion(TypeCombinator::union(...$resultTypes));
64: }
65:
66: protected function pickFromTypes(
67: callable $getValues,
68: callable $criteria,
69: ): array
70: {
71: $values = [];
72: foreach ($this->getTypes() as $type) {
73: $innerValues = $getValues($type);
74: if ($innerValues === [] && $criteria($type)) {
75: return [];
76: }
77:
78: foreach ($innerValues as $innerType) {
79: $values[] = $innerType;
80: }
81: }
82:
83: return $values;
84: }
85:
86: public function getOffsetValueType(Type $offsetType): Type
87: {
88: $types = [];
89: foreach ($this->getTypes() as $innerType) {
90: $valueType = $innerType->getOffsetValueType($offsetType);
91: if ($valueType instanceof ErrorType) {
92: continue;
93: }
94:
95: $types[] = $valueType;
96: }
97:
98: if (count($types) === 0) {
99: return new ErrorType();
100: }
101:
102: return TypeUtils::toBenevolentUnion(TypeCombinator::union(...$types));
103: }
104:
105: protected function unionResults(callable $getResult): TrinaryLogic
106: {
107: return TrinaryLogic::createNo()->lazyOr($this->getTypes(), $getResult);
108: }
109:
110: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
111: {
112: $result = AcceptsResult::createNo();
113: foreach ($this->getTypes() as $innerType) {
114: $result = $result->or($acceptingType->accepts($innerType, $strictTypes));
115: }
116:
117: return $result;
118: }
119:
120: public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
121: {
122: $types = TemplateTypeMap::createEmpty();
123:
124: foreach ($this->getTypes() as $type) {
125: $types = $types->benevolentUnion($type->inferTemplateTypes($receivedType));
126: }
127:
128: return $types;
129: }
130:
131: public function inferTemplateTypesOn(Type $templateType): TemplateTypeMap
132: {
133: $types = TemplateTypeMap::createEmpty();
134:
135: foreach ($this->getTypes() as $type) {
136: $types = $types->benevolentUnion($templateType->inferTemplateTypes($type));
137: }
138:
139: return $types;
140: }
141:
142: public function traverse(callable $cb): Type
143: {
144: $types = [];
145: $changed = false;
146:
147: foreach ($this->getTypes() as $type) {
148: $newType = $cb($type);
149: if ($type !== $newType) {
150: $changed = true;
151: }
152: $types[] = $newType;
153: }
154:
155: if ($changed) {
156: return TypeUtils::toBenevolentUnion(TypeCombinator::union(...$types));
157: }
158:
159: return $this;
160: }
161:
162: public function traverseSimultaneously(Type $right, callable $cb): Type
163: {
164: $newType = parent::traverseSimultaneously($right, $cb);
165: if ($newType === $this) {
166: return $this;
167: }
168:
169: return TypeUtils::toBenevolentUnion($newType);
170: }
171:
172: }
173: