Skip to content

Fix #7719: ReflectionMethod with invokeArgs: Dead catch - exception is never thrown in the try block.#4970

Closed
phpstan-bot wants to merge 2 commits into2.1.xfrom
create-pull-request/patch-ktypnth
Closed

Fix #7719: ReflectionMethod with invokeArgs: Dead catch - exception is never thrown in the try block.#4970
phpstan-bot wants to merge 2 commits into2.1.xfrom
create-pull-request/patch-ktypnth

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When ReflectionMethod::invoke() or ReflectionMethod::invokeArgs() is called, the dynamically invoked method can throw any exception. PHPStan was incorrectly reporting "Dead catch" for non-ReflectionException catch blocks in try blocks containing these calls.

Changes

  • Added src/Type/Php/ReflectionMethodInvokeMethodThrowTypeExtension.php — a DynamicMethodThrowTypeExtension that declares ReflectionMethod::invoke() and invokeArgs() can throw Throwable
  • Added regression test in tests/PHPStan/Rules/Exceptions/data/bug-7719.php
  • Added test method testBug7719 in tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php

Root cause

PHPStan had DynamicStaticMethodThrowTypeExtension for ReflectionMethod::__construct() (to narrow the throw type when class/method names are known constants), but no corresponding extension for invoke()/invokeArgs(). Without an extension, PHPStan fell back to the @throws ReflectionException annotation from PHP stubs, meaning only ReflectionException was considered as a possible throw type. Since these methods execute arbitrary user code, they can throw any Throwable.

Test

The regression test creates a scenario where ReflectionMethod::invokeArgs() and invoke() are called with a dynamic method name, and a RuntimeException is caught. Before the fix, PHPStan incorrectly reported these catches as dead.

Fixes phpstan/phpstan#7719

phpstan-bot and others added 2 commits February 17, 2026 01:03
- Added ReflectionMethodInvokeMethodThrowTypeExtension to declare that
  ReflectionMethod::invoke() and invokeArgs() can throw any Throwable,
  since they execute arbitrary user code
- New regression test in tests/PHPStan/Rules/Exceptions/data/bug-7719.php
- The root cause was that without a DynamicMethodThrowTypeExtension,
  PHPStan only considered ReflectionException (from stubs) as the throw
  type, causing any non-ReflectionException catch to be flagged as dead
@staabm
Copy link
Contributor

staabm commented Feb 17, 2026

duplicate of #4978

@staabm staabm closed this Feb 17, 2026
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

Comments