composer outdated显示可升但update未执行,根本原因是版本约束限制(如"^2.0"拒绝v3),需先修改composer.json约束再update;更新后必须验证composer.lock中version和reference是否匹配预期,且提交lock文件确保ci环境一致性。

直接结论:不核对 composer.lock 与 composer.json 的约束匹配性,所谓“安全更新”就是裸奔。
为什么 composer outdated 显示可升,但 composer update 却没动?
常见错误现象:composer outdated --direct 显示 monolog/monolog 2.8.0 → 3.5.0,但执行 composer update monolog/monolog 后版本仍是 2.8.0。
根本原因不是网络或缓存,而是 composer.json 里写的是 "monolog/monolog": "^2.0" —— 这个约束明确拒绝 v3,outdated 只报“有新版本”,不校验你是否允许它进来。
- 必须用
composer show monolog/monolog确认当前安装版、composer.json中声明的约束、以及该约束实际能覆盖的最新兼容版本 - 若想升到 v3,得先改
composer.json为"monolog/monolog": "^3.0",再composer update monolog/monolog - 改约束后务必跑
composer validate --strict,检查是否与其他包的conflicts或require冲突
升级后怎么确认 composer.lock 没被悄悄绕过?
composer.lock 不是生成物,是契约。一次“安全更新”若没在 lock 文件里留下对应变更,等于没发生。
真实翻车场景:CI 脚本里写了 composer install,但上一次 composer update 是本地手动运行、未提交 composer.lock;结果 CI 安装的是旧 lock 里的旧依赖,漏洞还在。
- 每次
composer update后,必须git status确认composer.lock有修改且已暂存 - CI 中加一步:
composer install --dry-run,它会报错 “Your lock file does not contain the required package XYZ”,立刻暴露 lock 和 json 不一致 - 禁止在 CI 中用
composer update—— 它不该出现在自动化流程里,只允许install+validate --strict
如何从 composer.lock 反向验证某次更新是否真正落地?
别信日志,看锁文件本身。v2 格式的 composer.lock 是 JSON,人类可读性差,但关键字段很直白。
比如刚升级了 guzzlehttp/guzzle,直接查 lock 文件里是否有:
"packages": [
{
"name": "guzzlehttp/guzzle",
"version": "7.8.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "abc123..."
}
}
]
- 对比
version字段是否为你预期的版本号 - 检查
reference是否是该 tag 对应的真实 commit hash(不是分支名) - 若用了
config.platform,还要确认platform-check块里是否包含你声明的 PHP 版本和扩展,否则该包可能被跳过安装
最易被忽略的一点:composer.lock 里每个包的 dist 块含 shasum,它和下载时校验的 SHA1 严格对应。如果你手动改过 lock 文件、或用非官方镜像导致 checksum 不匹配,composer install 会静默失败——但不会报错,只会装回旧版。核对必须落到这一行。











