Skip to content

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

Closed
predictor2718 wants to merge 2 commits intophpstan:2.1.xfrom
predictor2718:fix-12871
Closed

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

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

@phpstan-bot
Copy link
Copy Markdown
Collaborator

You've opened the pull request against the latest branch 2.2.x. PHPStan 2.2 is not going to be released for months. If your code is relevant on 2.1.x and you want it to be released sooner, please rebase your pull request and change its target to 2.1.x.

@predictor2718 predictor2718 changed the base branch from 2.2.x to 2.1.x April 28, 2026 19:41
@predictor2718
Copy link
Copy Markdown
Contributor Author

Closing in favour of a clean branch based directly on 2.1.x.

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.

Wrong "Access to undefined property" with chained isset() tests

2 participants