composer remove 不会自动清理孤儿依赖,因它仅删除指定包及 composer.json 条目,不反向检测传递依赖是否仍被使用;真正识别“幽灵包”需用 composer-unused 扫描代码中 use、new 等实际调用。

仅靠 composer remove 无法识别“未使用”的包,它只响应你明确指定的卸载动作;真正要发现那些没人调用、也没被其他包依赖的“幽灵包”,必须用 composer-unused 扫描代码引用关系。
为什么 composer remove 不会自动清理孤儿依赖
Composer 的依赖解析是单向的:它知道 A 依赖 B,B 依赖 C,但不会反向检查“C 是否被项目代码直接或间接使用”。composer remove vendor/package 只做三件事:删 composer.json 条目、卸载该包、更新 composer.lock。它不管 C 是不是只剩一个空目录躺在 vendor/ 里。
- 常见翻车点:
monolog/monolog被删后,psr/log还在 —— 因为别的包(比如symfony/http-kernel)仍声明依赖它 - 更隐蔽的情况:
symfony/polyfill-*包常被核心组件悄悄 require,手动删掉会导致 PHP 低版本运行时报Fatal error - 执行
composer remove后vendor/目录仍有残留?那是正常现象 —— 它只卸载目标包,不清理传递依赖的“残骸”
用 composer-unused 扫描真实调用痕迹
它不看 composer.json 里写了啥,而是扫描你的 src/、tests/、app/(需手动指定)等目录下的 PHP 文件,找 use、new、函数调用、常量引用等实际符号使用。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 安装:运行
composer require --dev composer-unused/composer-unused - 基础扫描:执行
composer unused,默认只扫src/和tests/(注意:测试依赖默认不参与判定,除非加--scan-tests) - 漏报常见原因:业务代码在
app/或lib/下?得加--scan-dir=app --scan-dir=lib - 误报常见原因:类名通过字符串动态加载(如
class_exists('Some\Class'))、配置文件里写死类名、DI 容器注入 —— 这些composer-unused默认不分析
移除前必须人工核对的三个地方
composer-unused 可以告诉你“没被代码引用”,但不能判断它是否在框架生命周期中被隐式触发。删之前务必确认:
- 全局搜索包名或类名:用
grep -r 'GuzzleHttp\' . --include="*.php",别漏掉config/下的 PHP 配置文件(如app.php中的providers数组) - 检查是否被注解驱动:Doctrine 实体里的
@ORMEntity、PHPStan 的@var、Laravel 的@mixin等,都可能让类虽未use却必须存在 - 运行
composer dump-autoload -o后启动应用,观察日志或报错 —— 特别注意Class not found是否来自服务提供者注册、中间件、事件监听器等非显式调用链
删完之后 vendor/ 还有残留文件?这是预期行为
执行 composer remove 后 vendor/ 目录没立刻变干净,不是 bug,是 Composer 的设计逻辑:它只卸载你点名的包,不负责递归清理“已无人引用”的子依赖。真正同步 vendor/ 的命令是 composer install(按 composer.lock 重装)或 composer update(按 composer.json 重新解析依赖树)。
- 如果想彻底清空所有未声明的包,先确保
composer.json干净,再删掉composer.lock和整个vendor/,最后跑composer install - 不要手动删
vendor/子目录 —— Composer 不会感知,下次install可能跳过重装,导致 autoload 映射错乱 -
composer dump-autoload不会影响vendor/内容,它只刷新自动加载文件
最易被忽略的点:require-dev 里的包(如 phpunit/phpunit、phpstan/phpstan)经常只在 CI 脚本或本地开发时用,composer-unused 默认不扫描 .github/workflows/ 或 docker/ 下的 YAML/Shell 文件,这类依赖得靠人工交叉验证。










