PhpStorm调用传入匿名函数的方法时@throws标签关联问题
问题场景
现有封装的数据库事务静态方法如下:
/** * Performs a function in a transaction. * * @param callable $function Function to call. * * @throws \Throwable */ public static function transaction(callable $function) {...}
方法内部逻辑为捕获所有\Throwable类型异常,执行事务回滚后重新抛出,因此初始PHPDoc标记了@throws \Throwable。但该方法实际抛出的异常类型完全由传入的$function参数决定:若传入的callable仅抛出ModelNotFoundException,transaction()方法实际也只会抛出该异常,而非宽泛的\Throwable。
编写如下调用代码时:
function doSomething(SomeModelClass $model) { Helper::transaction(function () use ($model) { $model->otherMethod(); $model->methodThatCouldThrownAnModelNotFoundException(); $model->otherMethod2(); $model->otherMethodEtc(); }); }
PhpStorm会提示需要添加try/catch捕获或声明@throws标签,但提示待处理的异常为\Throwable。如果仅在调用方方法上声明@throws ModelNotFoundException,PhpStorm会因transaction方法声明抛出\Throwable产生两个警告:
- PHPDoc注释未包含所有必要的
@throws标签 - 存在未处理的
\Throwable
目前临时解决方式为添加@noinspection注解抑制警告:
/** * @throws ModelNotFoundException * @noinspection PhpDocMissingThrowsInspection */ function doSomething(SomeModelClass $model) { /** @noinspection PhpUnhandledExceptionInspection */ Helper::transaction(function () use ($model) { $model->otherMethod(); $model->methodThatCouldThrownAnModelNotFoundException(); $model->otherMethod2(); $model->otherMethodEtc(); }); }
但每次调用都手动添加抑制注解十分繁琐,需要实现将transaction方法的@throws标签与传入参数$function的@throws标签关联,让PhpStorm可根据传入的匿名函数自动推导实际抛出的异常类型,无需重复添加抑制注解。
可行解决方案
泛型模板绑定异常类型(推荐,PhpStorm 2022.2+版本支持)
借助PhpStorm对通用泛型注解的支持,在transaction方法上声明异常类型模板,将方法抛出的异常类型与传入callable的抛出类型绑定:/** * Performs a function in a transaction. * * @template T of \Throwable * @param callable(): void $function Function to call. * * @throws T */ public static function transaction(callable $function) {...}配置后PhpStorm会自动提取传入callable声明的抛出异常类型,作为transaction方法的实际抛出类型。传入的匿名函数声明抛出什么异常,PhpStorm就会识别transaction方法对应抛出什么异常,不需要额外添加抑制注解。如果传入的匿名函数没有声明任何@throws,PhpStorm才会回退到基础的异常检查逻辑。
需确认PhpStorm开启了泛型注解识别:进入Settings > PHP > Quality Tools,开启PHPStan/Psalm注解支持即可,无需实际运行静态分析工具。收窄顶层异常声明范围
如果项目中所有业务异常都继承自统一的自定义基类(如BaseBusinessException),可以将transaction方法的@throws声明从\Throwable收窄为该自定义基类,从根源上缩小异常检查范围,减少宽泛类型带来的误报。注意该方案需要确保传入的callable不会抛出非自定义基类派生的异常,避免漏检。全局调整异常检查规则(不推荐)
进入Settings > PHP > Inspections > Undeclared possible exceptions,将\Throwable、\Exception这类顶层异常类型加入忽略列表。该方案会全局关闭顶层异常的未处理检查,可能漏掉真实存在的未处理异常问题,非必要不使用。
内容的提问来源于stack exchange,提问作者LordF

