- 开发工具
- 代码质量
- 质量保障
【免费下载链接】psalm
A PHP static analysis tool for finding errors and security vulnerabilities in PHP applications
本篇技术指南以 Psalm(PHP 静态分析工具)的DeprecatedTrait问题类型为主题,讲解该问题何时被触发、底层是如何被检测出来的、它在错误级别体系中的位置,以及面对存量废弃 trait 时的实际处理方案。读完本文,你将理解@deprecated注解在 trait 场景下的完整分析链路,并能在自己的项目中准确配置、抑制或逐步消除这类告警。
一、问题概述:什么是 DeprecatedTrait
DeprecatedTrait是 Psalm 在引用(use)一个已被标记为废弃的 trait时报告的问题类型。它属于 Psalm 错误报告体系中的一条独立 Issue,定义位于 src/Psalm/Issue/DeprecatedTrait.php,对应的短代码(SHORTCODE)为171。
原文档给出的最小触发示例:
<?php /** @deprecated */ trait T {} class A { use T; }运行 Psalm 后,class A中use T;的位置会被报告为DeprecatedTrait,错误信息形如:
ERROR: DeprecatedTrait - file.php:4:5 - Trait T is deprecated与之同族的废弃类告警还包括DeprecatedClass、DeprecatedInterface、DeprecatedMethod、DeprecatedProperty、DeprecatedConstant与DeprecatedFunction,它们的判定逻辑与报告形式完全一致,只是作用对象不同。相关文档分别位于 docs/running_psalm/issues/DeprecatedClass.md、docs/running_psalm/issues/DeprecatedInterface.md 等。
二、为什么这是坏味道
@deprecated标记通常意味着这段代码将在不远的将来停止工作("normally indicative of code that will stop working in the near future")。
具体到 trait 场景,隐患是双重的:
- 使用废弃 trait 等于继承了它的全部方法、属性与行为。一旦 trait 被移除或行为发生破坏性变更,所有
use了它的类都会连锁受影响; - trait 的复用特性让影响面比废弃单个方法更广——一个 trait 可能被十几个类引用,静态分析能够帮你一次性把这些引用点全部暴露出来,而不是等到运行时才逐个踩坑。
因此 Psalm 将这类问题视为需要尽快处理的技术债信号,而不是可以长期搁置的噪音。
三、底层实现:Psalm 是如何检测到废弃 trait 的
DeprecatedTrait的检测链路可以分为"标记解析"与"使用点检查"两个阶段,这里结合源码说明其真实工作方式。
1. 标记解析:@deprecated从 docblock 进入存储
当 Psalm 扫描(scan)一个 trait 时,会解析它的文档注释。在 src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php 中可以看到:
if (isset($parsed_docblock->tags['deprecated'])) { $info->deprecated = true; }也就是说,只要 docblock 中出现@deprecated标签(即使没有附带说明文字),该 trait 的 storage 就会被标记为deprecated = true。
2. 标记解析:PHP 8 属性(Attribute)同样有效
除了 docblock,Psalm 还支持通过 PHP 8 的原生属性(Attribute)标记废弃。在 src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php 中,以下三种属性都会把对应类/接口/trait 标记为废弃:
#[\Deprecated]#[\Psalm\Deprecated]#[\JetBrains\PhpStorm\Deprecated]
因此下面这种写法同样会触发DeprecatedTrait:
<?php #[\Deprecated] trait T {} class A { use T; }3. 使用点检查:analyzeTraitUse发出报告
真正发出DeprecatedTrait报告的位置在 src/Psalm/Internal/Analyzer/ClassAnalyzer.php 的analyzeTraitUse方法中。每当 Psalm 分析到一个use Trait;语句,它会依次完成:
- 解析 trait 的完全限定名(FQN),并校验 trait 是否存在(不存在则报
UndefinedTrait,参见同一方法的 1476-1490 行); - 检查大小写是否正确(大小写不符同样按
UndefinedTrait处理); - 通过
classlike_storage_provider取出该 trait 的存储信息; - 关键判断(1511-1519 行):
if ($trait_storage->deprecated) { IssueBuffer::maybeAdd( new DeprecatedTrait( 'Trait ' . $fq_trait_name . ' is deprecated', new CodeLocation($previous_trait_analyzer ?? $this, $trait_name), ), $storage->suppressed_issues + $this->getSuppressedIssues(), ); }这里可以观察到两个重要细节:
- 报告位置(
CodeLocation)精确指向use语句中的 trait 名称节点,方便你在 IDE 或报告中直接定位; - 报告是否会真正输出,会综合该文件/类的
suppressed_issues与上下文抑制配置——这正是下文"如何抑制"一节能生效的底层原因。
4. 测试印证
仓库的测试用例 tests/DeprecatedAnnotationTest.php 提供了与文档示例几乎一致的回归测试:
'deprecatedTrait' => [ 'code' => '<?php /** @deprecated */ trait T {} class C { use T; } ', 'error_message' => 'DeprecatedTrait', ],说明该行为是 Psalm 明确保障并持续回归验证的契约,而非偶然的副作用。
四、错误级别:DeprecatedTrait 什么时候算"错误"
DeprecatedTrait类的定义中标明ERROR_LEVEL = 2(见 src/Psalm/Issue/DeprecatedTrait.php),这与 Psalm 的整体错误级别体系直接挂钩。根据 docs/running_psalm/error_levels.md 的说明:
- Psalm 的错误级别范围是 1(最严格)到 8(最宽松),默认级别为 2;
- 在级别 1 与 2 下,
DeprecatedTrait作为**错误(error)**报告,会直接影响退出码并阻止 CI 通过; - 从级别 3 开始,所有
Deprecated*系列问题(包括DeprecatedTrait)被降级为info(提示),不再阻塞构建。
| 级别 | 严格度 | 对Deprecated*系列的处理 |
|---|---|---|
| 1(最严格) | 一切皆错误 | 作为错误报告 |
| 2(默认) | 大部分问题 | 作为错误报告 |
| 3 | 中等 | 降级为 info |
| 4–8 | 越来越宽松 | 保持 info |
因此,如果你暂时无法清理完所有废弃 trait,又希望保持较高的整体严格度,可以显式将项目错误级别设为 3,例如在psalm.xml中:
<psalm errorLevel="3" ... > </psalm>或者更精细地,仅在配置里对DeprecatedTrait单独降级:
<issueHandlers> <DeprecatedTrait errorLevel="info" /> </issueHandlers>五、如何修复与处理存量告警
原文档给出的修复建议只有一句话:不要使用这个废弃的 trait("Don't use the deprecated trait")。在实际工程中,这可以展开为以下几种具体操作路径。
1. 根本修复:替换或移除 trait
- 如果项目里存在该 trait 的替代实现,直接把
use T;换成新的 trait 或内联实现; - 如果该 trait 属于第三方依赖,升级依赖版本并同步调整引用点;
- 结合 Psalm 的报告位置,逐个类清理
use语句,必要时先与上游维护者沟通废弃时间表。
2. 存量代码的过渡方案:按文档注释抑制
在无法立即修复、需要保留废弃引用的过渡期,可以使用@psalm-suppress在函数/方法的 docblock中抑制该问题(语法细节见 docs/running_psalm/dealing_with_code_issues.md):
<?php /** @deprecated */ trait T {} class A { /** * @psalm-suppress DeprecatedTrait */ use T; }如果希望一次性抑制所有问题,也可以使用@psalm-suppress all。
3. 全局或按目录抑制:通过配置
在psalm.xml的issueHandlers中,可以对整个代码库或特定目录/文件关闭该问题(参见 docs/running_psalm/dealing_with_code_issues.md):
<issueHandlers> <!-- 全局抑制 --> <DeprecatedTrait errorLevel="suppress" /> <!-- 仅对特定目录抑制 --> <errorLevel type="suppress"> <directory name="legacy" /> </errorLevel> </issueHandlers>4. 建立基线(Baseline)渐进清零
对于遗留代码量大的项目,更推荐使用基线机制:先运行vendor/bin/psalm --set-baseline生成psalm-baseline.xml,把现有DeprecatedTrait问题记录进基线,之后在psalm.xml中通过errorBaseline="./psalm-baseline.xml"引用;随着代码逐步清理,再用--update-baseline刷新基线,让"废弃引用数量"成为一个可量化、可持续下降的指标。
六、小结
DeprecatedTrait是 Psalm 帮助开发者提前识别"即将失效的 trait 引用"的低成本告警。它的完整工作链路是:docblock 或 Attribute 标记 → ClassLikeNodeScanner/ClassLikeDocblockParser 写入 storage → ClassAnalyzer::analyzeTraitUse 在 use 点校验并报告。理解这一链路后,你既可以用错误级别(level 3 起降级为 info)和 issueHandlers 配置控制它的严格程度,也能借助@psalm-suppress与基线机制在无法立即清理的存量代码中平滑过渡,最终把所有废弃 trait 引用彻底清零。
- 开发工具
- 代码质量
- 质量保障
【免费下载链接】psalm
A PHP static analysis tool for finding errors and security vulnerabilities in PHP applications
相关推荐
Psalm 静态分析实战:理解、定位与修复 MixedPropertyFetch 错误
Psalm 静态分析实战:理解、定位与修复 MixedPropertyFetch 错误 MixedPropertyFetch 是 Psalm 在无法推断出属性访
开发工具代码质量质量保障PHPStan 错误标识符 class.extendsDeprecatedTrait 详解:类 extends 已废弃 Trait 的检测与修复
PHPStan 错误标识符 class.extendsDeprecatedTrait 详解:类 extends 已废弃 Trait 的检测与修复 导读 clas
开发工具代码质量静态分析Psalm 静态分析实战:InaccessibleProperty 属性可见性违规的检测原理与修复方案
Psalm 静态分析实战:InaccessibleProperty 属性可见性违规的检测原理与修复方案 Psalm 会在静态分析阶段发现"从不可访问的作用域读取
开发工具代码质量质量保障
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考