真正锁定 Composer 依赖需同时满足:composer.json 中写死三位版本号(如"monolog/monolog": "2.11.0")、提交 composer.lock 至 Git、部署时仅运行 composer install;缺一不可。

真正锁定 Composer 依赖,不是靠 composer.lock 文件本身,而是靠三件事同时成立:composer.json 里写死三位版本号、composer.lock 提交进 Git、部署时只跑 composer install。少一个,锁定就失效。
composer.json 怎么写才算真正锁定
必须用无修饰符的纯数字三位点号版本,例如 "monolog/monolog": "2.11.0"。任何带 ^、~、*、dev-、@dev 的写法都不算锁定:
-
"monolog/monolog": "^2.11"→ 允许升到2.12.0甚至2.99.9 -
"monolog/monolog": "2.11"→ 行为不一致:Composer 2.0+ 解析为2.11.0,旧版可能当2.11.x处理,别赌 -
"monolog/monolog": "2.11.0 as 2.11.0"可以,但没必要;"2.11.0"就够了
改完 composer.json 后必须立刻执行什么命令
只改 composer.json 不运行命令,等于没改。必须立即执行 composer update monolog/monolog(指定包名):
- 不能只跑
composer update(无参数):它会重新解析整棵树,可能连带升级其他包 - 不能删掉
vendor再跑composer install:如果composer.lock还没更新,install仍会装旧版 - 验证是否生效:
composer show monolog/monolog输出应为2.11.0;再跑一次composer update monolog/monolog,若提示Nothing to install or update,才算锁住
为什么 composer.lock 不是“开关”,而只是快照
composer.lock 本身不执行任何逻辑,它只是记录当前依赖树的快照:每个包的精确 version、dist.sha256、安装路径、完整依赖链。它起作用的前提是——你用 composer install 来读它:
- CI 或新同事执行
composer install时,完全忽略composer.json的约束,只认lock里的版本 - 如果
composer.lock被删或没提交 Git,composer install就退化成按composer.json重解析,哪怕你写了"2.11.0",也可能装上2.11.1(尤其镜像源缓存了新版本) -
composer install --locked是上线前最后一道保险:它校验lock里每个版本是否仍满足composer.json的约束,不满足直接报错退出
临时跳过某个包更新的实操方式
这不是长期锁定,而是单次操作绕过,适用于调试兼容性问题或 CI 中临时压制某包变更:
- Composer 2.2+:
composer update --ignore=laravel/framework,跳过该包及其子依赖的更新检查 - 兼容旧版:显式列出要更新的包,漏掉那个就行,例如
composer update symfony/console phpunit/phpunit
最容易被忽略的是:composer.lock 必须随代码一起提交,且上线脚本里不能出现 composer update。哪怕 composer.json 写得再死,只要部署时跑了 update 或者 lock 文件缺失,版本就不可控。


















