Skip to content

Fix phpstan/phpstan#14353: Falsy "Variable might not be defined"#5280

Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-mykfjvs
Open

Fix phpstan/phpstan#14353: Falsy "Variable might not be defined"#5280
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-mykfjvs

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

False positive "Variable $report_ids might not be defined" reported inside an isset() block after an unrelated foreach loop over the same array that was used to conditionally define the variable.

Changes

  • Modified src/Analyser/MutatingScope.php in specifyExpressionType(): when certainty is set to Yes, remove conditional expressions for that variable where the type holder has No certainty (i.e., "variable doesn't exist" conditions)
  • Added regression test in tests/PHPStan/Rules/Variables/data/bug-14353.php
  • Added test method testBug14353 in tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php

Root cause

After a foreach like foreach ($reports as $v) { $report_ids[$v] = 1; }, the scope merge creates conditional expressions tracking the relationship: "if $reports is empty (the loop didn't execute), then $report_ids doesn't exist."

When isset($report_ids) later confirms the variable exists, specifyExpressionType sets certainty to Yes but does not clean up these stale conditional expressions. A subsequent foreach ($reports as $v) {} triggers filterByTruthyValue for the "array is empty" case, which activates the stale conditional expression and removes $report_ids from the scope. The merge then downgrades $report_ids certainty from Yes to Maybe, causing the false positive.

The fix removes No-certainty conditional expressions for a variable when its certainty is explicitly set to Yes, since confirming a variable exists invalidates any conditional that says it doesn't.

Test

Added a rule test reproducing the exact scenario from the issue: a variable defined inside both branches of an if/else (each containing a foreach), checked with isset(), then accessed after an unrelated foreach loop. The test expects no errors.

Fixes phpstan/phpstan#14353

…r foreach

- After a foreach that conditionally defines a variable, conditional expressions
  are created tracking "if array is empty, variable doesn't exist"
- When isset() confirms the variable exists, specifyExpressionType sets certainty
  to Yes but stale conditional expressions with No certainty persist
- A subsequent foreach over the same array triggers filterByTruthyValue for the
  "array is empty" case, activating the stale conditional and removing the variable
- Fix: in specifyExpressionType, when certainty is Yes, remove conditional
  expressions for the variable that have No certainty (would unset it)
- New regression test in tests/PHPStan/Rules/Variables/data/bug-14353.php
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.

1 participant