replace仅修改依赖图,不删除文件或重映射类,必须先执行composer remove old/package,再更新代码、配置及autoload,否则平替无效。

replace字段只改依赖图,不删文件也不重映射类
很多人加了"replace": {"old/package": "self.version"}就以为旧包自动消失了,结果vendor/old/package还在,new OldPackage\Client()照样能 new 出来——因为replace只是告诉 Composer “别再装它”,对已存在的文件、autoload 映射、代码调用零影响。
它本质是依赖解析阶段的声明,不是运行时的代理或重定向。如果你没手动删掉旧包、没改代码里的use和new、没清理autoload.psr-4里残留路径,那平替就是假象。
- 执行
composer remove old/package是必须的第一步,否则replace形同虚设 -
replace生效后,composer show old/package仍会显示已安装——这是正常现象,说明物理目录未清,得靠后续composer install或composer update触发清理 - 若旧包被其他已装包硬依赖(比如
laravel/frameworkrequire 它),composer remove会报错,此时replace也救不了你,得先升级上游
接口兼容性断裂比依赖冲突更致命
能composer update成功 ≠ 迁移完成。很多项目卡在ArgumentCountError或Class not found上,根源不在 Composer,而在新旧包的契约差异。
比如从monolog/monolog:1.x切到2.x,Logger::__construct()参数从HandlerInterface变成HandlerInterface[];又或者guzzlehttp/ringphp彻底移除了RingClient类——这些不会被replace捕获,也不会在composer install时报错,只会在 runtime 抛异常。
- 逐个检查所有
use语句、new调用、catch块里的异常类名(如OldException→NewTransportException) - 配置文件里硬编码的类名(如
config/logging.php中的'handler' => MonologHandlerStreamHandler::class)必须同步改 - 若新包实现的是
Psr\Log\LoggerInterface而旧包是自定义接口,得补一层适配器,不能直接替换构造函数参数
本地路径依赖 + replace 的组合最容易踩 vendor 冲突
想用本地开发版平替废弃包?常见错误是:先写"replace": {"old/package": "dev-main"},再composer require --dev ./local-replacement,结果vendor/old/package和vendor/local-replacement共存,PHP 自动加载优先命中旧包的类——因为命名空间一样,且旧包路径在autoload_psr4.php里排更前。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
这不是 Composer bug,是 autoload 顺序决定的加载优先级。你得主动打破这个顺序。
- 必须先
composer remove old/package,再composer require --dev ./local-replacement,否则两个同名命名空间必然打架 -
replace值不能写"dev-main",得是合法语义化版本号(如"2.1.0"),否则 Composer 忽略该条目 - 本地包的
composer.json里要显式声明"provide": {"old/package": "2.1.0"},否则强约束依赖(如"require": {"old/package": "^1.0"})仍会报“no matching package found”
间接依赖的废弃包根本删不掉,只能升级上游或加 conflict
执行composer remove old/package报错Package is not installed?不是命令错了,是它被symfony/console或laravel/framework这类顶层包硬 require 了。Composer 不允许你删“别人家的孩子”。
这时replace也无效——因为依赖图里根本没给你留插手的位置。你唯一可控的动作,是让 Composer 拒绝拉入它。
- 查谁在拉它:
composer depends old/package,如果返回一堆包名,说明它是传递依赖 - 看上游包 release notes:比如
doctrine/orm v2.12+已移除对doctrine/common的依赖,那你升级doctrine/orm就自然淘汰了废弃包 - 实在没法升级上游?加
"conflict": {"old/package": "*"},强制 Composer 在解析依赖时拒绝任何版本,哪怕下游包写了require
灰度替换不是加一行replace就完事。真正的难点永远在代码层:类名、参数、异常、配置键、缓存序列化格式……这些全得人肉核对。Composer 只管声明,不管契约。










