Fix #14207: slow analysis on return with a big OR condition (wordpress)#5076
Merged
ondrejmirtes merged 2 commits intophpstan:2.1.xfrom Feb 27, 2026
Merged
Conversation
- Changed BooleanOr falsey scope computation to avoid reprocessing the entire chain through TypeSpecifier at each level - Instead of filterByFalseyValue on the full expression, only filter by the right operand since the left's narrowing is already in the scope - Reduces O(N^2) work to O(N) for chains of N conditions - New regression test in tests/PHPStan/Analyser/nsrt/bug-14207.php - Updated bug-7156 test assertion for equivalent type description Closes phpstan/phpstan#14207
64495a4 to
850ddeb
Compare
Closes phpstan/phpstan#5984 Closes phpstan/phpstan#9400
Contributor
|
great to see that triggering the bot for this issue yielded a waaaay better fix than my PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes extremely slow analysis when a function returns a large
||(BooleanOr) condition chain, such as checking a variable against 80+ string constants (common in WordPress HTML tag checking code). Analysis time dropped from ~15 seconds to ~0.15 seconds for the reproducing case.Changes
src/Analyser/NodeScopeResolver.php: Changed the falsey scope callback forBooleanOr/LogicalOrprocessing from$rightResult->getScope()->filterByFalseyValue($expr)to$rightResult->getScope()->filterByFalseyValue($expr->right)tests/PHPStan/Analyser/nsrt/bug-14207.phpreproducing the WordPressis_special()pattern with ~80 OR conditionstests/PHPStan/Rules/Functions/data/bug-7156.phpassertion for semantically equivalent type description (non-empty-arrayvsnon-empty-array<mixed, mixed>)Root cause
When processing a left-associative chain like
A || B || C || ... || Z, at each level of the BooleanOr AST,NodeScopeResolvercalls$leftResult->getFalseyScope()to determine the scope for processing the right operand. This triggers the lazy falsey scope callback from the inner level, which calledfilterByFalseyValue($expr)on the entire inner BooleanOr chain.filterByFalseyValueinvokesTypeSpecifier::specifyTypesInCondition()which recursively processes the chain. This resulted in O(N^2) total work — at each of N levels, the TypeSpecifier reprocessed a chain of length up to N.The fix recognizes that
$rightResult->getScope()is already derived from$leftResult->getFalseyScope()(the right side was processed in the left's falsey scope), so the left chain's falsey narrowing is already baked into the scope. We only need to additionally filter by the right operand being false, not the entire expression. This reduces the work to O(N) — each level does O(1) work filtering by a single leaf expression.Test
Added
tests/PHPStan/Analyser/nsrt/bug-14207.phpwhich contains a class with a method returning a chain of ~80||string comparisons (modeled after WordPress'sWP_HTML_Processor::is_special()). Without the fix, this test takes ~15 seconds; with the fix, it completes in ~0.15 seconds.Fixes phpstan/phpstan#14207