真正固定依赖必须在 composer.json 中使用精确版本号(如"monolog/monolog": "2.9.1"),去掉^~等修饰符;仅靠 composer.lock 不可靠,需结合 composer.json 的明确声明才能确保版本不变。

直接在 composer.json 中把依赖写成带具体版本号的字符串,就能固定它——Composer 默认的 ^ 或 ~ 会自动放宽约束,不写这些符号才是真锁定。
用精确版本号替代语义化版本范围
很多人误以为写 "monolog/monolog": "^2.9" 是“固定到 2.9”,其实它允许升级到 2.99.0 甚至 2.10.0(只要主版本仍是 2)。真正固定必须去掉所有修饰符:
-
"monolog/monolog": "2.9.1"→ 只安装且只接受2.9.1这一个版本 -
"guzzlehttp/guzzle": "7.8.0"→ 不会升级到7.8.1,哪怕有安全补丁 - 如果写成
"symfony/console": "6.4"(缺小版本),Composer 会按最低匹配规则解析为6.4.0,但依然可能被composer update升级到6.4.1—— 必须写全三位
执行 composer update 时跳过某包升级
临时防止某个已安装包被更新,不用改 composer.json,直接指定包名运行:
composer update --with-dependencies laravel/framework
但更稳妥的做法是先锁死版本再更新:
- 先运行
composer show laravel/framework查看当前装的是哪个精确版本 - 把它填进
composer.json的require字段,格式如"laravel/framework": "10.32.1" - 再运行
composer update laravel/framework—— 此时它只会重装该版本,不会升
用 composer.lock 固定但不可靠
composer.lock 确实记录了每个包的精确版本和 hash,composer install 会严格按它来装。但它不是“固定策略”,而是“快照结果”:
- 一旦有人运行
composer update xxx或删了composer.lock再install,约束就失效 - 团队协作中,若没提交
composer.lock,或有人本地手动改过composer.json后忘了update,实际装的版本就会漂移 - 所以仅靠
lock文件不能替代composer.json中的精确版本声明
真正可靠的固定,是让 composer.json 自身就表达不可变意图;否则每次 composer update 都是一次潜在的意外升级触发点。










