composer install必须基于composer.lock执行,因其唯一职责是严格还原锁定状态:只读取lock中记录的精确版本、哈希值和下载地址,完全忽略composer.json的版本范围,确保环境100%一致;缺失lock则报错或退化为不可控的update。

composer install 必须有 composer.lock,否则它根本不会装依赖,而是直接报错或退化为不可控的 composer update。
composer install 为什么拒绝执行,除非存在合法的 composer.lock
它不是“没找到就帮你生成”,而是设计上只消费 lock 文件。没有 composer.lock,新版 Composer(v2.5+)会报 Command "install" is not defined;旧版可能静默失败,但 vendor/ 里装的包大概率是错的。
常见错误现象:
- CI 构建卡在 “Resolving dependencies…” —— 实际已在隐式运行
composer update,触发 SAT 求解器重算整个依赖树 - 本地能跑,CI 报
Class not found或TypeError—— 因为没拉到composer.lock,不同机器解析出不同版本的guzzlehttp/guzzle或symfony/http-foundation - 新项目第一次运行
composer install直接失败,提示No composer.lock file present—— 这时必须用composer update,它才是唯一能生成 lock 的命令
composer.json 是需求说明书,composer.lock 是交付清单
composer.json 里写的是模糊约束:"monolog/monolog": "^2.0" 表示“2.x 中任一兼容版本”;而 composer.lock 记的是铁板钉钉的结果:"version": "2.9.1"、"dist": {"sha256": "a1b2c3..."}、甚至包括该包的完整依赖树和 autoload 配置。
关键点:
-
composer install完全忽略composer.json中的^、~、*,只按 lock 里写的装 - 即使你把
composer.json中的"php": "^8.1"改成"^8.2",只要 lock 没更新,install仍会装旧版依赖(哪怕它们已不支持 PHP 8.2) - lock 文件里还存着
platform字段(如"php": "8.2.15"),这是当时解析依赖所用的环境快照,不是运行时校验依据
Git 合并冲突时别手改 composer.lock
它不是普通 JSON 配置文件,而是整个依赖图的完整序列化快照。手动删字段、调缩进、凑合保留某一方内容,都会破坏 content-hash 校验,导致后续 composer install 失败或类加载异常。
正确做法:
- 用
git checkout --ours composer.lock或git checkout --theirs composer.lock任选其一还原(推荐选目标分支,比如main) - 删掉
vendor/目录(确保无残留干扰) - 运行
composer install—— 如果当前composer.json和所选 lock 不匹配,会立刻报错Your lock file does not contain a compatible set of packages,逼你确认到底要哪套依赖 - 若只是想对齐 lock 和当前
vendor(比如你本地改过包但没更新 lock),用composer update --lock,它只重写 lock 文件,不装不卸不升级
什么时候该动 composer.lock,什么时候不该
它不是随改随提的配置项,只对影响依赖解析逻辑的变更敏感。改了却没提交新 lock,就是埋雷。
必须触发 composer update 并提交新 composer.lock 的场景:
- 修改
require或require-dev中的包名或版本约束(例如把"guzzlehttp/guzzle": "^7.0"改成"^8.0") - 增删包(
composer require foo/bar或手动删require条目) - 调整
config.platform、minimum-stability或启用prefer-stable
仅改 autoload、scripts、name、description 等字段,无需更新或提交 composer.lock。
CI/CD 流水线中禁止出现 composer update —— 它破坏可重现性,应始终用 composer install。真正麻烦的不是锁文件本身,而是人误以为它只是个“日志”,随手删、手动改、不提交、不拉取。


















