composer.lock才是版本锁定的真正执行者,它记录所有包的确切版本、哈希值和依赖树,只要存在且被提交,composer install 就严格还原;团队必须提交该文件,CI/CD 应用 install 而非 update。

composer.lock 文件不是摆设,它才是版本锁定的真正执行者
运行 composer update 时自动升级,根本原因不是没写死版本号,而是你没提交或没信任 composer.lock。这个文件记录了当前所有包的确切版本、哈希值和依赖树,只要它存在且未被忽略,composer install 就会严格还原——哪怕 composer.json 里写的是 "monolog/monolog": "^2.0"。
- 团队协作中,必须把
composer.lock提交进 Git;删掉它等于放弃版本控制 -
composer install只读composer.lock,不碰composer.json的约束范围;composer update才会重新解析并更新 lock 文件 - CI/CD 流程里务必用
composer install,而不是composer update—— 后者在构建时升级包是高危操作
版本号写法决定“锁得多死”,^ 和 ~ 的行为差异很关键
^ 和 ~ 看似都表示“兼容性升级”,但实际放开的范围差很多:^1.2.3 允许升级到 1.999.999(即下一个主版本前),而 ~1.2.3 只允许到 1.2.999(即下一个次版本前)。线上项目建议优先用 ~ 或直接写死。
- 想彻底禁止任何升级?直接写具体版本,如
"guzzlehttp/guzzle": "7.5.0"(不带符号) - 用
^时要清楚:PHP 包主版本升 8.x 可能引入 BC break,即使 Composer 认为“兼容” - 运行
composer show guzzlehttp/guzzle可查当前解析出的实际版本,比猜更可靠
composer update 不加参数就升级全部,这是最常踩的坑
很多人只记得“要锁版本”,却在维护时随手敲 composer update,结果全量刷新 lock 文件——连 dev-dependencies 都可能被拉新,测试工具版本一变,phpunit 的断言方法就报错。
- 只更新某一个包:用
composer update vendor/package-name,例如composer update symfony/console - 只更新开发依赖:加
--dev或-d,如composer update --dev phpunit/phpunit - 想更新但保留主版本?用
--with-dependencies谨慎配合,别默认开启
vendor 目录不该进 Git,但 lock 文件的变更必须人工审核
有人为“确保一致”把整个 vendor/ 提交进仓库,这会让 Git 压力暴增、diff 失效、合并冲突频发。正确做法是靠 composer.lock + composer install 还原,但前提是每次 lock 变更都要肉眼确认。
- 执行
composer update后,先git diff composer.lock,看是否意外升级了关键包(比如doctrine/dbal从 3.6 升到 4.0) - 某些包(如
laravel/framework)的次版本更新也可能含破坏性改动,不能只看主版本号 - CI 构建失败时,第一反应不该是重跑,而是检查
composer.lock最近一次提交改了哪些包










