Dynamic Throw Type Extensions
To support precise try-catch-finally analysis, you can write a dynamic throw type extension to describe functions and methods that might throw an exception only when specific types of arguments are passed during a call.
The implementation is all about applying the core concepts so check out that guide first and then continue here.
Because you have to write the code with the type-resolving logic, it can be as complex as you want.
This is the interface for dynamic throw type extension:
namespace PHPStan\Type;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
interface DynamicMethodThrowTypeExtension
{
public function isMethodSupported(MethodReflection $methodReflection): bool;
public function getThrowTypeFromMethodCall(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): ?Type;
}
An example #
Let’s say you have a method with an implementation that looks like this:
/** @throws ComponentNotFoundException */
public function getComponent(string $name, bool $throw): ?Component
{
if (!array_key_exists($name, $this->components)) {
if ($throw) {
throw new ComponentNotFoundException($name);
}
return null;
}
return $this->components[$name];
}
This is how you’d write the extension that tells PHPStan the exception can be thrown only when $throw
is true
:
public function isMethodSupported(MethodReflection $methodReflection): bool
{
return $methodReflection->getDeclaringClass()->getName() === ComponentContainer::class
&& $methodReflection->getName() === 'getComponent';
}
public function getThrowTypeFromMethodCall(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): ?Type
{
if (count($methodCall->getArgs()) < 2) {
return $methodReflection->getThrowType();
}
$argType = $scope->getType($methodCall->getArgs()[1]->value);
if ((new ConstantBooleanType(true))->isSuperTypeOf($argType)->yes()) {
return $methodReflection->getThrowType();
}
return null;
}
And finally, register the extension in the configuration file:
services:
-
class: App\PHPStan\GetComponentThrowTypeExtension
tags:
- phpstan.dynamicMethodThrowTypeExtension
There’s also analogous functionality for:
- static methods using
DynamicStaticMethodThrowTypeExtension
interface andphpstan.dynamicStaticMethodThrowTypeExtension
service tag. - functions using
DynamicFunctionThrowTypeExtension
interface andphpstan.dynamicFunctionThrowTypeExtension
service tag.