composer show vendor/package-name 是查单个包许可证最可靠方式,直接读取 vendor/ 下真实 composer.json 的 license 字段,需先执行 composer install;批量查看用 composer show --format=json | jq -r '.packages[] | "(.name) (.license // "UNKNOWN")"';无 vendor/ 时可用 jq 解析 composer.lock,但有漏检风险;license 兼容性须以 spdx.org/licenses 为准。

composer show vendor/package-name 是最可靠的第一步
直接查单个包的许可证,composer show 是唯一原生、不依赖插件、不依赖外部服务的方式。它读取的是该包 vendor/ 目录下真实 composer.json 里的 license 字段,不是推测,也不是缓存。
- 必须已执行过
composer install,否则vendor/为空,composer show会报错Package not found - 输出中
license:后的值可能是字符串(如MIT)、数组(如["BSD-3-Clause", "GPL-2.0-only"])或空(显示为license:或压根不显示该行) - 遇到
see LICENSE in LICENSE.md或proprietary这类非 SPDX 标识符的值,不能跳过——必须手动打开对应文件核对全文,SPDX 官方不认模糊描述
批量看所有包的 license:用 --format=json + jq 最稳
composer licenses 不是 Composer 内置命令,绝大多数环境执行会报错 Command "licenses" is not defined.。别信网上过时教程里写的“直接运行”,那是装了插件(比如 zicht/composer-license-plugin)后的效果,不是默认行为。
- 真正跨环境可用的方案是:
composer show --format=json | jq -r '.packages[] | "\(.name) \(.license // "UNKNOWN")"' - 这个命令不依赖插件,只依赖
jq(Linux/macOS 通常自带,Windows 可装 jq-win64) -
.license // "UNKNOWN"是关键:处理null或缺失字段,避免 jq 报错退出 - 注意:它只读
vendor/下已安装包的composer.json,不会递归进嵌套依赖的 dev 包,但覆盖了生产环境实际加载的全部依赖
想绕过 vendor/?解析 composer.lock 更轻量,但有盲区
如果你在 CI 中还没跑 composer install(比如只做了 composer update),vendor/ 还不存在,这时只能靠 composer.lock ——它是合法 JSON,且记录了所有解析出的依赖快照。
- 用这条命令:
jq -r '.packages[] | select(.license != null) | "\(.name)\t\(.version)\t\(.license | join(" | "))"' composer.lock - 好处是不依赖
vendor/,执行快,适合早期合规扫描 - 坑在于:有些包只在
packages-dev数组里声明license,或嵌套依赖根本没写license字段(即使其composer.json有),composer.lock就不会收录,导致漏检 - 另外,
composer.lock中的license是安装时抓取的快照,如果包作者后来改了composer.json里的声明,这里不会自动更新
license 兼容性不能靠猜,SPDX 是唯一事实源
看到 GPL-2.0-only 和 GPL-3.0-or-later 并列,别以为“都是 GPL 就能混用”。语义差得远:-only 表示严格锁定版本,-or-later 表示可升级——两者不可互换,混用可能直接违反许可条款。
- 判断是否兼容,唯一权威来源是 spdx.org/licenses,不是博客、不是 Stack Overflow,也不是你同事说的“应该没问题”
- 常见陷阱:
Apache-2.0和GPL-2.0-only不兼容;MIT和几乎所有主流许可都兼容;AGPL-3.0对网络服务有传染性,商用 SaaS 必须警惕 - CI 中真要自动化拦截,建议导出 JSON 后用 Python 或 shell 脚本过滤关键词,例如:
jq -r '.packages[] | select(.license | contains("AGPL") or contains("SSPL")) | "\(.name) \(.license)"' licenses.json
许可证不是元数据装饰,是法律约束。最常被忽略的点是:90% 的问题不出在 MIT/Apache 这些宽松许可上,而出现在那些写成 see license in COPYING、proprietary、或者干脆留空的包里——它们需要你点开源码、逐字读完 LICENSE 文件才能确认真实意图。










