diff --git a/src/Type/AcceptsResult.php b/src/Type/AcceptsResult.php index 1690d7b90ce..87afd50dcae 100644 --- a/src/Type/AcceptsResult.php +++ b/src/Type/AcceptsResult.php @@ -152,4 +152,37 @@ public static function maxMin(self ...$operands): self return new self(TrinaryLogic::maxMin(...$results), array_values(array_unique($reasons))); } + /** + * @template T + * @param T[] $objects + * @param callable(T): self $callback + */ + public static function lazyMaxMin( + array $objects, + callable $callback, + ): self + { + $results = []; + $reasons = []; + $hasNo = false; + foreach ($objects as $object) { + $isAcceptedBy = $callback($object); + if ($isAcceptedBy->result->yes()) { + return $isAcceptedBy; + } elseif ($isAcceptedBy->result->no()) { + $hasNo = true; + } + $results[] = $isAcceptedBy; + + foreach ($isAcceptedBy->reasons as $reason) { + $reasons[] = $reason; + } + } + + return new self( + $hasNo ? TrinaryLogic::createNo() : TrinaryLogic::createMaybe(), + array_values(array_unique($reasons)), + ); + } + } diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 06e3d781ef2..bd203c08bd8 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -266,7 +266,11 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult return $otherType->isSuperTypeOf($this); } - $result = IsSuperTypeOfResult::maxMin(...array_map(static fn (Type $innerType) => $otherType->isSuperTypeOf($innerType), $this->types)); + $result = IsSuperTypeOfResult::lazyMaxMin( + $this->types, + static fn (Type $innerType) => $otherType->isSuperTypeOf($innerType), + ); + if ( !$result->no() && $this->isOversizedArray()->yes() @@ -280,7 +284,11 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult { - $result = AcceptsResult::maxMin(...array_map(static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes), $this->types)); + $result = AcceptsResult::lazyMaxMin( + $this->types, + static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes), + ); + if ($this->isOversizedArray()->yes()) { if (!$result->no()) { return AcceptsResult::createYes(); diff --git a/src/Type/IsSuperTypeOfResult.php b/src/Type/IsSuperTypeOfResult.php index b0826f518cf..0c4fb92a50c 100644 --- a/src/Type/IsSuperTypeOfResult.php +++ b/src/Type/IsSuperTypeOfResult.php @@ -185,6 +185,39 @@ public static function maxMin(self ...$operands): self return new self(TrinaryLogic::maxMin(...$results), array_values(array_unique($reasons))); } + /** + * @template T + * @param T[] $objects + * @param callable(T): self $callback + */ + public static function lazyMaxMin( + array $objects, + callable $callback, + ): self + { + $results = []; + $reasons = []; + $hasNo = false; + foreach ($objects as $object) { + $isSuperTypeOf = $callback($object); + if ($isSuperTypeOf->result->yes()) { + return $isSuperTypeOf; + } elseif ($isSuperTypeOf->result->no()) { + $hasNo = true; + } + $results[] = $isSuperTypeOf; + + foreach ($isSuperTypeOf->reasons as $reason) { + $reasons[] = $reason; + } + } + + return new self( + $hasNo ? TrinaryLogic::createNo() : TrinaryLogic::createMaybe(), + array_values(array_unique($reasons)), + ); + } + public function negate(): self { return new self($this->result->negate(), $this->reasons);