Skip to content

Commit

Permalink
Optimize match expression with many conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Nov 26, 2023
1 parent 4591b1e commit c238fe7
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 17 deletions.
23 changes: 14 additions & 9 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -1490,16 +1490,21 @@ private function resolveType(string $exprString, Expr $node): Type
throw new ShouldNotHappenException();
}

$filteringExpr = null;
foreach ($arm->conds as $armCond) {
$armCondExpr = new BinaryOp\Identical($cond, $armCond);

if ($filteringExpr === null) {
$filteringExpr = $armCondExpr;
continue;
if (count($arm->conds) === 1) {
$filteringExpr = new BinaryOp\Identical($cond, $arm->conds[0]);
} else {
$items = [];
foreach ($arm->conds as $filteringExpr) {
$items[] = new Expr\ArrayItem($filteringExpr);
}

$filteringExpr = new BinaryOp\BooleanOr($filteringExpr, $armCondExpr);
$filteringExpr = new FuncCall(
new Name\FullyQualified('in_array'),
[
new Arg($cond),
new Arg(new Array_($items)),
new Arg(new ConstFetch(new Name\FullyQualified('true'))),
],
);
}

$filteringExprType = $matchScope->getType($filteringExpr);
Expand Down
24 changes: 18 additions & 6 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
throw new ShouldNotHappenException();
}

$filteringExpr = null;
$filteringExprs = [];
$armCondScope = $matchScope;
$condNodes = [];
foreach ($arm->conds as $armCond) {
Expand All @@ -2864,12 +2864,24 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
$hasAlwaysTrueCond = true;
}
$armCondScope = $armCondResult->getScope()->filterByFalseyValue($armCondExpr);
if ($filteringExpr === null) {
$filteringExpr = $armCondExpr;
continue;
}
$filteringExprs[] = $armCond;
}

$filteringExpr = new BinaryOp\BooleanOr($filteringExpr, $armCondExpr);
if (count($filteringExprs) === 1) {
$filteringExpr = new BinaryOp\Identical($expr->cond, $filteringExprs[0]);
} else {
$items = [];
foreach ($filteringExprs as $filteringExpr) {
$items[] = new ArrayItem($filteringExpr);
}
$filteringExpr = new FuncCall(
new Name\FullyQualified('in_array'),
[
new Arg($expr->cond),
new Arg(new Array_($items)),
new Arg(new ConstFetch(new Name\FullyQualified('true'))),
],
);
}

$bodyScope = $matchScope->filterByTruthyValue($filteringExpr);
Expand Down
15 changes: 13 additions & 2 deletions src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Node\Expr\AlwaysRememberedExpr;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
Expand Down Expand Up @@ -47,7 +48,8 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
return new SpecifiedTypes();
}

$needleType = $scope->getType($node->getArgs()[0]->value);
$needleExpr = $node->getArgs()[0]->value;
$needleType = $scope->getType($needleExpr);
$arrayType = $scope->getType($node->getArgs()[1]->value);
$arrayValueType = $arrayType->getIterableValueType();
$isStrictComparison = $isStrictComparison
Expand All @@ -71,12 +73,21 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
)
) {
$specifiedTypes = $this->typeSpecifier->create(
$node->getArgs()[0]->value,
$needleExpr,
$arrayValueType,
$context,
false,
$scope,
);
if ($needleExpr instanceof AlwaysRememberedExpr) {
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
$needleExpr->getExpr(),
$arrayValueType,
$context,
false,
$scope,
));
}
}

if (
Expand Down

0 comments on commit c238fe7

Please sign in to comment.