Skip to content

Fix #11923: Match class-type#5060

Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-7i6olsk
Closed

Fix #11923: Match class-type#5060
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-7i6olsk

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When using a match expression with a ternary subject like match ($request ? $request::class : null), PHPStan failed to narrow $request's type in each match arm. For example, in the RequestA::class arm, $request remained object|null instead of being narrowed to RequestA, causing false-positive errors about property access on object|null and argument type mismatches.

Changes

  • Added ternary expression decomposition in TypeSpecifier::resolveNormalizedIdentical() (src/Analyser/TypeSpecifier.php)
  • When processing ($cond ? $trueExpr : $falseExpr) === $value in truthy context, determines which ternary branch can produce the matched value by checking type compatibility
  • If only the true branch can match (false branch type is incompatible with the value), narrows the ternary condition as truthy and recurses on $trueExpr === $value
  • If only the false branch can match (true branch type is incompatible with the value), narrows the ternary condition as falsy and recurses on $falseExpr === $value
  • The true branch type is evaluated in a truthy-narrowed scope, so $request::class is computed with $request narrowed from ?object to object

Root cause

TypeSpecifier::resolveNormalizedIdentical() had specific pattern recognition for $expr::class === 'ClassName' comparisons, but when the match subject was a ternary expression like $request ? $request::class : null, the ternary wrapper prevented the $expr::class pattern from being recognized. The fix decomposes the ternary by determining which branch can produce the matched value and then recurses with the appropriate branch, allowing the existing ::class narrowing logic to apply.

Test

Added regression test tests/PHPStan/Analyser/nsrt/bug-11923.php that verifies:

  • $request is narrowed to null in the null => match arm
  • $request is narrowed to RequestA in the RequestA::class => arm
  • $request is narrowed to RequestB in the RequestB::class => arm
  • Direct $request::class match subject (non-nullable) also works correctly

Fixes phpstan/phpstan#11923

…: null

- Added ternary expression decomposition in TypeSpecifier::resolveNormalizedIdentical
- When ($cond ? $trueExpr : $falseExpr) === $value and only one branch can produce
  the value, narrows the condition and the matching branch independently
- Evaluates the true branch type in the truthy-narrowed scope so that e.g.
  $request::class is computed with $request narrowed to non-null
- New regression test in tests/PHPStan/Analyser/nsrt/bug-11923.php

Closes phpstan/phpstan#11923
@VincentLanglet VincentLanglet deleted the create-pull-request/patch-7i6olsk branch March 8, 2026 12:16
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