确认包真未使用需三步:先用composer depends查无直接依赖,再grep -r搜索代码和配置中引用,最后composer dump-autoload && grep验证自动加载文件未包含该包。

直接删掉没用的包,比任何配置技巧都管用;但“有没有用”得靠查链路、看代码、跑运行时,不能只看composer.json里写没写。
怎么确认某个包真没被项目用到?
很多人删包后报Class not found,不是命令错了,是没验证实际调用关系。
- 先查谁在拉它进来:
composer depends vendor/package-name,如果输出为空,说明它只是深层传递依赖,没人直接 require - 再搜代码里是否硬编码引用:
grep -r "Vendor\Package" app/ src/ config/ --include="*.php",特别注意配置文件里的字符串类名、事件监听器、队列任务的job字段 - 最后验证 autoload 是否还加载它:
composer dump-autoload --no-dev && grep -l "vendor/package-name" vendor/composer/autoload_*.php,没命中才大概率安全
为什么composer install --no-dev --optimize-autoloader还是装了一堆包?
这个命令只跳过require-dev安装,不解决主依赖自身带的可选依赖(suggests)或provide声明引发的隐式拉取。
-
monolog/monolog的suggests里列了aws/aws-sdk-php,哪怕你只用StreamHandler,它仍会被 Composer 解析进依赖图——除非你用conflict显式排除 - 某些包(如
guzzlehttp/guzzle)通过provide声明虚拟包,conflict无效,得用replace+ 空包占位,例如"replace": {"psr/http-client": "*"} -
dev-develop这类分支版本会放大依赖树,换成稳定 tag(如v9.52.16)能显著收窄候选包范围
如何生成真正干净的composer.lock?
--no-dev参数不会清理composer.lock里已存在的require-dev记录,部署时可能误判依赖边界。
- 手动删掉
composer.json中的整个"require-dev": { ... }块(包括花括号),别留空对象 - 运行
composer update --no-dev --lock,不是install——它会重算依赖图并生成不含 dev 元数据的 lock 文件 - 打开新
composer.lock,确认里面没有require-dev键,且packages数组长度明显减少
镜像源和--prefer-dist为什么有时反而让依赖变多?
国内镜像虽快,但若元数据同步不全,Composer 会 fallback 到 source 模式,而git clone可能拉下tests/、docs/甚至.github/目录里的 autoload-dev 配置,导致生产环境意外加载开发类路径。
- 检查镜像配置末尾是否有冗余斜杠:
composer config -g repos.packagist.org,错误格式如https://mirrors.aliyun.com/composer//会导致 dist URL 解析失败 - 强制走 dist 并禁用干扰项:
composer install --prefer-dist --no-dev --no-plugins --no-scripts --optimize-autoloader - 验证是否真走了缓存:
composer install -v | grep "Loading from cache",没看到就说明镜像或包本身 dist 缺失
最难的从来不是命令怎么敲,而是判断某个包在运行时到底参没参与关键链路——比如spatie/laravel-ray在 ThinkPHP 项目里只是开发调试工具,但它的autoload-dev路径如果被vendor/autoload.php加载了,就会污染 OPcache,这种细节得看vendor/composer/autoload_dev.php是否被任何地方require。











