composer install不解析任何版本约束,仅严格按composer.lock中记录的精确版本、哈希值和依赖快照还原安装;嵌套依赖版本由SAT求解器在update时全局协商确定,install阶段完全固化,不受根或子包composer.json中^/~等约束影响。

composer install 不继承、不传播、不推导任何版本约束——它只照 lock 文件里写死的版本安装,连 composer.json 里的 ^/~ 都当不存在。
composer install 根本不看嵌套依赖的 version 字段
你打开 vendor/monolog/monolog/composer.json,看到它 require "psr/log": "^1.0",但这对 composer install 完全没影响。install 阶段只读 composer.lock 中 monolog 条目下的 require 列表(只是元数据),以及 psr/log 条目下独立记录的精确版本(比如 "version": "1.2.4")。那个 "^1.0" 是 update 阶段求解时用的,install 时已被“快照固化”。
常见错误现象:composer install 后发现 psr/log 是 2.0.0,但 monolog 的 composer.json 明明写着 "^1.0" → 实际是 laravel/framework 或其他包把 psr/log 拉到了 2.x,而 lock 文件已记录该结果,install 就照装。
嵌套依赖的版本由 SAT 求解器全局协商,不是“父传子”
Composer 不会从根 composer.json 开始一层层往下“继承”约束。所有包(包括间接依赖)的 require、conflict、PHP 版本、稳定性等全部摊平进一个逻辑公式,交给 SAT 求解器一次性找解。这意味着:
- monolog 要
"psr/log": "^1.0",guzzlehttp/guzzle 要"psr/log": "^2.0",最终装哪个版本,取决于谁的约束更“松”,且能和其他所有约束共存 - 你不能靠“改 monolog 的 composer.json”来控制项目里
psr/log的版本——你根本没权限改 vendor 下的文件,而且就算改了,下次composer update也会被覆盖 - 真正起效的只有你在根
composer.json里写的require和conflict,它们和所有间接依赖的约束地位完全平等
想锁住间接依赖?别碰嵌套包的 composer.json,用 conflict
你无法通过修改嵌套依赖自身的 composer.json 来干预版本选择,但可以在根 composer.json 中用 conflict 主动排除你不想要的版本。例如,强制让 psr/log 不升到 2.x:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
{
"conflict": {
"psr/log": "^2.0"
}
}
这样,哪怕某个上游包声明要 "^2.0",SAT 求解器也会拒绝该组合,转而寻找满足 psr/log: "^1.0" 的解(前提是存在)。
注意点:
-
conflict必须配合composer update生效,composer install不重新求解,所以改完后得跑一次 update - 不要写
"psr/log": "2.*"—— Composer 不支持通配符,会报Invalid version string - 冲突范围宁窄勿宽,比如
"^2.0"比">=2.0.0"更安全,避免误杀未来可能兼容的 3.0.0
lock 文件才是嵌套关系的唯一权威来源
composer.lock 里每个包条目都包含完整字段:name、version、source.reference(Git commit)、dist.sha256、甚至它自己的 require 列表。这个 require 列表只是记录“当时这个版本依赖了哪些包”,不参与任何解析逻辑,仅用于 composer show --tree 这类展示命令。
真正决定运行时加载哪些类的,是 lock 文件中扁平列出的所有包及其精确版本。嵌套层级只是视觉结构,Composer 加载器(autoload)按 PSR-4/PSR-0 规则注册路径,跟“谁依赖谁”无关。
容易被忽略的关键点:如果你手动删掉 vendor/psr/log 目录,再跑 composer install,它不会去读 monolog 的 composer.json 找该装哪个 psr/log —— 它只查 lock 文件里 psr/log 条目的 version 和 dist.url,然后拉那个 zip 包。这就是“确定性复现”的底层实现。

















