Skip to content

🧪 类型化返回值

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

类型获取方法含义
🔒 AuditResultAuditWithJSON()安全审计结果:漏洞数、公告列表、严重等级
📊 AuditInfoGetAuditInfo()审计的汇总信息(抽象后的高层视图)
📦 OutdatedResult / OutdatedInfoOutdatedPackages() / GetOutdatedInfo()过期包列表:当前版本、最新版本、是否直接依赖
🏷️ VersionInfoGetVersionInfo()Composer 自身版本信息
📋 PackageInfoGetPackageInfo(name)某个已安装包的结构化信息
ValidateResultValidateStructured()composer.json 校验结果:是否合法、错误列表
🖥️ PlatformReqsCheckPlatformReqsStructured()平台要求检查结果(PHP 版本、扩展)
📄 LicensesInfoGetLicensesInfo()项目所有依赖的许可证信息
⚙️ ConfigInfoGetConfigStructured()Composer 配置的结构化视图
🔍 SearchInfoSearchInfo(query)本地搜索结果
🩺 DiagnoseInfoDiagnoseStructured()composer diagnose 的结构化结果

命名规律

后缀为 WithJSON 的方法返回 Composer 原始 JSON 解析后的结构体(字段最全);后缀为 InfoStructured 的方法返回 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 > 0strings.Contains(out, "Vulnerabilities") 清楚一百倍。

注意返回值命名

部分方法名以 WithJSON 结尾,返回 Composer 原始 JSON 的直接映射,字段最全但也最"原始";如果你只想要高层抽象,优先选 Get*Info*Structured 命名的变体。两者都返回类型化结构体。

🧭 下一步

基于 MIT 许可证发布