composer.lock才是依赖变更的唯一可信来源,因其固化了实际安装的完整依赖树、精确版本、dist哈希及平台约束;而composer.json仅声明意图,不反映真实安装状态。

composer.lock 文件才是依赖变更的唯一可信来源,只看 composer.json 的增删改等于没审。
为什么必须对比 composer.lock 而不是只读 composer.json
composer.json 是“声明意图”,而 composer.lock 是“执行快照”——它固化了整个依赖树的实际版本、源类型、dist hash 和平台约束。一次 composer update(哪怕没改 composer.json)可能把 psr/log 从 2.x 升到 3.x,触发 BC break;也可能悄悄把 monolog/monolog 的子依赖从 phpoption/phpoption 切换为 phpstan/phpstan 的某个内部 fork 版本。
常见错误现象:
• PR 中 composer.json 没动,composer.lock 几百行变更,reviewer 直接跳过
• CI 构建失败,报错 Class not found,但本地 composer install 正常——原因往往是 lock 文件里某包的 dist.shasum 指向了已失效的 ZIP,或平台条件(如 ext-redis)在 CI 环境不满足却未被 lock 显式约束
- 检查
content-hash字段是否变化:变了说明依赖图真实不同;没变大概率只是重生成(如换 PHP 版本后重 runcomposer install) - 重点盯
packages和packages-dev下每个包的version、source.type("type": "git"比"type": "package"更需警惕)、dist.shasum - 发现
"version": "dev-main"或"reference": "abc123"→ 必须确认是否真需指向不稳定分支或特定 commit
用 git diff 快速筛出关键变更点
逐行扫 composer.lock 效率极低。直接用结构化命令过滤新增/修改的包信息:
git diff --no-color HEAD~1 composer.lock | grep -E'^\+ {4}"(name|version|source|dist)'
这条命令能精准捕获加号开头的、缩进四格的字段,避开无意义的格式变动。实际使用时注意:
- 若项目用
composer install --no-dev上线,但packages-dev区域大量变动,通常可忽略;反之则要严查 - 同一包名出现多个版本(如
symfony/event-dispatcher同时存在6.3.2和7.0.0)→ 表明依赖冲突,Composer 已做降级处理,需回溯composer.json中哪些 require 引入了不兼容约束 - 对比前后
platform字段:PHP、ext 扩展版本变化会直接影响运行时行为,尤其在跨 major 版本升级时
audit 命令不是万能的,但它能补上人工 review 的盲区
composer audit 不扫描源码,也不检测私有包或未公开 CVE 的投毒行为,但它能快速比对 composer.lock 中每个包的 name + version 是否命中 Packagist 安全数据库里的已知漏洞条目。前提是你的 Composer ≥ 2.5.0 且启用了实验特性:
- 先确认支持:
composer --version和composer list | grep audit;若无输出,说明版本太低或未启用 - 启用方式:
composer config --global experimental.audit true(该配置写入全局auth.json) - 生产环境建议加
--no-dev --locked:只审计 lock 文件中锁定的生产依赖,避免 dev 包干扰判断 - 遇到
CVE-2023-12345类警告,别急着升级——先查该 CVE 影响范围是否覆盖你实际使用的 API,有些漏洞仅存在于特定配置或废弃方法中
依赖变更日志不能只靠人肉,得靠自动化钩子
人工 review 再仔细也会漏,尤其在高频迭代项目中。最有效的做法是把变更检测嵌入流程:
- CI 中加一步:
composer status --verbose,它会列出所有被本地修改过的包(比如有人手动改了 vendor 里的文件) - 用
composer outdated --direct检查显式声明的包是否过时,再配合--minor-only或--major-only控制升级粒度 - 部署前跑
composer install --dry-run -vvv 2>&1 | grep "Resolving dependencies",验证锁文件能否在目标环境成功解析(--dry-run不下载,但会走完整 solver 流程) - 别信
composer update --dry-run -vvv的输出——它跳过平台检查和部分缓存校验,真实执行仍可能失败
真正难的不是看到变更,而是判断哪一行 lock 变更会导致 runtime 异常。这需要把 composer.lock 当作和 schema.sql 一样严肃对待的可执行清单,而不是“自动生成就不用管”的副产品。











