1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\ShouldNotHappenException;
6: use PHPStan\TrinaryLogic;
7: use function array_map;
8: use function array_merge;
9: use function array_unique;
10: use function array_values;
11:
12: /**
13: * @api
14: */
15: final class IsSuperTypeOfResult
16: {
17:
18: /**
19: * @api
20: * @param list<string> $reasons
21: */
22: public function __construct(
23: public readonly TrinaryLogic $result,
24: public readonly array $reasons,
25: )
26: {
27: }
28:
29: public function yes(): bool
30: {
31: return $this->result->yes();
32: }
33:
34: public function maybe(): bool
35: {
36: return $this->result->maybe();
37: }
38:
39: public function no(): bool
40: {
41: return $this->result->no();
42: }
43:
44: public static function createYes(): self
45: {
46: return new self(TrinaryLogic::createYes(), []);
47: }
48:
49: /**
50: * @param list<string> $reasons
51: */
52: public static function createNo(array $reasons = []): self
53: {
54: return new self(TrinaryLogic::createNo(), $reasons);
55: }
56:
57: public static function createMaybe(): self
58: {
59: return new self(TrinaryLogic::createMaybe(), []);
60: }
61:
62: public static function createFromBoolean(bool $value): self
63: {
64: return new self(TrinaryLogic::createFromBoolean($value), []);
65: }
66:
67: public function toAcceptsResult(): AcceptsResult
68: {
69: return new AcceptsResult($this->result, $this->reasons);
70: }
71:
72: public function and(self ...$others): self
73: {
74: $results = [];
75: $reasons = [];
76: foreach ($others as $other) {
77: $results[] = $other->result;
78: $reasons[] = $other->reasons;
79: }
80:
81: return new self(
82: $this->result->and(...$results),
83: array_values(array_unique(array_merge($this->reasons, ...$reasons))),
84: );
85: }
86:
87: public function or(self ...$others): self
88: {
89: $results = [];
90: $reasons = [];
91: foreach ($others as $other) {
92: $results[] = $other->result;
93: $reasons[] = $other->reasons;
94: }
95:
96: return new self(
97: $this->result->or(...$results),
98: array_values(array_unique(array_merge($this->reasons, ...$reasons))),
99: );
100: }
101:
102: /**
103: * @param callable(string): string $cb
104: */
105: public function decorateReasons(callable $cb): self
106: {
107: $reasons = [];
108: foreach ($this->reasons as $reason) {
109: $reasons[] = $cb($reason);
110: }
111:
112: return new self($this->result, $reasons);
113: }
114:
115: public static function extremeIdentity(self ...$operands): self
116: {
117: if ($operands === []) {
118: throw new ShouldNotHappenException();
119: }
120:
121: $result = TrinaryLogic::extremeIdentity(...array_map(static fn (self $result) => $result->result, $operands));
122:
123: return new self($result, self::mergeReasons($operands));
124: }
125:
126: public static function maxMin(self ...$operands): self
127: {
128: if ($operands === []) {
129: throw new ShouldNotHappenException();
130: }
131:
132: $result = TrinaryLogic::maxMin(...array_map(static fn (self $result) => $result->result, $operands));
133:
134: return new self($result, self::mergeReasons($operands));
135: }
136:
137: public function negate(): self
138: {
139: return new self($this->result->negate(), $this->reasons);
140: }
141:
142: public function describe(): string
143: {
144: return $this->result->describe();
145: }
146:
147: /**
148: * @param array<self> $operands
149: *
150: * @return list<string>
151: */
152: private static function mergeReasons(array $operands): array
153: {
154: $reasons = [];
155: foreach ($operands as $operand) {
156: foreach ($operand->reasons as $reason) {
157: $reasons[] = $reason;
158: }
159: }
160:
161: return $reasons;
162: }
163:
164: }
165: