PHPStan需显式启用高阶级别(Level 8+)才能检测未使用变量:Level 7查未用私有属性,Level 8查未用私有方法参数,Level 9查未用局部变量;须通过composer require --dev phpstan/phpstan安装,并在composer.json中配置"check-unused": "phpstan analyse --level=8 src/"实现一键检测。

composer 本身不检测未使用的变量——它只管依赖管理,不碰 PHP 源码分析。你要的静态检测能力来自 PHP 专用工具,比如 PHPStan 或 Psalm,它们能识别 $unusedVar、未读取的私有属性、无调用的私有方法参数等。
下面直接说怎么做、为什么这么配、容易掉进哪些坑。
PHPStan 怎么开箱检测未使用变量
PHPStan 默认不报未使用变量(包括变量、私有属性、私有方法参数),必须显式启用 phpstan-phpunit 以外的扩展或手动加规则。最简单的方式是安装并启用 phpstan-extension 中的 UnusedVariableRule,但更通用的做法是升级到 PHPStan Level 8+ 并配合配置:
- Level 7 开始检查未使用的私有属性(
private $foo;从没被读/写) - Level 8 起检查未使用的私有方法参数(
private function foo($a, $b) { return $a; }中的$b) - Level 9 还会查未使用的局部变量(
$tmp = []; // 后面没用过) - 注意:public/protected 成员和函数参数永远不报——因为可能被子类或外部调用,PHPStan 不做跨文件推断
命令行执行:vendor/bin/phpstan analyse --level=8 src/;如果想只聚焦“未使用”问题,可加 --error-format=raw 后 grep unused。
composer.json 里怎么封装成脚本
把分析命令固化为 composer run-script 可复用的快捷方式,避免每次敲长命令。在 composer.json 的 scripts 字段加一条:
"scripts": {
"check-unused": "phpstan analyse --level=8 --no-progress src/"
}
之后运行 composer check-unused 就能触发检测。建议加 --no-progress 避免 CI 日志刷屏,也别漏掉 src/ 路径——不指定路径时 PHPStan 默认只扫当前目录,容易漏。
常见错误现象:composer check-unused 报错 “Command not found”,是因为没先 composer install --dev 安装 phpstan/phpstan;或者脚本里写了 phpstan 但没加 vendor/bin/ 前缀,而系统 PATH 里又没这个二进制。
为什么 PHPStan 检出的“未使用”有时不准
它基于 AST 静态扫描,对运行时行为无感知。以下情况会导致误报或漏报:
- 动态变量名:
$varName = 'foo'; $$varName = 'bar';→$foo不会被识别为已使用 - 魔术方法调用:
__get()/__set()访问的私有属性不会被标记为“已读/已写” - DI 容器注入:Laravel/Symfony 中通过构造函数注入的私有属性,若没在本类中显式读写,会被标为未使用
- 测试文件默认不扫:如果你只在
tests/里用了某个变量,而分析命令没包含该目录(如只跑src/),就看不到报告
解决办法:加 --paths-file=paths.txt 显式列出 src/ 和 tests/;对确需保留的变量,用命名前缀 $unusedFoo 或注释 // @phpstan-ignore-next-line 抑制。
和 composer-unused 是两回事,别混用
composer-unused 查的是“哪个包没被任何 use 或 new 调用”,目标是删 composer.json 里的冗余 require 行;而 PHPStan 查的是“哪行 PHP 代码定义了但没用的变量”,目标是清理死代码。两者扫描粒度、原理、输出结果完全不同。
典型混淆场景:你删掉了 guzzlehttp/guzzle 的 require,composer-unused 说它未使用,但 PHPStan 仍可能报 $client = new \GuzzleHttp\Client(); 所在文件“Class not found”——因为 vendor/ 已空,而 PHPStan 分析时需要类存在才能解析类型。所以顺序一定是:先 composer install 确保环境完整,再跑 PHPStan。


















