composer status 标记“modified”表示 vendor 中某包源码与 composer.lock 记录的原始压缩包哈希不一致,需用 composer status -v 定位包,再通过 git status 或 diff 对比具体修改;composer.lock 是依赖快照,决定实际安装版本,而 composer.json 仅为版本范围声明。

composer status 显示“modified”但看不出改了哪行
直接跑 composer status 只会告诉你哪些包的源码被改过,不显示具体文件或 diff。它本质是比对 vendor/ 里已安装的代码和原始包发布时的压缩包哈希(来自 composer.lock),一旦有本地修改就标为 modified。
真正要看改了什么,得进对应目录手动对比:
- 先用
composer status -v确认是哪个包(比如输出monolog/monolog modified (1 file)) - 然后
cd vendor/monolog/monolog,再执行git status(前提是该包带 .git,很多 packagist 包不带) - 更通用的办法:用
diff -r vendor/monolog/monolog ~/.composer/cache/files/monolog/monolog/<hash>.zip.extracted/</hash>—— 但这个路径得自己拼,<hash></hash>要从composer.lock里找"monolog/monolog"下的"dist": {"shasum": "..."}
composer.lock 和 composer.json 的差异不是“配置 vs 锁定”,而是“声明 vs 快照”
composer.json 是你写的依赖声明,比如 "php": "^8.1" 或 "guzzlehttp/guzzle": "^7.5",它只管范围,不管具体版本;composer.lock 是某次 composer install 或 update 生成的完整快照,记录了每个包的确切版本、下载地址、哈希值、依赖树结构,甚至 autoloader 映射。
关键区别在于:删掉 composer.lock 再跑 composer install,结果可能和原来完全不同——因为 ^7.5 现在可能解析出 7.8.1 而不是之前的 7.5.0,尤其当上游发了新 patch 版本时。
-
composer.json改了但没 runcomposer update→composer.lock不变,vendor/不更新 -
composer.lock手动改了(比如调了某个子依赖的 version 字段)→ 下次composer install会按新 lock 装,但可能和composer.json声明冲突,触发警告 - CI 环境只跑
composer install(不带 –no-lock)→ 它只认composer.lock,完全忽略composer.json里的 range 声明
审计时发现 lock 文件里有未声明的包,大概率是间接依赖升级带进来的
composer.lock 里出现 symfony/polyfill-mbstring 这种包,但在 composer.json 里找不到,不是误加,而是某个你直接声明的包(比如 symfony/console)在升级后悄悄把它的 require 列表里加了 polyfill —— Composer 会自动拉取整条依赖链,全部记进 lock。
想确认来源,用这个命令:
composer depends symfony/polyfill-mbstring
它会列出谁 require 了它。如果输出为空,说明是 root package 自己 require 的(即你项目里写了),否则就是透传下来的。
- 这种“幽灵依赖”容易被忽略,但它参与 autoloading、影响攻击面,审计时不能跳过
- 某些安全扫描工具(如
composer audit)只查composer.lock,不看composer.json,所以即使你没直接写,只要 lock 里有就得管 - 想锁死间接依赖版本?不行。Composer 不支持 pin 二级依赖,只能靠
composer update foo/bar --with-dependencies控制升级范围
composer update 后 lock 变了但 json 没动,怎么知道哪些包实际升级了
composer update 默认只更新 composer.json 里声明的包及其子依赖,变化全记在 composer.lock。但光看 lock 文件 diff 很难定位“哪个包升了主版本”。用这个组合更准:
- 先备份旧 lock:
cp composer.lock composer.lock.bak - 跑
composer update --dry-run看计划升级哪些(但不真实执行) - 真实更新后,用
git diff composer.lock.bak composer.lock,重点搜"version":行,注意前后双引号里的值,比如"version": "3.4.2"→"version": "4.0.0" - 更省事:用
composer show -l(list outdated),它只列那些有新版且满足composer.jsonrange 的包,但不反映实际是否已升级
别依赖 ide 或文本比较工具高亮 JSON 结构——lock 文件里字段顺序不固定,diff 容易误判。老老实实按 version 字段肉眼扫,或者写个简单脚本提取前后 version 对比。
lock 文件的哈希和 dist 信息才是可信锚点,json 里的 caret 或 tilde 版本只是建议。审计时盯着 lock,而不是相信 json 写了什么。