conflict字段是最直接的底层干预方式,它在Composer解析阶段就报错阻断,使目标包完全不参与依赖计算、不写入composer.lock;必须带版本约束(如"*")才生效,仅写包名无效。

用 conflict 强制阻止包进入依赖树
想让某个包完全不参与解析,conflict 是最直接、最底层的干预方式。它在 Composer 解析阶段就报错,目标包根本不会被考虑,更不会写入 composer.lock。
常见错误是只写包名不写版本约束,导致语法无效。正确写法必须带版本号或通配符:
-
"conflict": { "monolog/monolog": "^2.0" }—— 冲突指定范围,其他版本仍可能被引入 -
"conflict": { "monolog/monolog": "*" }—— 彻底禁止任何版本,最常用
注意:如果项目里已有代码直接引用该包(如 use MonologLogger;),加 conflict 后会直接导致 composer install 失败,而不是静默跳过——这正是它“阻断解析”的体现。
用 replace 声明“已存在”来绕过安装
replace 不是删除,而是告诉 Composer:“这个包的功能我已有实现,你别装了”。它不会报错,也不会出现在 vendor/ 中,但要求你自行确保运行时兼容性。
典型场景包括替换扩展驱动、使用 fork 版本、或彻底移除某功能模块:
-
"replace": { "phpunit/phpunit": "*" }—— 适合你本地已全局安装 PHPUnit 或改用 Pest -
"replace": { "symfony/polyfill-mbstring": "*" }—— 当服务器已启用ext-mbstring,无需 polyfill
关键风险:若其他已安装包(如 symfony/console)在代码中硬依赖被 replace 的包,且你没提供等效类,运行时就会抛出 Class not found。它不检查调用链,只管“不装”。
删 require 声明 + 重生成 lock 文件才是彻底清除
如果你的目标是让某个包从整个项目依赖图中消失(包括所有间接依赖),唯一可靠路径是手动清理 composer.json 并重建锁文件。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
不能只删 vendor/ 或只跑 composer install --no-dev,那些操作对已声明的包无效:
- 先从
composer.json的require或require-dev中彻底删除该包行 - 运行
composer update --lock(不是install),强制刷新composer.lock中的依赖快照 - 验证是否生效:
composer show vendor/package应返回Package not found,且grep -c '"vendor/package"' composer.lock输出为0
这一步容易被跳过,尤其在 CI 环境中误以为 --no-dev 能屏蔽 require-dev 里的指定包——其实不能,它只跳过安装,不改变解析逻辑。
为什么 post-install-cmd 删除 vendor 子目录是危险操作
有人试图在 scripts 里加 "post-install-cmd": ["rm -rf vendor/monolog/monolog"] 来“事后清理”,这会导致 autoload 映射和实际文件严重脱节。
即使文件被删,只要 vendor/composer/autoload_psr4.php 里还保留着 'Monolog\' => ['vendor/monolog/monolog/src'] 这样的映射,class_exists('Monolog\Logger') 就仍返回 true,但实例化时直接 fatal error。
更隐蔽的问题是:Composer 不会自动检测并剔除这些“悬空映射”,composer dump-autoload 也不会修复——除非你手动编辑 autoload 文件,或清空 vendor/composer/ 后重新生成。
真正要排除一个包,必须在依赖解析阶段就让它不存在,而不是在安装完成后做外科手术式删除。

















