1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan; |
4: | |
5: | use PHPStan\Type\BooleanType; |
6: | use PHPStan\Type\Constant\ConstantBooleanType; |
7: | use function array_column; |
8: | use function max; |
9: | use function min; |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | class TrinaryLogic |
16: | { |
17: | |
18: | private const YES = 1; |
19: | private const MAYBE = 0; |
20: | private const NO = -1; |
21: | |
22: | |
23: | private static array $registry = []; |
24: | |
25: | private function __construct(private int $value) |
26: | { |
27: | } |
28: | |
29: | public static function createYes(): self |
30: | { |
31: | return self::$registry[self::YES] ??= new self(self::YES); |
32: | } |
33: | |
34: | public static function createNo(): self |
35: | { |
36: | return self::$registry[self::NO] ??= new self(self::NO); |
37: | } |
38: | |
39: | public static function createMaybe(): self |
40: | { |
41: | return self::$registry[self::MAYBE] ??= new self(self::MAYBE); |
42: | } |
43: | |
44: | public static function createFromBoolean(bool $value): self |
45: | { |
46: | $yesNo = $value ? self::YES : self::NO; |
47: | return self::$registry[$yesNo] ??= new self($yesNo); |
48: | } |
49: | |
50: | private static function create(int $value): self |
51: | { |
52: | self::$registry[$value] ??= new self($value); |
53: | return self::$registry[$value]; |
54: | } |
55: | |
56: | public function yes(): bool |
57: | { |
58: | return $this->value === self::YES; |
59: | } |
60: | |
61: | public function maybe(): bool |
62: | { |
63: | return $this->value === self::MAYBE; |
64: | } |
65: | |
66: | public function no(): bool |
67: | { |
68: | return $this->value === self::NO; |
69: | } |
70: | |
71: | public function toBooleanType(): BooleanType |
72: | { |
73: | if ($this->value === self::MAYBE) { |
74: | return new BooleanType(); |
75: | } |
76: | |
77: | return new ConstantBooleanType($this->value === self::YES); |
78: | } |
79: | |
80: | public function and(self ...$operands): self |
81: | { |
82: | $operandValues = array_column($operands, 'value'); |
83: | $operandValues[] = $this->value; |
84: | return self::create(min($operandValues)); |
85: | } |
86: | |
87: | |
88: | |
89: | |
90: | |
91: | |
92: | public function lazyAnd( |
93: | array $objects, |
94: | callable $callback, |
95: | ): self |
96: | { |
97: | $results = []; |
98: | foreach ($objects as $object) { |
99: | $result = $callback($object); |
100: | if ($result->no()) { |
101: | return $result; |
102: | } |
103: | |
104: | $results[] = $result; |
105: | } |
106: | |
107: | return $this->and(...$results); |
108: | } |
109: | |
110: | public function or(self ...$operands): self |
111: | { |
112: | $operandValues = array_column($operands, 'value'); |
113: | $operandValues[] = $this->value; |
114: | return self::create(max($operandValues)); |
115: | } |
116: | |
117: | |
118: | |
119: | |
120: | |
121: | |
122: | public function lazyOr( |
123: | array $objects, |
124: | callable $callback, |
125: | ): self |
126: | { |
127: | $results = []; |
128: | foreach ($objects as $object) { |
129: | $result = $callback($object); |
130: | if ($result->yes()) { |
131: | return $result; |
132: | } |
133: | |
134: | $results[] = $result; |
135: | } |
136: | |
137: | return $this->or(...$results); |
138: | } |
139: | |
140: | public static function extremeIdentity(self ...$operands): self |
141: | { |
142: | if ($operands === []) { |
143: | throw new ShouldNotHappenException(); |
144: | } |
145: | $operandValues = array_column($operands, 'value'); |
146: | $min = min($operandValues); |
147: | $max = max($operandValues); |
148: | return self::create($min === $max ? $min : self::MAYBE); |
149: | } |
150: | |
151: | |
152: | |
153: | |
154: | |
155: | |
156: | public static function lazyExtremeIdentity( |
157: | array $objects, |
158: | callable $callback, |
159: | ): self |
160: | { |
161: | $lastResult = null; |
162: | $results = []; |
163: | foreach ($objects as $object) { |
164: | $result = $callback($object); |
165: | if ($lastResult === null) { |
166: | $lastResult = $result; |
167: | $results[] = $result; |
168: | continue; |
169: | } |
170: | if ($lastResult->equals($result)) { |
171: | $results[] = $result; |
172: | continue; |
173: | } |
174: | |
175: | return self::createMaybe(); |
176: | } |
177: | |
178: | return self::extremeIdentity(...$results); |
179: | } |
180: | |
181: | public static function maxMin(self ...$operands): self |
182: | { |
183: | if ($operands === []) { |
184: | throw new ShouldNotHappenException(); |
185: | } |
186: | $operandValues = array_column($operands, 'value'); |
187: | return self::create(max($operandValues) > 0 ? 1 : min($operandValues)); |
188: | } |
189: | |
190: | |
191: | |
192: | |
193: | |
194: | |
195: | public static function lazyMaxMin( |
196: | array $objects, |
197: | callable $callback, |
198: | ): self |
199: | { |
200: | $results = []; |
201: | foreach ($objects as $object) { |
202: | $result = $callback($object); |
203: | if ($result->yes()) { |
204: | return $result; |
205: | } |
206: | |
207: | $results[] = $result; |
208: | } |
209: | |
210: | return self::maxMin(...$results); |
211: | } |
212: | |
213: | public function negate(): self |
214: | { |
215: | return self::create(-$this->value); |
216: | } |
217: | |
218: | public function equals(self $other): bool |
219: | { |
220: | return $this === $other; |
221: | } |
222: | |
223: | public function compareTo(self $other): ?self |
224: | { |
225: | if ($this->value > $other->value) { |
226: | return $this; |
227: | } elseif ($other->value > $this->value) { |
228: | return $other; |
229: | } |
230: | |
231: | return null; |
232: | } |
233: | |
234: | public function describe(): string |
235: | { |
236: | static $labels = [ |
237: | self::NO => 'No', |
238: | self::MAYBE => 'Maybe', |
239: | self::YES => 'Yes', |
240: | ]; |
241: | |
242: | return $labels[$this->value]; |
243: | } |
244: | |
245: | |
246: | |
247: | |
248: | public static function __set_state(array $properties): self |
249: | { |
250: | return self::create($properties['value']); |
251: | } |
252: | |
253: | } |
254: | |