Skip to content

Commit f8f6e2a

Browse files
committed
TrinaryLogic - lazy evaluation methods
1 parent 493284c commit f8f6e2a

10 files changed

Lines changed: 163 additions & 89 deletions

src/Reflection/Type/IntersectionTypeMethodReflection.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function getVariants(): array
8787

8888
public function isDeprecated(): TrinaryLogic
8989
{
90-
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated(), $this->methods));
90+
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated());
9191
}
9292

9393
public function getDeprecatedDescription(): ?string
@@ -114,12 +114,12 @@ public function getDeprecatedDescription(): ?string
114114

115115
public function isFinal(): TrinaryLogic
116116
{
117-
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isFinal(), $this->methods));
117+
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isFinal());
118118
}
119119

120120
public function isInternal(): TrinaryLogic
121121
{
122-
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isInternal(), $this->methods));
122+
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isInternal());
123123
}
124124

125125
public function getThrowType(): ?Type
@@ -144,7 +144,7 @@ public function getThrowType(): ?Type
144144

145145
public function hasSideEffects(): TrinaryLogic
146146
{
147-
return TrinaryLogic::maxMin(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects(), $this->methods));
147+
return TrinaryLogic::lazyMaxMin($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects());
148148
}
149149

150150
public function getDocComment(): ?string

src/Reflection/Type/IntersectionTypePropertyReflection.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function isPublic(): bool
6161

6262
public function isDeprecated(): TrinaryLogic
6363
{
64-
return TrinaryLogic::maxMin(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isDeprecated(), $this->properties));
64+
return TrinaryLogic::lazyMaxMin($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isDeprecated());
6565
}
6666

6767
public function getDeprecatedDescription(): ?string
@@ -88,7 +88,7 @@ public function getDeprecatedDescription(): ?string
8888

8989
public function isInternal(): TrinaryLogic
9090
{
91-
return TrinaryLogic::maxMin(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isInternal(), $this->properties));
91+
return TrinaryLogic::lazyMaxMin($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isInternal());
9292
}
9393

9494
public function getDocComment(): ?string

src/Reflection/Type/UnionTypeMethodReflection.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function getVariants(): array
8181

8282
public function isDeprecated(): TrinaryLogic
8383
{
84-
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated(), $this->methods));
84+
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isDeprecated());
8585
}
8686

8787
public function getDeprecatedDescription(): ?string
@@ -108,12 +108,12 @@ public function getDeprecatedDescription(): ?string
108108

109109
public function isFinal(): TrinaryLogic
110110
{
111-
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isFinal(), $this->methods));
111+
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isFinal());
112112
}
113113

114114
public function isInternal(): TrinaryLogic
115115
{
116-
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->isInternal(), $this->methods));
116+
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->isInternal());
117117
}
118118

119119
public function getThrowType(): ?Type
@@ -138,7 +138,7 @@ public function getThrowType(): ?Type
138138

139139
public function hasSideEffects(): TrinaryLogic
140140
{
141-
return TrinaryLogic::extremeIdentity(...array_map(static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects(), $this->methods));
141+
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (MethodReflection $method): TrinaryLogic => $method->hasSideEffects());
142142
}
143143

144144
public function getDocComment(): ?string

src/Reflection/Type/UnionTypePropertyReflection.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function isPublic(): bool
6161

6262
public function isDeprecated(): TrinaryLogic
6363
{
64-
return TrinaryLogic::extremeIdentity(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isDeprecated(), $this->properties));
64+
return TrinaryLogic::lazyExtremeIdentity($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isDeprecated());
6565
}
6666

6767
public function getDeprecatedDescription(): ?string
@@ -88,7 +88,7 @@ public function getDeprecatedDescription(): ?string
8888

8989
public function isInternal(): TrinaryLogic
9090
{
91-
return TrinaryLogic::extremeIdentity(...array_map(static fn (PropertyReflection $property): TrinaryLogic => $property->isInternal(), $this->properties));
91+
return TrinaryLogic::lazyExtremeIdentity($this->properties, static fn (PropertyReflection $propertyReflection): TrinaryLogic => $propertyReflection->isInternal());
9292
}
9393

9494
public function getDocComment(): ?string

src/TrinaryLogic.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,59 @@ public function and(self ...$operands): self
8484
return self::create(min($operandValues));
8585
}
8686

87+
/**
88+
* @template T
89+
* @param T[] $objects
90+
* @param callable(T): self $callback
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+
87110
public function or(self ...$operands): self
88111
{
89112
$operandValues = array_column($operands, 'value');
90113
$operandValues[] = $this->value;
91114
return self::create(max($operandValues));
92115
}
93116

117+
/**
118+
* @template T
119+
* @param T[] $objects
120+
* @param callable(T): self $callback
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+
94140
public static function extremeIdentity(self ...$operands): self
95141
{
96142
if ($operands === []) {
@@ -102,6 +148,36 @@ public static function extremeIdentity(self ...$operands): self
102148
return self::create($min === $max ? $min : self::MAYBE);
103149
}
104150

151+
/**
152+
* @template T
153+
* @param T[] $objects
154+
* @param callable(T): self $callback
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+
105181
public static function maxMin(self ...$operands): self
106182
{
107183
if ($operands === []) {
@@ -111,6 +187,29 @@ public static function maxMin(self ...$operands): self
111187
return self::create(max($operandValues) > 0 ? 1 : min($operandValues));
112188
}
113189

190+
/**
191+
* @template T
192+
* @param T[] $objects
193+
* @param callable(T): self $callback
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+
114213
public function negate(): self
115214
{
116215
return self::create(-$this->value);

src/Type/BenevolentUnionType.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PHPStan\TrinaryLogic;
66
use PHPStan\Type\Generic\TemplateTypeMap;
7-
use function array_map;
87
use function count;
98

109
/** @api */
@@ -46,17 +45,12 @@ protected function unionTypes(callable $getType): Type
4645

4746
protected function unionResults(callable $getResult): TrinaryLogic
4847
{
49-
return TrinaryLogic::createNo()->or(...array_map($getResult, $this->getTypes()));
48+
return TrinaryLogic::createNo()->lazyOr($this->getTypes(), $getResult);
5049
}
5150

5251
public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
5352
{
54-
$results = [];
55-
foreach ($this->getTypes() as $innerType) {
56-
$results[] = $acceptingType->accepts($innerType, $strictTypes);
57-
}
58-
59-
return TrinaryLogic::createNo()->or(...$results);
53+
return TrinaryLogic::createNo()->lazyOr($this->getTypes(), static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes));
6054
}
6155

6256
public function inferTemplateTypes(Type $receivedType): TemplateTypeMap

src/Type/Constant/ConstantArrayType.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -495,12 +495,7 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
495495
{
496496
$offsetType = ArrayType::castToArrayKeyType($offsetType);
497497
if ($offsetType instanceof UnionType) {
498-
$results = [];
499-
foreach ($offsetType->getTypes() as $innerType) {
500-
$results[] = $this->hasOffsetValueType($innerType);
501-
}
502-
503-
return TrinaryLogic::extremeIdentity(...$results);
498+
return TrinaryLogic::lazyExtremeIdentity($offsetType->getTypes(), fn (Type $innerType) => $this->hasOffsetValueType($innerType));
504499
}
505500

506501
$result = TrinaryLogic::createNo();

src/Type/IntersectionType.php

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,7 @@ public function isSuperTypeOf(Type $otherType): TrinaryLogic
144144
return TrinaryLogic::createYes();
145145
}
146146

147-
$results = [];
148-
foreach ($this->getTypes() as $innerType) {
149-
$result = $innerType->isSuperTypeOf($otherType);
150-
if ($result->no()) {
151-
return $result;
152-
}
153-
154-
$results[] = $result;
155-
}
156-
157-
return TrinaryLogic::createYes()->and(...$results);
147+
return TrinaryLogic::createYes()->lazyAnd($this->getTypes(), static fn (Type $innerType) => $innerType->isSuperTypeOf($otherType));
158148
}
159149

160150
public function isSubTypeOf(Type $otherType): TrinaryLogic
@@ -163,31 +153,12 @@ public function isSubTypeOf(Type $otherType): TrinaryLogic
163153
return $otherType->isSuperTypeOf($this);
164154
}
165155

166-
$results = [];
167-
foreach ($this->getTypes() as $innerType) {
168-
$result = $otherType->isSuperTypeOf($innerType);
169-
if ($result->yes()) {
170-
return $result;
171-
}
172-
$results[] = $result;
173-
}
174-
175-
return TrinaryLogic::maxMin(...$results);
156+
return TrinaryLogic::lazyMaxMin($this->getTypes(), static fn (Type $innerType) => $otherType->isSuperTypeOf($innerType));
176157
}
177158

178159
public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
179160
{
180-
$results = [];
181-
foreach ($this->getTypes() as $innerType) {
182-
$result = $acceptingType->accepts($innerType, $strictTypes);
183-
if ($result->yes()) {
184-
return $result;
185-
}
186-
187-
$results[] = $result;
188-
}
189-
190-
return TrinaryLogic::maxMin(...$results);
161+
return TrinaryLogic::lazyMaxMin($this->getTypes(), static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes));
191162
}
192163

193164
public function equals(Type $type): bool
@@ -669,8 +640,7 @@ public static function __set_state(array $properties): Type
669640
*/
670641
private function intersectResults(callable $getResult): TrinaryLogic
671642
{
672-
$operands = array_map($getResult, $this->types);
673-
return TrinaryLogic::maxMin(...$operands);
643+
return TrinaryLogic::lazyMaxMin($this->types, $getResult);
674644
}
675645

676646
/**

0 commit comments

Comments
 (0)