From 9adae6c4f8c4797c8e60353430ba9a66dc897bb7 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 8 Aug 2023 10:58:40 +0200 Subject: [PATCH] Fix performance problem with nested BooleanOr and BooleanAnd --- src/Analyser/MutatingScope.php | 40 +++- .../Analyser/AnalyserIntegrationTest.php | 6 + tests/PHPStan/Analyser/data/bug-9690.php | 174 ++++++++++++++++++ 3 files changed, 212 insertions(+), 8 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-9690.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 7aa5eb338e..77d0e1286c 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -143,6 +143,8 @@ class MutatingScope implements Scope { + private const BOOLEAN_EXPRESSION_MAX_PROCESS_DEPTH = 4; + /** @var Type[] */ private array $resolvedTypes = []; @@ -768,10 +770,14 @@ private function resolveType(string $exprString, Expr $node): Type return new ConstantBooleanType(false); } - $noopCallback = static function (): void { - }; - $leftResult = $this->nodeScopeResolver->processExprNode($node->left, $this, $noopCallback, ExpressionContext::createDeep()); - $rightBooleanType = $leftResult->getTruthyScope()->getType($node->right)->toBoolean(); + if ($this->getBooleanExpressionDepth($node->left) <= self::BOOLEAN_EXPRESSION_MAX_PROCESS_DEPTH) { + $noopCallback = static function (): void { + }; + $leftResult = $this->nodeScopeResolver->processExprNode($node->left, $this, $noopCallback, ExpressionContext::createDeep()); + $rightBooleanType = $leftResult->getTruthyScope()->getType($node->right)->toBoolean(); + } else { + $rightBooleanType = $this->filterByTruthyValue($node->left)->getType($node->right)->toBoolean(); + } if ($rightBooleanType->isFalse()->yes()) { return new ConstantBooleanType(false); @@ -796,10 +802,14 @@ private function resolveType(string $exprString, Expr $node): Type return new ConstantBooleanType(true); } - $noopCallback = static function (): void { - }; - $leftResult = $this->nodeScopeResolver->processExprNode($node->left, $this, $noopCallback, ExpressionContext::createDeep()); - $rightBooleanType = $leftResult->getFalseyScope()->getType($node->right)->toBoolean(); + if ($this->getBooleanExpressionDepth($node->left) <= self::BOOLEAN_EXPRESSION_MAX_PROCESS_DEPTH) { + $noopCallback = static function (): void { + }; + $leftResult = $this->nodeScopeResolver->processExprNode($node->left, $this, $noopCallback, ExpressionContext::createDeep()); + $rightBooleanType = $leftResult->getFalseyScope()->getType($node->right)->toBoolean(); + } else { + $rightBooleanType = $this->filterByFalseyValue($node->left)->getType($node->right)->toBoolean(); + } if ($rightBooleanType->isTrue()->yes()) { return new ConstantBooleanType(true); @@ -4707,6 +4717,20 @@ private function compareVariableTypeHolders(array $variableTypeHolders, array $o return true; } + private function getBooleanExpressionDepth(Expr $expr, int $depth = 0): int + { + while ( + $expr instanceof BinaryOp\BooleanOr + || $expr instanceof BinaryOp\LogicalOr + || $expr instanceof BinaryOp\BooleanAnd + || $expr instanceof BinaryOp\LogicalAnd + ) { + return $this->getBooleanExpressionDepth($expr->left, $depth + 1); + } + + return $depth; + } + /** @api */ public function canAccessProperty(PropertyReflection $propertyReflection): bool { diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 53e7ae85ba..8d67aea477 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1217,6 +1217,12 @@ public function testBug9428(): void $this->assertNoErrors($errors); } + public function testBug9690(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-9690.php'); + $this->assertNoErrors($errors); + } + /** * @param string[]|null $allAnalysedFiles * @return Error[] diff --git a/tests/PHPStan/Analyser/data/bug-9690.php b/tests/PHPStan/Analyser/data/bug-9690.php new file mode 100644 index 0000000000..547285c2cb --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-9690.php @@ -0,0 +1,174 @@ +