Skip to content

Fix phpstan/phpstan#13539: property.notFound in chained isset() with checkDynamicProperties#5562

Open
predictor2718 wants to merge 3 commits intophpstan:2.1.xfrom
predictor2718:fix-13539
Open

Fix phpstan/phpstan#13539: property.notFound in chained isset() with checkDynamicProperties#5562
predictor2718 wants to merge 3 commits intophpstan:2.1.xfrom
predictor2718:fix-13539

Conversation

@predictor2718
Copy link
Copy Markdown
Contributor

Summary

When checkDynamicProperties: true (enabled by phpstan-strict-rules), chained isset() calls on a mixed variable incorrectly reported property.notFound for the second check.

function broken(string $x): void {
    $tmp = json_decode($x, false);
    if (!isset($tmp->foo) || !isset($tmp->bar)) { // false positive on $tmp->bar
    }
}

Root cause

isset($tmp->foo) narrows mixed to object&hasProperty(foo). When evaluating isset($tmp->bar), PHPStan sees object&hasProperty(foo) and calls hasInstanceProperty('bar')maybe. In AccessPropertiesCheck, the maybe branch has an early return for isset() context (isUndefinedExpressionAllowed), but only when checkDynamicProperties is false. With checkDynamicProperties: true, it falls through to pickProperty, which returns null for generic object types (no concrete class), and then falls through to the property.notFound error.

Fix

After pickProperty returns null, check whether the type has any known class names (getObjectClassNames() === []). A generic object or object&hasProperty(...) has no class names — the property could genuinely exist — so the error is suppressed in isset() context. Concrete classes (e.g. FinalHelloWorld) still trigger the error as before.

Test

Regression fixture at tests/PHPStan/Rules/Properties/data/bug-13539.php, covering the playground snippet from the issue. Test runs with checkDynamicProperties: true.

Fixes phpstan/phpstan#13539

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