composer install --no-dev 不改变 composer.lock 中的 dev 依赖记录,安全审计需用 composer export --format=lock --without-dev 基于现有 lock 文件生成纯净生产环境依赖快照。

composer install --no-dev 导出的 lock 文件不等于生产依赖清单
很多人以为 composer install --no-dev 能直接生成纯生产环境的依赖快照,其实它只是跳过 require-dev 的安装,并不会改变 composer.lock 里已记录的全部包(含 dev 包)。安全审计需要的是「不含任何开发依赖的、可验证的依赖树」,必须从 lock 文件中剥离 dev 信息重新生成。
用 composer show --no-dev -f json 输出纯净生产依赖树
composer show 默认输出所有已安装包,加 --no-dev 才真正过滤掉 require-dev 中声明的包(注意:不是“未安装”的包,而是“不在 require-dev 列表里的包”)。配合 -f json 可导出结构化数据,便于后续解析或上传到 SCA 工具。
- 执行
composer show --no-dev -f json > prod-deps.json,得到仅含生产依赖的 JSON 清单 - 该命令不依赖当前是否已运行
install,只要composer.lock存在就能读取依赖关系 - 输出中每个包的
require字段仍可能包含 dev-only 包(比如某个生产包间接依赖了phpunit),但这种情况极少见;若需彻底排除传递性 dev 依赖,得用更重的方案(见下一条)
用 composer export --format=lock --without-dev 生成最小化 lock 子集
Composer 2.4+ 内置了 export 命令,专为审计场景设计。它不是简单过滤列表,而是基于 composer.lock 重建一个不含 require-dev 相关字段、且移除了所有 dev-only 包及其子依赖的新 lock 文件。
- 运行
composer export --format=lock --without-dev > composer-prod.lock - 生成的
composer-prod.lock可被composer install --no-dev正常消费,也能直接喂给 Dependabot、Snyk 等工具做比对 - 注意:该命令要求 lock 文件中各包版本已解析完成(即至少执行过一次
composer install或update),否则会报错Lock file does not contain required packages - 如果项目用了
platform-check或自定义repositories,export不会自动继承这些配置,需额外确认环境一致性
为什么不能直接删掉 composer.json 里的 require-dev 再 dump-autoload?
删 require-dev 后运行 composer update --lock 看似能“刷新” lock 文件,但实际风险很大:Composer 会重新计算整个依赖图,可能导致生产包版本漂移(比如原本靠 dev 包的约束锁住的某个次版本,现在松动升级了)。审计要的是「当前线上运行时的真实依赖快照」,不是「假设没 dev 时可能装出来的依赖」。
所以必须基于现有 composer.lock 做裁剪,而不是触发新的解析逻辑。这也是 export 比手动改 composer.json 更可靠的原因——它只做减法,不做重算。
真正容易被忽略的是:很多团队把 composer.lock 当作黑盒,却没意识到它的结构本身支持按角色切片。只要 Composer 版本够新,export 就是最轻量、最保真的方式。










