composer install 会升级包是因为缺失或过期的 composer.lock 文件导致其退化为 update 行为;真正锁定版本需用精确版本号(如 "pkg": "1.2.3")并执行 composer update pkg,--lock 仅重写 lock 文件而不改版本。

composer install 时为什么还会升级包?
因为 composer install 只按 composer.lock 安装,但如果你本地没这个文件,或它被删了、过期了,Composer 就会退化成 composer update 行为——也就是重新解析依赖并拉最新兼容版本。
常见错误现象:git clone 项目后直接 composer install,结果装出来的包和队友不一样;CI 环境每次构建都“看似稳定”,实则版本漂移。
- 确认
composer.lock是否在 Git 中提交(必须提交) - CI/CD 流程里禁止执行
composer update,只允许composer install --no-interaction - 本地开发前先
git pull,再composer install,别手快敲update
怎么真正锁定某个包不更新?
靠 "package-name": "1.2.3" 这种精确版本写法,不是靠 ^ 或 ~。Composer 的版本约束本质是「允许的范围」,不是「当前装的版本」。
使用场景:你依赖的某个包刚发布了一个有 bug 的小版本(比如 v2.1.5),而你线上正用着稳定的 v2.1.4,需要立刻冻结。
- 改
composer.json里的对应行:"monolog/monolog": "2.1.4"(去掉^) - 运行
composer update monolog/monolog,它会降级/锁定到该精确版本,并更新composer.lock - 别用
composer require monolog/monolog:2.1.4,它默认加^,得加--no-update再手动改 JSON
composer update --lock 是干啥的?
它只重写 composer.lock,不碰 vendor 和 composer.json,常用于修复 lock 文件损坏或手动调整后同步校验和。
容易踩的坑:以为它能“刷新锁定”,其实它不会改变任何包的实际版本,也不会解决依赖冲突;如果 lock 文件里记录的是 1.2.3,执行完还是 1.2.3。
- 适用情况:
composer.lock被误编辑、Git 合并出错、校验和报错The lock file does not contain require-dev information - 不适用情况:你想升级某包、想降级某包、想解决未满足的依赖
- 执行后记得
git diff composer.lock看是否真有变化,没变化就白跑了
require-dev 包会影响生产环境版本吗?
会,只要它和 require 中的包存在共同依赖,就可能把生产依赖的版本顶高。比如你的 require 锁了 symfony/console:5.4.0,但某个 require-dev 包要求 symfony/console:^6.0,Composer 就不得不升到 6.x 来满足两者。
性能影响:dev 包本身不进生产代码,但它们的依赖图会拖慢 composer install 解析速度,尤其在 CI 上明显。
- 上线前用
composer install --no-dev,但它不能绕过依赖冲突,只能跳过安装 dev 包 - 定期清理无用的
require-dev,比如已迁走的测试工具、旧版 PHP-CS-Fixer - 对关键基础包(如
phpunit、mockery)也建议用精确版本,避免意外带偏主依赖
composer.lock 是唯一权威——它不是缓存,是契约。人手一删,契约就废。










