Skip to content

Fix phpstan/phpstan#7088: Accessing SimpleXMLElement nodes using braces gives mixed type#5406

Open
phpstan-bot wants to merge 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-2l111d3
Open

Fix phpstan/phpstan#7088: Accessing SimpleXMLElement nodes using braces gives mixed type#5406
phpstan-bot wants to merge 2 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-2l111d3

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

Accessing SimpleXMLElement properties using dynamic names ($prop->{$x}) was incorrectly resolved as mixed instead of SimpleXMLElement|null. Static property names ($prop->foo) and constant string expressions ($prop->{'foo-bar'}) already worked correctly.

Changes

  • Added fallback logic in src/Analyser/ExprHandler/PropertyFetchHandler.php for non-constant string property names: when the name is a string type but not a constant string, query the var type's property reflection to determine the type
  • New regression test in tests/PHPStan/Analyser/nsrt/bug-7088.php

Root cause

In PropertyFetchHandler::resolveType(), when the property name expression is not an Identifier and not a constant string, the code immediately returned MixedType. For classes like SimpleXMLElement that have a PropertiesClassReflectionExtension accepting any property name, we can still determine the correct type by querying the property reflection with a representative property name.

Test

Added tests/PHPStan/Analyser/nsrt/bug-7088.php that verifies all three property access patterns on SimpleXMLElement resolve to (SimpleXMLElement|null):

  • $prop->foo (static name)
  • $prop->{'foo-bar'} (constant string expression)
  • $prop->{$x} (dynamic variable name)

Fixes phpstan/phpstan#7088

…turns mixed

- When accessing SimpleXMLElement properties via $prop->{$x} with a non-constant
  string name, the type was incorrectly resolved as mixed instead of SimpleXMLElement|null
- Added fallback in PropertyFetchHandler::resolveType() to query the var type's
  property reflection for non-constant string property names
- New regression test in tests/PHPStan/Analyser/nsrt/bug-7088.php
Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

What about passing null as propertyName rather than a custom constant __phpstan_dynamic_property

Edit: The method in Type interface requires a string

@VincentLanglet VincentLanglet force-pushed the create-pull-request/patch-2l111d3 branch from 9e33578 to 721d265 Compare April 6, 2026 09:22
@phpstan phpstan deleted a comment from phpstan-bot Apr 6, 2026
@VincentLanglet
Copy link
Copy Markdown
Contributor

VincentLanglet commented Apr 6, 2026

While using a hardcoded constant __phpstan_dynamic_property is not the best, I don't see another easy way without BC break (and lot of changes) just to have hasInstanceProperty() method accepting NULL.

I think it's good enough but we might prefer ask validation on ondrej. WDYT ?

@VincentLanglet VincentLanglet self-assigned this Apr 6, 2026
@VincentLanglet VincentLanglet requested a review from staabm April 6, 2026 09:39

if ($nameType->isString()->yes()) {
$fetchedOnType = $scope->getType($expr->var);
$returnType = $this->propertyFetchType($scope, $fetchedOnType, '__phpstan_dynamic_property', $expr);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is this magic __phpstan_dynamic_property constant about? I don't get why we need it.
if we need it, it might make sense to extract it into a private constant and give it a name, which makes its use-case obvious.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Extracted the magic string '__phpstan_dynamic_property' into a private constant DYNAMIC_PROPERTY_NAME with a docblock explaining its purpose: it's a representative property name used when resolving dynamic property access ($obj->{$expr}), so that PropertiesClassReflectionExtension implementations (like SimpleXMLElement) that accept any property name can return the correct type. Both make phpstan and the bug-7088 test pass.

Address review feedback: the magic string '__phpstan_dynamic_property'
is now a private constant with a docblock explaining its purpose.
It serves as a representative property name for dynamic property access
resolution ($obj->{$expr}), allowing PropertiesClassReflectionExtensions
that accept any property name to return the correct type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

3 participants