composer.lock 是带哈希校验的依赖快照而非配置文件,必须提交至 Git 且禁止手动编辑;它被 composer install 严格按版本、哈希、URL 执行安装,而 composer update 则忽略 lock、重新解析 json 约束生成新依赖树。

composer.lock 不是配置文件,而是带哈希校验的依赖快照;它必须提交到 Git,且绝不能手动编辑。
为什么 composer install 会失败,而 composer update 却能过
这是最常被误解的起点:两者根本不是同一类操作。
-
composer install只读composer.lock,严格按里面记录的版本、哈希、dist URL 安装——如果 lock 文件里存的是"monolog/monolog": "2.9.1",哪怕 packagist 上已有 2.10.0,它也绝不升级 -
composer update完全忽略composer.lock,重新解析composer.json的约束(如"^2.9"),计算新依赖树,再生成新 lock —— 这才是“升级”的正确路径 - 常见错误:CI 脚本里写
composer install --no-lock,结果报错 “Unrecognized options: --no-lock” —— 这个参数压根不存在,Composer 不提供绕过 lock 的合法方式
composer.lock 内容不一致导致 vendor 目录差异
团队成员执行 composer install 后 vendor/ 内容不同,几乎 100% 是因为 lock 文件没同步或被破坏。
-
content-hash字段基于composer.json全文生成;只要 json 有改动(哪怕只是多一个空格),hash 就变,composer install会警告 “The lock file is not up to date” -
packages数组中包的顺序影响 hash 计算;不同 Composer 版本或平台(PHP 8.1 vs 8.3)可能生成不同顺序,引发 Git 冲突 - 不要用 diff 工具比对 lock 文件差异来“修复”;一旦发现
vendor/行为不一致,优先运行composer validate检查 lock 是否损坏
Git 合并时出现 composer.lock 冲突怎么办
别打开冲突标记去删 或改字段顺序——这等于直接炸掉依赖契约。
- 先确保
composer.json已无冲突,且是你期望的最终状态(可用git checkout --theirs composer.json快速采纳远端) - 删掉当前冲突的
composer.lock(可先mv composer.lock composer.lock.bak备份) - 运行
composer update --lock:它只重写 lock 结构,不改变任何已锁定的包版本,输出应为 “Lock file operations: 0 installs, 0 updates, 0 removals” - 若
vendor/已清空(如 rebase 中),改用composer install --no-interaction,前提是现有 lock 能被正常解析
删了 composer.lock 会怎样
等价于隐式执行一次不受控的 composer update,风险极高。
- 子依赖可能悄悄升级(如
guzzlehttp/guzzle从 7.5.0 降到 7.2.0),触发已知 DNS bug - 安全扫描工具无法比对历史基线,SCA 告警失效
- CI 构建通过,但线上报
Class not found—— 因为开发机上还残留旧vendor/,掩盖了问题 - 唯一安全的删除场景:确认
composer.lock已损坏(composer validate报 JSON 解析错误),且你明确要重置整个依赖状态
真正容易被忽略的点在于:composer.lock 的字段顺序、空格、换行都有语义,它不是给人读的 JSON,而是给 Composer 校验用的二进制等价物。任何“看起来无害”的手动调整,都可能导致 install 阶段直接退出或静默安装错误版本。


















