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 Type[] $types
16: */
17: public function __construct(array $types)
18: {
19: parent::__construct($types);
20: }
21:
22: public function describe(VerbosityLevel $level): string
23: {
24: return '(' . parent::describe($level) . ')';
25: }
26:
27: protected function unionTypes(callable $getType): Type
28: {
29: $resultTypes = [];
30: foreach ($this->getTypes() as $type) {
31: $result = $getType($type);
32: if ($result instanceof ErrorType) {
33: continue;
34: }
35:
36: $resultTypes[] = $result;
37: }
38:
39: if (count($resultTypes) === 0) {
40: return new ErrorType();
41: }
42:
43: return TypeUtils::toBenevolentUnion(TypeCombinator::union(...$resultTypes));
44: }
45:
46: protected function unionResults(callable $getResult): TrinaryLogic
47: {
48: return TrinaryLogic::createNo()->lazyOr($this->getTypes(), $getResult);
49: }
50:
51: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
52: {
53: return TrinaryLogic::createNo()->lazyOr($this->getTypes(), static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes));
54: }
55:
56: public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
57: {
58: $types = TemplateTypeMap::createEmpty();
59:
60: foreach ($this->getTypes() as $type) {
61: $types = $types->benevolentUnion($type->inferTemplateTypes($receivedType));
62: }
63:
64: return $types;
65: }
66:
67: public function inferTemplateTypesOn(Type $templateType): TemplateTypeMap
68: {
69: $types = TemplateTypeMap::createEmpty();
70:
71: foreach ($this->getTypes() as $type) {
72: $types = $types->benevolentUnion($templateType->inferTemplateTypes($type));
73: }
74:
75: return $types;
76: }
77:
78: public function traverse(callable $cb): Type
79: {
80: $types = [];
81: $changed = false;
82:
83: foreach ($this->getTypes() as $type) {
84: $newType = $cb($type);
85: if ($type !== $newType) {
86: $changed = true;
87: }
88: $types[] = $newType;
89: }
90:
91: if ($changed) {
92: return TypeUtils::toBenevolentUnion(TypeCombinator::union(...$types));
93: }
94:
95: return $this;
96: }
97:
98: /**
99: * @param mixed[] $properties
100: */
101: public static function __set_state(array $properties): Type
102: {
103: return new self($properties['types']);
104: }
105:
106: }
107: