根本原因是团队成员误执行 composer update、未提交或篡改 composer.lock;只要 lock 文件被正确提交且只运行 install,版本必一致。

为什么 composer install 会装出不同版本?
根本原因不是 Composer 本身“不一致”,而是团队成员执行了 composer update 或本地有未提交的 composer.lock,又或者有人删了 lock 文件重生成。只要 composer.lock 被提交且所有人只运行 composer install,版本就必然一致。
常见错误现象:composer install 后 vendor/ 里某个包的版本和别人不一样;CI 构建失败,提示 “package X has different version than expected”;git status 显示 composer.lock 被修改但没人动过依赖。
- 别让任何人手动运行
composer update—— 这是最大风险源 - 确保
composer.lock在 Git 中已跟踪且未被.gitignore排除 - 检查 CI 脚本是否误用了
composer update(比如加了--no-interaction就以为安全) - 确认所有环境都使用相同 PHP 版本和 Composer 版本(
composer --version),不同 Composer 大版本(如 1.x vs 2.x)可能生成结构不同的 lock 文件
如何让 composer install 拒绝 lock 文件不匹配?
Composer 本身不校验 lock 文件内容是否“合理”,但它提供了一个硬性开关:一旦启用,只要 composer.json 和 composer.lock 的 hash 不匹配,就直接报错退出。
这个开关就是 lock 配置项,需写进 composer.json:
{
"config": {
"lock": true
}
}
启用后,如果有人改了 composer.json(比如加了个 require)但没跑 composer update 更新 lock,再执行 composer install 就会报错:
The lock file does not contain the required package "monolog/monolog".
- 这个配置只影响
install,不影响update;它不防止你升级依赖,只防止“装错” - PHP 7.4+ / Composer 2.2+ 才支持该配置,旧版本忽略它且不报错
- 不要把它和
composer validate混淆——后者只检查 JSON 格式,不校验 lock 一致性
Git 预提交钩子自动检测 lock 文件变更
光靠约定或文档拦不住手抖,最稳妥的是在代码提交前自动检查:如果 composer.json 被改了,但 composer.lock 没同步更新,就拒绝提交。
用一个简单的 shell 脚本就能做到:
#!/bin/sh
if git status --porcelain | grep -q '^[AM] composer.json'; then
if ! git status --porcelain | grep -q '^[AM] composer.lock'; then
echo "ERROR: composer.json changed but composer.lock not updated."
echo "Run 'composer update' and commit both files."
exit 1
fi
fi
把这个存为 .git/hooks/pre-commit 并加执行权限即可。注意:Windows 用户需用 Git Bash 或适配 PowerShell 版本。
- 该脚本不替代
lock: true,而是补前端防线;两者叠加才真正堵死疏漏 - 别把钩子脚本塞进项目仓库(
.git/hooks/不受 Git 管理),推荐用lefthook或husky统一管理并纳入版本 - CI 流水线里也建议加一步:
composer install --dry-run,它会模拟安装并报出 lock 不一致问题,不实际写文件
团队协作中容易被忽略的三个细节
很多团队踩坑不是因为不懂命令,而是忽略了这些隐性条件。
-
composer.lock必须用 Unix 换行符(LF),Windows 用户若 Git 自动转 CRLF,可能导致 hash 计算差异,锁文件“看起来一样”却校验失败 - 某些 IDE(如 PHPStorm)默认勾选 “Synchronize composer.json and composer.lock”,它会在你编辑 json 时偷偷执行
update—— 关掉这个选项 - 如果你用 Docker,确保容器内运行的 Composer 版本和本地一致;镜像里用
composer:2,本地却是composer v1.10,生成的 lock 文件格式不同,lock: true也会失效
锁文件不是摆设,它是契约。契约生效的前提,是所有人读同一份文本、用同一支笔签字——差一行换行符,差一个 patch 版本,都可能让整条流水线卡住。










