composer install和composer show无法检测未使用依赖,因composer仅依据composer.json安装并列出包,不扫描php代码中的use、new、class_exists()等实际引用行为,故无法识别“声明但未使用”的冗余包。

composer-unused 是目前唯一能可靠识别「声明了但代码里真没用」的工具,但它不是万能扫描器——误报常见,必须人工交叉验证。
为什么 composer install 和 composer show 都不能检测未使用依赖
Composer 只管 composer.json 里写了什么,它不读你的 PHP 文件。即使某个包在 require 里躺着,只要代码里没出现 use、new、class_exists()、static:: 等调用痕迹,Composer 就完全无感。运行 composer show 只是列出 vendor/ 下装了啥,和“有没有被用”毫无关系。
常见错误现象:
-
composer outdated只比对版本,不涉及使用逻辑 -
grep -r "SomePackage"容易漏掉动态类名或配置驱动场景 - 依赖树里层层嵌套,A→B→C,删掉 A 后 C 还留在 vendor/ 里,但没人调用——这种“幽灵包”只能靠静态引用分析发现
composer-unused 怎么跑才不容易误报
默认行为会扫 tests/、vendor/、var/,导致结果污染。必须显式约束扫描范围:
- 加
--exclude tests:避免把仅在测试中使用的包标为“冗余”(除非你明确要检查测试依赖) - 加
--exclude vendor:防止 vendor 内部包互相引用干扰判断 - 加
--exclude var(或--exclude storage、--exclude bootstrap/cache):排除生成文件目录 - 用
--no-dev只查生产环境依赖,避开phpunit、larastan这类纯开发工具 - 若业务代码不在
src/而在app/或lib/,需加--scan-dir app显式指定
示例命令:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
composer-unused --no-progress --no-dev --exclude tests --exclude vendor --exclude var --scan-dir app
哪些“未使用”其实是假阳性,千万别删
composer-unused 基于静态分析,对运行时行为完全无感。以下情况它必然标为“未使用”,但删掉就炸:
-
doctrine/annotations:代码里没use,但被 Doctrine ORM 的注解解析器反射加载 -
spatie/laravel-permission:权限逻辑全在 migration 和 artisan 命令里,composer-unused扫不到 -
monolog/monolog:只在config/logging.php中以字符串形式注册 handler,无 PHP 类调用 -
symfony/polyfill-*:加载即注册函数别名,副作用型包,连class_exists()都不会触发 -
phpunit/phpunit:只在phpunit.xml或 CI 脚本中调用,源码层确实没引用
验证方法很简单:它说某个包没被用,立刻执行
grep -r "Monolog\" app/ config/ --include="*.php"
如果真没结果,再结合是否属于上述几类场景做最终判断。
CI 中怎么安全接入并避免误伤
直接在 CI job 里跑 composer-unused 并设为失败项,风险极高——一次误报就能阻断发布。推荐分两步走:
- 首次接入时用
--json > unused-report.json导出结果,人工确认一批“合理但未使用”的包,写进项目根目录的composer-unused.php白名单 - CI 中启用
--fail-on-unused+--min-confidence=high,只对高置信度结果报错 - 不要和
composer install放同一个 job:前者读源码,后者读composer.lock,职责分离更稳
最麻烦的从来不是命令怎么敲,而是得花时间判断:那个被标红的包,到底是该删的累赘,还是你还没补全调用路径的隐性依赖。










