🧪 类型化返回值
Composer Skills 区别于"手写 exec.Command"的核心价值之一:所有有意义的命令都返回结构化的 Go 类型,而不是原始字符串。本页列举主要的类型化返回值,并对比字符串解析的痛苦。
🔄 结构化返回的数据流
💔 字符串解析的痛苦
go
// 老方法:拿到一段文本,自己切分、正则、猜列宽
out, _ := exec.Command("composer", "audit", "--format=json").Output()
// out 是 []byte,你得:
// - json.Unmarshal 到一个自己捏的结构体
// - 字段名猜错 → 空值
// - Composer 升级改 schema → 静默失效不仅写起来烦,测试也难:你得 mock 整段子进程。Composer Skills 把这些结构体定义好、测好,直接返回给你。
✅ 类型化返回值一览
下面是最常用的结构化返回类型,都来自 pkg/composer:
| 类型 | 获取方法 | 含义 |
|---|---|---|
🔒 AuditResult | AuditWithJSON() | 安全审计结果:漏洞数、公告列表、严重等级 |
📊 AuditInfo | GetAuditInfo() | 审计的汇总信息(抽象后的高层视图) |
📦 OutdatedResult / OutdatedInfo | OutdatedPackages() / GetOutdatedInfo() | 过期包列表:当前版本、最新版本、是否直接依赖 |
🏷️ VersionInfo | GetVersionInfo() | Composer 自身版本信息 |
📋 PackageInfo | GetPackageInfo(name) | 某个已安装包的结构化信息 |
✅ ValidateResult | ValidateStructured() | composer.json 校验结果:是否合法、错误列表 |
🖥️ PlatformReqs | CheckPlatformReqsStructured() | 平台要求检查结果(PHP 版本、扩展) |
📄 LicensesInfo | GetLicensesInfo() | 项目所有依赖的许可证信息 |
⚙️ ConfigInfo | GetConfigStructured() | Composer 配置的结构化视图 |
🔍 SearchInfo | SearchInfo(query) | 本地搜索结果 |
🩺 DiagnoseInfo | DiagnoseStructured() | composer diagnose 的结构化结果 |
命名规律
后缀为 WithJSON 的方法返回 Composer 原始 JSON 解析后的结构体(字段最全);后缀为 Info 或 Structured 的方法返回 SDK 抽象后的高层视图(更好用)。两者都类型安全。
🌰 示例:结构化审计
go
result, err := comp.AuditWithJSON()
if err != nil {
log.Fatal(err)
}
fmt.Printf("漏洞总数: %d\n", result.Found)
for _, v := range result.Advisories {
fmt.Printf(" ⚠ %s: %s\n", v.Package, v.Title)
fmt.Printf(" 严重等级: %s\n", v.Severity)
fmt.Printf(" CVE: %s\n", v.CVE)
fmt.Printf(" 修复版本: %s\n", v.Solution)
}
// CI 失败判定,一行搞定
if result.Found > 0 {
os.Exit(1)
}对比字符串解析:你拿到的是 result.Found(int)、v.Package(string)——编译器替你检查类型,不存在"列宽变了导致下标越界"。
🌰 示例:过期包监控
go
outdated, err := comp.GetOutdatedInfo()
if err != nil {
log.Fatal(err)
}
for _, p := range outdated.Packages {
fmt.Printf("%s: %s → %s (direct: %v)\n",
p.Name, p.Version, p.Latest, p.DirectDependency)
}🌰 示例:composer.json 校验
go
res, err := comp.ValidateStructured()
if err != nil {
log.Fatal(err)
}
if !res.Valid {
for _, e := range res.Errors {
fmt.Printf("❌ 第 %d 行: %s\n", e.Line, e.Message)
}
}🌐 Packagist API 也是类型化的
两套 SDK 一脉相承——pkg/client 同样返回结构体:
go
pkg, _ := c.GetPackage("monolog/monolog")
// pkg.Package.Name / .Description / .Versions ... 全是结构化字段
advisories, _ := c.GetSecurityAdvisories()
// advisories.Advisories 是 []Advisory,每条带 Package/Title/Severity
stats, _ := c.GetStatistics()
// stats.Packages / .Downloads 是 int💡 为什么结构化返回值重要
- 🛡️ 可靠性:字段名由 SDK 绑定,Composer 输出微调不会让你的程序静默出错。
- 🧪 可测试性:SDK 内置
SetupMockOutput,测试时注入结构化预期,无需真跑 Composer。 - 🤖 可组合:拿到
AuditResult后,可以直接喂给报告生成器、CI 失败判定、告警系统。 - 📖 可读性:
result.Found > 0比strings.Contains(out, "Vulnerabilities")清楚一百倍。
注意返回值命名
部分方法名以 WithJSON 结尾,返回 Composer 原始 JSON 的直接映射,字段最全但也最"原始";如果你只想要高层抽象,优先选 Get*Info 或 *Structured 命名的变体。两者都返回类型化结构体。
🧭 下一步
- 🔒 安全审计 SDK ——
AuditWithJSON等方法的完整签名与字段。 - ✅ 验证 SDK ——
ValidateStructured字段详解。 - 🖥️ 平台 SDK —— 平台要求检查结构化结果。
- 🌐 Packagist 安全公告 —— 远程公告的结构化类型。