replace仅在依赖解析时声明替代关系,不卸载或覆盖文件;必须先composer remove旧包,再配置replace、conflict及正确autoload,否则无效。

replace 字段本身不会替换任何已安装的包,也不会触发下载或卸载 —— 它只是依赖解析时的一条“声明规则”,生效前提非常具体,漏掉任一条件都会静默失效。
为什么写了 "replace": {"monolog/monolog": "*"} 却没用?
因为 Composer 完全忽略这行,除非同时满足:① 你的 fork 包已通过 composer require 显式安装;② 项目或某个依赖的 require 中明确写了 "monolog/monolog"(哪怕只写了一次)。replace 不是开关,它不主动干预已存在的安装状态。
- 运行
composer show monolog/monolog如果还能看到结果,说明它根本没被 replace 影响 - 执行
composer depends monolog/monolog返回 “no packages require”,才是 replace 生效的可靠信号 - 单纯在自己包的
composer.json里加replace,对当前项目无意义 —— 它只影响“谁会装你这个包”的下游场景
必须手动 remove 原包,否则类冲突不可避免
就算 replace 和 conflict 都配齐,只要 monolog/monolog 还在 vendor/ 目录下,Cannot declare class 就一定会报。Composer 不会删文件,它只管“下次装不装”。
- 先运行
composer remove monolog/monolog(不是delete或手动删目录) - 再确保你的 fork 包已通过
require引入,例如:"myorg/monolog-fork": "dev-main as 2.10.0" -
conflict必须显式加上:"conflict": {"monolog/monolog": "*"},否则 CI 环境可能因缓存或依赖树拉回原包
repositories + require + as 三件套缺一不可
想让 Composer 拉你的 fork 而非官方包,光靠 replace 是零作用。真正起效的是这三步组合:
-
repositories告诉 Composer:“我的 fork 在 GitHub 上,去那里找” -
require明确写你要哪个包 —— 必须是你 fork 的包名,不能写原包名 -
as别名让 Composer 认为它“就是原版本”,例如:"myorg/monolog-fork": "dev-main as 2.10.0" - 改完必须跑
composer update monolog/monolog(注意:这里写的是原包名,不是你的 fork 名)
autoload 不继承,必须手动对齐命名空间和路径
replace 后类找不到,90% 是因为 autoload 没配。Composer 不会抄原包的 autoload 配置,你得自己照着抄一遍。
- 查原包怎么 autoload:
composer show -p monolog/monolog | grep autoload - 如果原包是
"psr-4": {"Monolog\": "src/"},你的 fork 也得保持相同命名空间 + 相同路径映射 - 改完立刻运行
composer dump-autoload -o,再用composer show -p your-fork-package确认 autoload 规则已加载
最常被忽略的点:replace 生效后,vendor/autoload.php 里不会自动包含你的 fork 类 —— 它只依赖你自己的 autoload 配置是否正确。哪怕所有 JSON 字段都对了,autoload 错一个斜杠,Class not found 就稳稳等着你。











