- 开发工具
- 代码质量
- 质量保障
【免费下载链接】psalm
A PHP static analysis tool for finding errors and security vulnerabilities in PHP applications
Psalm 是一款面向 PHP 的静态分析工具,用于在 PHP 应用中查找错误与安全漏洞。当开启死代码检测(--find-dead-code/--find-unused-code)时,Psalm 会报告那些声明了但从未在方法体内被引用的参数,其中公共(public)与受保护(protected)方法对应PossiblyUnusedParam,私有(private)方法与普通函数则对应更确定的UnusedParam。本文以 docs/running_psalm/issues/PossiblyUnusedParam.md 为骨架,结合仓库源码与测试用例,深入讲解该问题的触发条件、与UnusedParam的差异、报错信息格式、下划线命名抑制机制以及配置层面的开关方式,帮助你在实际项目中精准治理"占位参数"。
问题触发条件:何时会报告 PossiblyUnusedParam
PossiblyUnusedParam在满足以下条件时被报告:
- 开启了死代码检测(
--find-dead-code或--find-unused-code,等价于配置项findUnusedCode); - 被检查的成员是public 或 protected方法(非接口方法);
- 方法体内从未引用某个形式参数;
- 该方法未被其他方法覆写(没有
overridden_method_ids)。
原文档给出的最小复现示例:
<?php class A { public function foo(int $a, int $b) : int { return $a + 4; } } $a = new A(); echo $a->foo(1, 2);运行检测命令:
vendor/bin/psalm --find-dead-code此时 Psalm 会在foo方法的$b参数处报告PossiblyUnusedParam,提示文本为"Param #2 is never referenced in this method"(参数编号从 1 开始)。仓库测试 tests/UnusedCodeTest.php 给出了完全一致的断言:
'possiblyUnusedParam' => [ 'code' => '<?php class A { /** @return void */ public function foo(int $i) {} } (new A)->foo(4);', 'error_message' => 'PossiblyUnusedParam - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:49 - Param #1 is never referenced in this method', ],从该测试可见,实际报告格式为PossiblyUnusedParam - 文件路径:行:列 - Param #N is never referenced in this method,其中N是 1 起始的参数序号。
与 UnusedParam 的区别:public/protected 与 private/函数
仓库中存在一对"姊妹"问题:PossiblyUnusedParam(SHORTCODE 134)与UnusedParam(SHORTCODE 135),二者在 src/Psalm/Issue/ 目录下定义,ERROR_LEVEL均为-2(参见 src/Psalm/Issue/PossiblyUnusedParam.php 与 src/Psalm/Issue/UnusedParam.php)。ERROR_LEVEL = -2表示该问题属于"始终报错"类别——即不随 error level(1 最严格、8 最宽松,默认 2)升降而被降级为 info,只能通过下划线命名或@psalm-suppress等方式抑制,无法用调整错误级别的方式掩盖。
两者的分派逻辑集中在 src/Psalm/Internal/Codebase/ClassLikes.php 的checkMethodParamReferences方法中:
- 若方法可见性不是
private且类不是接口,则遍历每个参数:- 方法被标记为
final时报告UnusedParam; - 否则报告
PossiblyUnusedParam("Possibly" 表示该方法未来可能被子类覆写或通过外部 API 使用该参数,因此语义上偏"可能未使用"而非"确定未使用");
- 方法被标记为
- 满足以下任一条件则跳过,不报告:
- 方法已被覆写(
overridden_method_ids非空); - 参数无位置信息(
location为空); - 参数是构造器属性提升参数(
promoted_property); - 参数经
FileReferenceProvider::isMethodParamUsed判定为在代码库中被引用过。
- 方法已被覆写(
与之相对,docs/running_psalm/issues/UnusedParam.md 说明UnusedParam针对private 方法或普通函数:
<?php function foo(int $a, int $b) : int { return $a + 4; }以及 docs/running_psalm/issues/UnusedClosureParam.md 说明闭包参数对应UnusedClosureParam:
$a = function (int $a, int $b) : int { return $a + 4; };同类"死代码族"问题还包括PossiblyUnusedMethod、PossiblyUnusedProperty、PossiblyUnusedReturnValue、UnusedClass、UnusedForeachValue等(见 docs/running_psalm/issues/ 目录),它们都由同一套未使用代码检测框架驱动。
为什么是 "Possibly":覆写与外部引用的不确定性
对于非 final 的 public/protected 方法,参数未在方法体内使用并不代表"永远没用"——子类可能覆写该方法并消费该参数,或者框架/第三方代码通过反射等方式以特定签名调用它。因此 Psalm 对这类方法使用偏保守的PossiblyUnusedParam:
- 方法若存在覆写关系(
overridden_method_ids非空),checkMethodParamReferences会直接跳过整个方法的参数检查; - 只有
final方法才升级为确定性的UnusedParam; - 检测逻辑还会借助 src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php 的
detectUnusedParameters,在函数体分析层面核对:抽象方法跳过、父类(非抽象)同位置存在参数时跳过——即只要父类实现使用了该位置参数,子类方法即使自己不用也不会被报告。
同时,Psalm 6 起未使用代码检测默认开启(findUnusedCode默认true),因此在没有显式传参的情况下也可能收到该报告。文档 docs/running_psalm/configuration.md 明确说明:
findUnusedCode="true"(默认)→ 检测所有未使用代码;findUnusedVariablesAndParams="true"→ 仅检测未使用变量与参数(等价于--find-unused-variables);- 可在 psalm.xml 中显式关闭:
<psalm findUnusedCode="false">。
命令行层面,src/Psalm/Internal/Cli/Psalm.php 显示--find-dead-code会被归一化为--find-unused-code(可选值auto/always,默认auto),随后由reportUnusedCode开启整条未使用代码检测链路。
下划线前缀:推荐的抑制方式
原文档给出的标准抑制方法是将未使用参数名加上下划线前缀:
<?php class A { public function foo(int $a, int $_b) : int { return $a + 4; } }这是 Psalm 官方推荐的做法,其底层实现在 src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php 的isIgnoredForUnusedParam方法中:
private function isIgnoredForUnusedParam(string $var_name): bool { return str_starts_with($var_name, '$_') || (str_starts_with($var_name, '$unused') && $var_name !== '$unused'); }即符合以下任一规则的参数名会被豁免:
- 以
$_开头(如$_b、$_request); - 以
$unused开头且不等于单独的$unused(如$unusedParam、$unusedFlag)。
该方法在detectUnusedParameters(src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php)与detectPreviousUnusedArgumentPosition(同文件 L2747)两处均被调用,保证这些参数既不会被报告,也不会影响"前一个有效参数"的定位计算。
参数命名建议:让签名自解释
由于$_前缀只是让 Psalm 忽略检查,实际代码中建议遵循以下约定,兼顾工具与可读性:
- 必须实现接口/抽象方法签名时:实现方往往必须保留某些参数(例如事件监听器、策略模式的统一签名),此时用
$_前缀明确表达"这是约定的占位参数"; - 框架回调/钩子方法:参数位置由框架约定固定,即使当前实现用不到也需保留,
$_命名让后续维护者一眼看出"该参数有意留空"; - 避免误用:如果参数其实"应该"被使用(只是暂时遗漏),
$_前缀会掩盖真实的逻辑缺陷。优先考虑是否真的不需要它,再决定是否抑制; - 团队一致性:
$_前缀是 Psalm 与 PHP 社区通用惯例($unused*变体亦受支持),在团队规范中统一即可。
其他抑制途径:@psalm-suppress 与基线
除下划线命名外,Psalm 还提供通用抑制机制:
- 行内抑制:在问题行上方使用
@psalm-suppress PossiblyUnusedParam,也可在方法 docblock 中抑制(见 docs/running_psalm/dealing_with_code_issues.md)。注意:抑制只对该符号本身生效,不会把该符号变成可达入口,因此仅被它引用的其他未使用代码仍会被报告(docs/running_psalm/configuration.md 有明确说明); - 基线(baseline):一次性生成当前所有问题的基线文件并逐步清零:
vendor/bin/psalm --set-baseline vendor/bin/psalm --use-baseline=psalm-baseline.xml详见 docs/running_psalm/dealing_with_code_issues.md。仓库根目录的 psalm-baseline.xml 即是 Psalm 自身项目使用的基线示例。
两种方式相比,$_前缀把"意图"固化在源码里(自文档化),而@psalm-suppress与基线只是压制报告;对于可能被覆写的 public/protected 方法,推荐优先使用$_前缀。
实践清单:让 PossiblyUnusedParam 保持可控
- 先定位:开启死代码检测,
vendor/bin/psalm --find-dead-code,关注Param #N is never referenced in this method报告; - 区分场景:public/protected 非 final 方法 →
PossiblyUnusedParam;final/private 方法或函数 →UnusedParam;闭包 →UnusedClosureParam; - 决策:若参数确实无用且为签名占位,加
$_前缀;若只是遗漏使用,应补充逻辑而非抑制; - 团队规范:约定
$_/$unused*命名规则,配合 phpcs.xml 等代码风格工具保持一致性; - 兜底:遗留问题较多时用基线逐步收敛,最终目标是无新增、无残留。
通过理解PossiblyUnusedParam的触发条件、Possibly语义背后的覆写与外部引用考量,以及下划线抑制的精确实现,你可以在启用 Psalm 死代码检测后,以最贴合源码意图的方式处理"未使用参数",既保持 API 签名兼容,又让代码库干净可维护。
- 开发工具
- 代码质量
- 质量保障
【免费下载链接】psalm
A PHP static analysis tool for finding errors and security vulnerabilities in PHP applications
相关推荐
Psalm InvalidGlobal 错误详解:`global` 关键字的作用域检查、触发条件与抑制策略
Psalm InvalidGlobal 错误详解: global 关键字的作用域检查、触发条件与抑制策略 导读 InvalidGlobal 是 Psalm 静态
开发工具代码质量质量保障Psalm 的 MissingTemplateParam 问题:泛型继承时模板参数缺失的检测、修复与抑制
Psalm 的 MissingTemplateParam 问题:泛型继承时模板参数缺失的检测、修复与抑制 本篇技术指南聚焦 Psalm(PHP 静态分析工具)在
开发工具代码质量质量保障OneUptime 集成指南:用 Webhook 把 Zabbix 问题自动转化为 Incident 工作流
OneUptime 集成指南:用 Webhook 把 Zabbix 问题自动转化为 Incident 工作流 本篇基于 OneUptime 官方文档 Zabbi
开发工具代码质量质量保障
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考