composer install 装旧包或新包取决于 composer.lock 是否存在及内容:有有效 lock 文件则严格按其安装;无 lock 则按 composer.json 版本范围解析最新兼容版并生成新 lock。

composer install 为什么有时装旧包,有时装新包?
因为 composer install 的行为完全由 composer.lock 文件是否存在和内容决定,跟 composer.json 里的版本范围无关。
- 如果项目目录下有有效的
composer.lock,composer install就只照着它装——哪怕composer.json写着"monolog/monolog": "^3.0",而 lock 里锁的是3.5.2,那就装3.5.2,绝不升级 - 如果没有
composer.lock,composer install会重新解析composer.json,按语义化版本规则取满足条件的最新兼容版(比如^3.0可能装上3.99.0),然后生成新的 lock 文件
常见错误现象:
- 新人 clone 项目后删了
composer.lock再install→ 装了一堆新版本,本地跑崩,但 CI 没问题 - 团队有人提交了改过的
composer.json却没运行composer update→ lock 文件没更新,别人install还是旧依赖,功能缺失却不报错
什么时候该用 composer update?
composer update 是唯一能“刷新 lock 文件”的命令,但它不是日常操作,而是有明确升级意图时的主动决策动作。
- 它强制忽略
composer.lock,重新读取composer.json,计算并安装所有依赖的最新兼容版本,再重写 lock - 一般只在以下场景使用:
- 需要升级某个特定包:
composer update monolog/monolog - 想批量更新开发依赖:
composer update --dev - 项目长期未维护,需要整体拉齐安全补丁:
composer update --with-all-dependencies(但务必先测)
- 需要升级某个特定包:
- 绝对避免在没有测试保障时执行裸
composer update—— 它可能把symfony/console从5.4.39升到6.4.0,而你的代码还调着已废弃的getHelperSet()
git 提交时漏掉 composer.lock 会怎样?
后果直接且隐蔽:团队环境彻底不可控。
-
composer.lock不提交 = 每个人的vendor目录都是“薛定谔的依赖” - CI 流水线可能用缓存装了某版
phpunit,而你本地install出来的是另一个小版本,断言行为不一致,测试忽过忽挂 - 生产部署若依赖
install,但没 lock 文件,就会按当时网络状态、Packagist 响应顺序装包——同一份代码,两次部署可能产出不同vendor
必须做到:
-
composer.lock和composer.json一起进 git,且不能加进.gitignore - 合并 PR 前检查 lock 文件是否变更;如有冲突,不要手动编辑,而是用
composer update重新生成,并验证功能 - CI 脚本第一行建议加
ls -l composer.lock,防止误删后静默失败
require 和 require-dev 在 lock 文件里怎么体现?
composer.lock 里明确分开了 packages 和 packages-dev 两个字段,但它们的处理逻辑完全不同。
-
packages:所有require的包 + 它们的传递依赖,会被composer install和composer install --no-dev一并安装(后者只是不装 dev 包) -
packages-dev:只含require-dev声明的包(如phpunit/phpunit、friendsofphp/php-cs-fixer),且仅当没加--no-dev才装
关键细节:
- 即使你在生产环境运行
composer install --no-dev,lock 文件里的packages-dev字段仍存在,只是被跳过 - 修改
require-dev后执行composer update,lock 文件中packages-dev会更新,但packages不变(除非 dev 包本身也出现在 require 里) - 有些工具(如 PHPStan)被误放在
require-dev,但 CI 又没装 dev 包 → 直接报 command not found,这种错很难一眼定位
composer.lock 不是备份文件,它是 Composer 工作流的“事实来源”。很多人把它当成可删可改的中间产物,其实它一旦生成,就定义了当前项目的依赖宇宙——改它,等于改契约;绕过它,等于放弃确定性。










