composer update 默认更新所有插件,它忽略 composer.lock 并依据 composer.json 的版本约束重算依赖树,可能导致不兼容升级;而 composer install 严格按 composer.lock 安装精确版本,保障环境一致。

直接更新所有插件?别急——composer update 默认就是这么干的,但多数人误以为它“安全”,其实它会无视 composer.lock 强制重算依赖树,很可能引入不兼容版本。
为什么 composer update 会升级到意外版本?
它不看 composer.lock 中已锁定的精确版本,而是重新从 composer.json 的版本约束(如 "^2.0" 或 "~3.1")出发,向仓库拉取满足条件的最新可用包。哪怕只改了一个小版本号(比如 symfony/console 从 6.3.0 升到 6.4.0),也可能触发整条依赖链重解析,连带升级其他间接依赖。
常见错误现象:
- 本地运行正常,CI 构建失败
- phpunit 突然报 Class "PHPUnit\Framework\TestCase" not found
- 第三方插件调用的接口在新版本里被废弃
实操建议:
- ✅ 永远先 git status 确认没未提交变更
- ✅ 运行前加 --dry-run 预览: composer update --dry-run
- ✅ 避免在生产环境或 CI 中直接执行,应先在开发分支验证
只想更新某几个插件,而不是全部?
指定包名即可,Composer 会保留其余包在 composer.lock 中的版本,只重解你列出的依赖及其子依赖。
例如:
- 更新 Laravel 核心和其生态插件:composer update laravel/framework laravel/tinker
- 只更新开发依赖中的 PHPUnit:composer update --dev phpunit/phpunit
- 排除某个包不更新(比如固定 guzzlehttp/guzzle 版本):composer update --with-dependencies --ignore=guzzlehttp/guzzle(需 Composer 2.5+)
注意:
- 多个包名之间用空格分隔,不能用逗号
- 如果某插件是其他包的子依赖(如 monolog/monolog 被 laravel/framework 声明),单独更新它可能被主包的约束覆盖,此时要配合 --with-dependencies
composer update 和 composer install 的根本区别在哪?
composer install 是“按锁安装”:读 composer.lock,下载其中记录的**确切版本**(包括哈希校验),确保所有人、所有环境行为一致;composer update 是“按需重算”:忽略锁文件,按 composer.json 重新求解最优解,本质是**一次新的依赖决策过程**。
使用场景判断:
- ✅ 新克隆项目、CI 构建、上线部署 → 用 composer install
- ✅ 开发中要尝鲜新特性、修复安全漏洞、或主动升级某组件 → 用 composer update
- ⚠️ 如果 composer.lock 不存在,install 会自动退化为 update 行为,这常被忽略
更新后发现 break,怎么快速回滚?
Composer 本身不提供原子回滚,但你可以借助 Git 和锁文件:
composer.lock 就是你的“依赖快照”。只要它被纳入 Git:
- 回退锁文件:git checkout HEAD -- composer.lock
- 重装锁定版本:composer install
- 若已提交了新锁文件,用 git revert 或 git reset 撤销那次提交
容易被忽略的点:
- 不少团队把 composer.lock 加进 .gitignore,导致无法追溯依赖状态
- composer update 后没提交 composer.lock,下次别人 install 时实际安装的是旧锁文件里的版本,造成环境不一致










