conflict 是最语义清晰、原生支持的阻止不安全旧版包安装的方式,需在 composer.json 根级声明,如 "conflict": { "monolog/monolog": ">=1.0.0,

直接用 conflict 拦住不安全版本
想阻止 Composer 安装已知有漏洞的旧版包(比如 monolog/monolog 的 1.25.0),conflict 是最语义清晰、原生支持的方式。它不是“禁用”,而是告诉 Composer:“这个版本和我的项目不兼容”,解析阶段就会直接报错,根本不会进安装流程。
-
conflict必须写在composer.json根级,格式为"conflict": { "vendor/package": ">=1.0.0, —— 注意逗号分隔多个约束,不是用 <code>&&或空格 - 范围写法要小心:
" 会拦住所有 <code>1.x旧版,但不会影响2.0.0;如果想同时拦1.x和2.x的某些版本,得明确列出或写成"=2.0.0, - 执行
composer update时,只要依赖图里任何路径试图拉入被conflict的版本,就会中断并提示your-project requires vendor/package satisfiable by vendor/package[1.25.0]这类错误 - 别指望
conflict能删掉已装的旧包——它只管后续安装/更新。已有包得靠composer remove vendor/package && composer require vendor/package:^2.11手动清理
用 replace + 自研 patch 版本替代高危包
当官方没修、你又不能升级到新版(比如因 API 不兼容),最稳妥的做法是 fork 出修复分支,再用 replace 让 Composer 认为“这个包已被我接管”。这不是绕过,而是主动承担兼容责任。
- 先 fork
monolog/monolog到私有仓库,打补丁后发布新版本,比如your-org/monolog-patched:1.25.0-patch1 - 在项目
composer.json中加:"replace": { "monolog/monolog": "1.25.0-patch1" }—— 注意这里写的是你 fork 包的版本,不是原包名 - 同时在
require里显式引入你的 fork:"your-org/monolog-patched": "1.25.0-patch1" - 关键点:
replace后,其他包若声明require monolog/monolog,Composer 就不再去拉官方版,而是信任你已提供兼容实现;但如果类名、命名空间或方法签名变了,运行时照样Class not found或Call to undefined method
临时禁用插件防止恶意行为扩散
有些安全风险来自 Composer 插件本身(比如被篡改的 phpstan/extension-installer 在 install 阶段执行任意代码),这时禁用插件比锁包更紧急。插件不走依赖解析,conflict 对它无效。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 调试时立刻生效:所有命令加
--no-plugins,例如composer install --no-plugins—— 参数必须紧贴子命令后,composer install --no-plugins --optimize-autoloader可以,composer install --optimize-autoloader --no-plugins在部分旧版本可能失效 - 长期停用某个插件:在
composer.json的extra.disabled-plugins里写全名,如"hirak/prestissimo",大小写敏感,且仅对 Composer ≥2.2 生效 - 别用重命名
vendor/xxx/.disabled当长期方案——它只是跳过扫描,目录还在,autoload 仍可能加载旧类;真要清理,得composer remove hirak/prestissimo && composer dump-autoload - CI 环境建议设环境变量
COMPOSER_NO_PLUGINS=1,避免漏掉参数;但注意这会禁用所有插件,包括symfony/flex的 recipes 功能,配置文件不会自动生成
靠 lock 文件 + --locked 守住生产底线
无论用 conflict 还是 replace,最终防线都是 composer.lock 和部署时的 --locked。它不防开发误操作,但能拦住 CI 构建时的意外升级。
-
composer.lock必须提交进 Git —— 它记录了每个包的确切 commit hash 或 version,哪怕composer.json里写着"^2.8",install 也只认 lock 里的2.8.1 - 上线脚本必须用
composer install --locked --no-dev --no-interaction:--locked会校验 lock 文件是否仍满足composer.json的所有约束(包括conflict),不满足直接失败,绝不生成新 lock - 常见坑:Docker 构建用了
composer update,或者 CI 缓存了旧composer.lock导致校验通过但实际装错版本;解决办法是每次构建前git checkout -- composer.lock再 install -
composer show vendor/package和composer outdated都不可信——前者显示已装版本,后者只查 packagist 元数据,不反映conflict是否生效;唯一真实反馈是composer update --dry-run时有没有报冲突错误
真正起效的从来不是单点配置,而是 conflict 定规则、replace 接责任、--no-plugins 切执行面、--locked 卡部署门——四者叠在一起,旧包才既跑不进来,也赖不住。










