composer install --locked 是强制 Composer 严格校验并仅依据 composer.lock 还原依赖的唯一开关;若 lock 缺失、格式错误或与 composer.json 冲突,命令直接失败,绝不生成新 lock。

composer install --locked 是什么,为什么它才是“真·不更新 lock”的开关
Composer 没有 --no-lock 参数,也不存在“跳过 lock 文件”的合法方式;--locked 才是唯一能强制 Composer 尊重 lock 文件、且拒绝任何妥协的参数。它不是可选项,而是校验锁文件存在的硬性要求:如果 composer.lock 缺失、格式错误、或与 composer.json 中的约束冲突(比如 lock 里写了 "monolog/monolog": "3.0.0",但 json 里写的是 "^2.0"),命令会直接失败退出,绝不动手生成新 lock。
-
composer install --locked是 CI/CD 部署脚本的底线配置,搭配--no-dev --no-interaction --optimize-autoloader才算完整 - 不加
--locked时,即使 lock 存在,只要 vendor 目录损坏、权限异常、或某些插件干扰,Composer 可能 fallback 到重建 lock —— 这种“悄悄更新”比明面上的update更危险 - 注意:
--locked不等于“忽略 platform 检查”,若 PHP 版本不匹配,仍可能报错;需要--ignore-platform-reqs时,务必确认这是你真正想要的降级行为
为什么 composer install 有时会“偷偷改 lock”,而你以为它不该动
根本原因不是命令本身有问题,而是上下文破了“还原快照”的前提。最常见三种触发场景:
-
composer.lock文件存在但被手动编辑过(比如删掉某包、改了哈希),导致校验失败,Composer 自动重写整个 lock 来对齐 - 本地
vendor/目录残留旧包或符号链接失效,Composer 在检测不一致时选择“以 lock 为准重装”,过程中因权限或 symlink 失败,fallback 到解析依赖图并生成新 lock - 执行了
composer install --dry-run或--ignore-platform-reqs后又立刻跑普通install,某些版本的 Composer 会因内部状态混乱误判 lock 无效
验证是否真被改过?CI 脚本第一行加:git diff --quiet composer.lock || (echo "composer.lock changed!"; exit 1) —— 这比事后追查快得多。
composer.json 改了但不想更新 lock,怎么避免“被自动同步”
只要你没运行 composer update,composer install 本身不会读取 composer.json 的新内容。但有个关键例外:当 composer.lock 不存在时,install 会退化为 update 并自动生成 lock —— 这是破坏“只还原”语义的主因。
- 团队协作中,必须把
composer.lock提交进 Git;禁止在 .gitignore 里加它 - 改完
composer.json后,如果只是想测试安装效果,用composer install --dry-run查看会装什么,而不是直接跑install - 如果确实要加新包但暂不更新 lock,先
composer require --no-update vendor/package,再手动检查 lock 是否仍能通过composer validate --strict
锁定单个包版本 ≠ 禁止 update,这两件事得分开处理
用精确版本号(如 "monolog/monolog": "2.11.0")能防止 composer update 升级它,但这不等于 install 会绕过它——只要它在 lock 里,就一定会装。真正的“禁止参与依赖解析”只有两种落地方式:
- 把它移到
require-dev(前提是它真不参与运行时逻辑) - 用
composer update --ignore=monolog/monolog(Composer 2.2+ 支持),注意:这仅跳过该包自身更新,不影响其依赖项;若其他包强依赖更高版 monolog,仍会报 conflict
别碰 composer.lock 手动删包,也别信 "config": { "lock": false } 这类无效配置——Composer 会静默忽略它。


















