composer如何检测已安装但未声明的依赖?(reverse dependency扫描工具)

6次阅读

composer找不到未声明的已安装包,因其仅以composer.json和composer.lock为依赖权威来源,vendor/目录仅为安装产物;需用composer-unused扫描代码引用或手动比对composer show与ls vendor/结果。

composer如何检测已安装但未声明的依赖?(reverse dependency扫描工具)

composer为什么找不到未声明的已安装包?

Composer 本身不维护“已安装但未声明”这类状态的索引,vendor/ 下的包只是安装产物,不是项目依赖图的权威来源。它只在 composer.jsoncomposer.lock 里记录显式声明的依赖。所以直接跑 composer outdatedcomposer show 都不会标出“偷偷混进来”的包。

composer-unused 扫描未声明却实际被引用的包

这是目前最贴近需求的工具:它静态分析 PHP 代码(usenew、函数调用等),再比对 composer.jsonrequirerequire-dev,找出“用了但没声明”的包。

常见错误现象:composer-unusedPackage xyz is required but not used(误报)或漏掉真正该报的(比如用了 class_alias字符串类名)。

  • 安装:运行 composer global require composer-unused/composer-unused(推荐全局)
  • 运行:在项目根目录执行 composer-unused,它默认扫描 src/tests/,可通过 --scan-dir 扩展
  • 注意:它不检测运行时 includeeval 引入的代码,也不处理反射动态加载的类
  • 若项目用 PSR-4 自动加载但命名空间和路径不严格匹配,可能漏判——先确保 composer dump-autoload -o 能正常加载所有类

手动验证:检查 vendor/ 里哪些包不在 composer.json

这解决的是另一类问题:“谁被装进来了,但我根本没打算用”。比如团队成员手动 composer require xxx 后忘了提交 composer.json,或者 CI 环境残留了旧包。

操作很简单,但容易踩坑:

  • 先运行 composer install --no-dev(或 --no-scripts)确保环境干净,避免脚本自动拉包干扰
  • 对比两份列表:composer show --no-dev --name-only | sort(声明的) vs ls vendor/ | sort(实际有的)
  • diff 可快速看出差异(linux/macos
  • windows 用户可用 PowerShell:Compare-Object (composer show --no-dev --name-only | Sort-Object) (Get-ChildItem vendor | foreach-Object Name | Sort-Object)
  • 注意:有些包如 composer-plugin-apipsr/log 是其他包的传递依赖,它们出现在 vendor/ 里完全正常,别一看到就删

为什么不能只靠 composer.lock 做反向校验?

composer.lock 记录的是完整依赖树(含传递依赖),而“未声明但已安装”通常指那些**既不在 composer.jsonrequire 里,也不在任何已声明包的 composer.json 中作为依赖存在**的包——这种情况极少见,一般是 vendor/ 被手动修改、CI 缓存污染,或用了非标准安装方式(如 git clone 直接扔进去)。

此时 composer-unused 也无能为力,因为没有代码引用它,它就是个“死包”。唯一可靠手段是清空 vendor/composer.lock,再从头 composer install,看哪些包会消失——消失的就是可疑对象

这种场景下,真正难的不是发现,而是确认它是否被某些未纳入 Git 的脚本、配置或私有代码间接依赖。得查 grep -r "vendor/xxx" . --include="*.php",甚至翻 CI 日志。

text=ZqhQzanResources