弃用包本身不直接报错,真正冲突源于下游依赖、API差异或被其他包间接引入;需用composer why/why-not/prohibits等命令定位根因,再按迁移提示谨慎升级或替换。

弃用包(deprecated package)本身不直接报错,但它的下游依赖、替代包的API差异、或它被其他活跃包悄悄拉入,才是冲突真正的导火索。别急着删它,先确认它是否真被项目需要。
查清弃用包是不是被 require-dev 悄悄带进来的
很多弃用包根本不在 require 里,而是藏在 require-dev 中——比如 phpunit/phpunit 旧版会拖着 sebastian/exporter 1.x,而新项目主依赖可能要求 4.x+,形成间接锁死。
- 运行
composer why phpunit/phpunit,看它是不是由某个测试工具引入 - 再跑
composer show --tree | grep sebastian/exporter,确认当前锁定版本和谁在用它 - 如果只是开发时才需要,且新版 PHPUnit 已兼容你的 PHP 版本,就直接升级:
composer require phpunit/phpunit:^10 --dev --no-update,再composer update phpunit/phpunit --with-dependencies - 别用
composer remove直接删——它可能没写在 composer.json 里,只是被其他包传递依赖进来的
用 composer why-not 定位“谁在封杀替代包”
弃用包常和它的替代包共存于依赖树中,但 Composer 不会自动切换。当你想换上推荐的替代包(比如用 psr/log 替代 monolog/monolog 的某些能力),却卡在“don’t install”错误,说明某处还在强依赖旧包。
- 执行
composer why-not psr/log:^3.0,输出会从底向上列出所有阻止该版本安装的路径 - 重点看最上面一行(即 root package 或你显式 require 的包),它才是真正提约束的地方
- 如果发现是某个已弃用包的
replace字段在起作用(比如"replace": {"monolog/monolog": "*"}),那它其实是在“冒充” monolog,此时你得检查代码里是否真调用了 monolog 的类——否则这个 replace 就是冗余甚至有害的
替换弃用包时小心 API 断层
不是所有“官方推荐替代”都能无感切换。例如 guzzlehttp/guzzle 从 6.x 升到 7.x,send() 返回值类型变了;symfony/http-foundation 从 4.x 到 5.x,Request::getRealIp() 被移除。这些不会在 composer update 阶段报错,但运行时立刻崩。
- 先查 Packagist 上该弃用包的 README,看它明确写的替代方案和迁移提示
- 执行
composer require vendor/replacement-package:^x.y --no-update,只改 composer.json - 再手动运行
composer update vendor/replacement-package --with-dependencies,观察 lock 文件里哪些包被连带更新 - 更新后立刻跑
git grep -n "Monolog\Logger|GuzzleHttp\Client",确认旧类引用是否还存在
conflict 字段要慎用,别让弃用包变成“幽灵阻塞源”
有些包在自己的 composer.json 里写了 "conflict": {"monolog/monolog": ">=2.0"},但你项目根本没 require monolog —— 它仍可能通过其他包被间接拉入,触发冲突。这种“隐性阻塞”最难排查。
- 运行
composer prohibits monolog/monolog,它会列出所有声明了冲突规则的包(比why-not更早一层) - 如果输出里有你不认识的包,用
composer show vendor/package看它的 conflict 字段原文 - 临时注释掉你项目 composer.json 里的
conflict(仅用于诊断),再composer update --dry-run看是否能过,确认是不是它在作祟 - 别长期留着
"conflict": {"*": "*"}这类宽泛写法——它会让 Composer 主动拒绝一切组合
弃用包的问题从来不在它自己身上,而在它和周围包之间那些没被显式声明、却真实存在的耦合。每次更新前看一眼 composer show --tree 输出里带 (replaced) 或 (provides) 标记的节点,比盲目删包管用得多。


















