Skip to content

Fix phpstan/phpstan#14269: Regression: maybe undefined variable#5208

Merged
ondrejmirtes merged 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-gzmqv8e
Mar 12, 2026
Merged

Fix phpstan/phpstan#14269: Regression: maybe undefined variable#5208
ondrejmirtes merged 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-gzmqv8e

Conversation

@phpstan-bot
Copy link
Collaborator

@phpstan-bot phpstan-bot commented Mar 12, 2026

Summary

Fixes a regression introduced in #5125 (fix for phpstan/phpstan#14117) where a variable assigned conditionally before a foreach loop was incorrectly reported as "might not be defined" inside the loop, even when the same condition was re-checked.

Changes

  • Modified intersectConditionalExpressions() in src/Analyser/MutatingScope.php to keep only the conditional expression keys that are present in both scopes, instead of dropping ALL keys for a variable when any single key is missing from the other scope
  • Added regression test tests/PHPStan/Analyser/nsrt/bug-14269.php

Root cause

The fix for phpstan/phpstan#14117 added logic in createConditionalExpressions() to keep variables with same type but different certainty in the candidate set. This correctly creates conditional expressions for the certainty difference, but as a side effect, it also creates spurious conditional expressions based on unrelated type guards (e.g., $availableIntervals type was used as a guard for $packageDurationInMinutes certainty).

During subsequent loop iteration scope merges, intersectConditionalExpressions() required ALL conditional expression keys for a variable to be present in both scopes. When one scope had the spurious key and the other didn't, the method dropped ALL conditional expressions for that variable — including the correct ones based on the actual condition (isMultiDay()). This caused the variable to lose its conditional certainty tracking, resulting in a false "might not be defined" error.

The fix changes intersectConditionalExpressions() to keep only the keys that ARE in both scopes (a true intersection), rather than the previous all-or-nothing behavior.

Test

Added tests/PHPStan/Analyser/nsrt/bug-14269.php which reproduces the exact scenario from the issue: a variable assigned inside if (!$package->isMultiDay()), then checked inside a foreach loop under the same condition, with an unrelated variable assignment ($availableIntervals = $s->subtract()) that triggers the bug.

Fixes phpstan/phpstan#14269
Fixes phpstan/phpstan#4284

…nments

- Fixed intersectConditionalExpressions to keep matching conditional expression
  keys instead of dropping all keys when any single key mismatches
- The previous all-or-nothing behavior caused conditional expressions (e.g. for
  variable certainty based on method return values) to be entirely dropped when
  an unrelated conditional expression key was added during loop scope merging
- New regression test in tests/PHPStan/Analyser/nsrt/bug-14269.php

Closes phpstan/phpstan#14269
Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a regression test for the snippet https://phpstan.org/r/dc24e5cb-ca3c-45f5-a49e-7425604e7ac9 from phpstan/phpstan#4284.

I want to check "If condition is always true" on line 23 so IfConstantConditionRuleTest.

@phpstan-bot
Copy link
Collaborator Author

I processed this review but have nothing to report.

1 similar comment
@phpstan-bot
Copy link
Collaborator Author

I processed this review but have nothing to report.

@ondrejmirtes ondrejmirtes merged commit 63cb407 into phpstan:2.1.x Mar 12, 2026
122 checks passed
@ondrejmirtes ondrejmirtes deleted the create-pull-request/patch-gzmqv8e branch March 12, 2026 07:36
@phpstan-bot
Copy link
Collaborator Author

I processed this review but have nothing to report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants