composer install 必须依赖 composer.lock,因为它专为“还原”设计,只读取锁文件中记录的精确版本、SHA-256校验值、下载地址及完整依赖树,完全忽略composer.json中的^、~等版本约束;缺失lock时会退化为不可控的update行为,导致环境不一致。

composer install 为什么必须依赖 composer.lock
因为 composer install 的设计目标就是“还原”,不是“解析”——它只读 composer.lock,完全忽略 composer.json 中的版本约束符号(如 ^、~ 或 *)。没有这个文件,它就无法知道该装哪个确切版本。
常见错误现象:
- CI 构建失败,报
Class not found或TypeError,但本地能跑 -
vendor/目录在不同机器上内容不一致,哪怕composer.json没动过 - 部署后突然出问题,回溯发现某个子依赖(比如
symfony/http-foundation)从 v5.4.32 升到了 v5.4.33,而后者引入了一个未文档化的内部方法变更
这些都不是偶然,而是 composer install 在找不到 composer.lock 时,自动退化为 composer update 的结果。它会重新解析整个依赖树,取当前满足约束的最新版本,行为不可控。
composer install 执行时到底做了什么
它按顺序做三件事:校验锁文件完整性 → 下载指定版本包 → 校验每个包的 SHA-256 哈希值是否匹配 composer.lock 记录 → 写入 vendor/autoload.php 和类映射。
关键点:
-
composer.lock不只是版本列表,还包含每个包的完整下载 URL、压缩包哈希、安装路径、依赖树快照 - 如果某包哈希校验失败(比如镜像源返回了被篡改的 zip),
composer install会中止,拒绝安装 - 它不检查 PHP 版本兼容性——那是
composer update阶段的事;install只管“还原”,不管“是否合理” - 即使你本地
composer.json里把php要求从^8.1改成^8.2,只要composer.lock没更新,install仍会装旧版依赖(可能已不支持 PHP 8.2)
什么时候会生成或覆盖 composer.lock
composer install 本身**从不生成或修改** composer.lock。它只消费这个文件。
真正触发锁文件变更的只有两个动作:
-
composer update:无论composer.lock是否存在,都会重新解析依赖、写入新锁文件 -
composer require xxx或composer remove xxx:先改composer.json,再隐式执行composer update xxx,从而更新锁文件 -
composer install在找不到composer.lock时,会先执行一次等价于composer update的操作来生成它,然后再继续安装——但这属于 fallback 行为,不是设计本意
所以如果你看到 CI 日志里出现 “Generating autoload files” 后紧跟着 “Writing lock file”,那说明项目根目录下根本没有提交 composer.lock,构建环境正在做一件本不该由它承担的事。
应用项目中 composer.lock 必须提交到 Git
这是硬性规则,不是建议。对 Laravel、Symfony、WordPress 插件这类应用型项目,composer.lock 和 composer.json 一样,是项目契约的一部分。
容易被忽略的细节:
- Git 忽略规则里写了
/vendor是对的,但绝不能写/composer.lock或composer.lock - 库项目(比如你写的 SDK 包)不提交
composer.lock,因为它不该决定下游用户的依赖版本 - 如果团队有人手动编辑
composer.lock(比如改哈希值绕过校验),会导致所有人install失败——这个文件只能由 Composer 自动生成 - CI/CD 流水线中,
composer install --no-dev依然严格依赖composer.lock;删掉它再加--no-dev,只会让生产环境装出和开发环境完全不同的依赖组合
最麻烦的情况不是锁文件缺失,而是它存在但和 composer.json 不同步——这时 composer install 会直接报错 Your lock file is out of sync,你得先想清楚:是要保留现有依赖(运行 composer install 并修复 json),还是接受变更(运行 composer update 并验证)。











