composer show --licenses --recursive --format=json是唯一能覆盖全部依赖(含子依赖)且输出结构化数据的原生命令;composer licenses仅读取项目根composer.json的license字段,不扫描vendor/,不支持--recursive,无法满足合规审计需求。

直接用 composer show --licenses --recursive --format=json,这是唯一能覆盖全部依赖(含子依赖)且输出结构化数据的原生命令。
为什么 composer licenses 不行?
它只显示顶层包的 license 字段,跟 vendor/ 里实际装了什么完全无关。运行后看到的全是你的项目根 composer.json 里写的 "license": "MIT" 这类信息,所有第三方依赖都被跳过。这不是 bug,是设计如此——它压根不读 vendor/ 目录。
常见错误现象:composer licenses 输出只有 1 行,但 vendor/ 里有上百个包;或者跑完发现没列 monolog/monolog、guzzlehttp/guzzle 这些核心依赖。
-
composer licenses是伪命令:Composer 2.2+ 才内置该命令,旧版本直接报错Command "licenses" is not defined - 它默认不输出
unknown或空 license 的包,等于主动过滤掉高风险项 - 不支持
--recursive,无法展开依赖树,对合规审计毫无意义
composer show --licenses --recursive 怎么用才有效?
这个命令才是真正遍历 vendor/ 下每个已安装包,并读取其 composer.json 中的 license 字段。但必须满足两个前提:
- Composer 版本 ≥ 2.2,否则报错
Unrecognized option: --recursive - 先执行过
composer install或composer update,确保vendor/存在且完整
推荐组合:
composer show --licenses --recursive --format=json > licenses.json
加 --no-dev 可排除开发依赖(如 phpunit/phpunit),适合发布前审计:
composer show --licenses --recursive --no-dev --format=json > prod-licenses.json
注意:--format=json 是关键,否则默认纯文本格式无法被脚本可靠解析。
license 字段为空或显示 unknown 怎么办?
这非常普遍,不是命令失效,而是包作者没填、填错、或填了非标准值(比如 "BSD" 而非 "BSD-3-Clause")。composer show 只读字段,不校验也不猜测。
此时不能跳过,必须人工核查:
- 进对应包的源码仓库(GitHub/GitLab),看根目录是否存在
LICENSE、LICENSE.md等文件 - 别信 Packagist 页面上“自动提取”的 license 标签,它和实际代码仓可能不同步
- 特别注意
"proprietary"、"unlicensed"、"custom"这类值,它们不表示合规,只是标记“需单独确认”
真正麻烦的是那些 composer.json 没声明 license,但源码里有完整 MIT 或 Apache-2.0 文本的包——字段为空,不代表无许可,也绝不等于可随意商用。
导出后怎么快速筛出 GPL 类高风险包?
JSON 结构是扁平数组,每项含 name、version、license 字段,适合用 jq 过滤:
cat licenses.json | jq -r 'map(select(.license | ascii_downcase | contains("gpl") or contains("agpl") or contains("sspl"))) | .[] | "\(.name) \(.version) \(.license)"'
如果没装 jq,临时用 grep -i 也行,但注意它会漏掉 license 是数组的情况(如 ["MIT", "GPL-2.0-only"]):
cat licenses.json | grep -i -E '"license"\s*:\s*"[^"]*(gpl|agpl|sspl)[^"]*"' | head -20
更稳的做法是先用 jq 展开数组字段再匹配,但多数合规初筛用上面第一种就够了。
license 字段不可信是常态,导出只是起点。真正卡点在于对 unknown 和模糊值的逐个溯源——这步跳不过,也没工具能全自动替代。











