Skip to content

🔧 输出解析

composer 各子命令的原始文本/JSON 输出解析成结构化 Go 数据——包信息、过时包、安全审计、搜索结果、依赖树、安装/更新/移除统计、self-update、平台需求、资金、诊断、配置、版本号、包名列表。

Composer 大量子命令支持 --format=json,但原始 JSON 形态各异(有的是对象、有的是数组、有的把包名当 key)。本模块把这些异构输出统一归一化为强类型结构体,并对若干只有文本输出的命令(如 diagnosefundself-updateabout)做了正则/缩进解析,让你无需手写解析逻辑。

何时使用

  • 📊 仪表盘 / CI 报告:把 install / update / outdated / audit 的输出转成数字统计。
  • 🌳 依赖可视化:ParseDependencyTreeOutputcomposer show --tree 的树形文本转成可递归遍历的 []DependencyNode
  • 🔍 检索:ParseComposerSearchJSON 把搜索结果转成结构化条目列表。
  • 🧪 单测里直接喂入固定字符串断言解析结果,不依赖真实 composer 调用。
  • 🩺 自检:ParseDiagnoseOutputAsChecksdiagnose 的多行 [OK]/[WARNING]/[ERROR] 拆成逐项检查。

与结构化方法的关系

解析函数 vs 结构化方法

本页函数都是包级函数Parse*Extract*),输入是 string、输出是结构体。它们与 auditpackagesversion 等页面里的 *Composer 方法(如 GetAuditInfoGetOutdatedInfoGetVersionInfo)一一对应——后者只是「先 c.Run(...) 拿到输出,再调本页的 Parse*」的语法糖。因此你既可以走高级方法,也可以自己拿输出后手动解析。

方法签名

函数签名说明
📦 ParseComposerShowJSONfunc ParseComposerShowJSON(output string) (*PackageInfo, error)解析 show --format=json,等价 ParsePackageInfo
🕰️ ParseComposerOutdatedJSONfunc ParseComposerOutdatedJSON(output string) (*OutdatedResult, error)解析 outdated --format=json,等价 ParseOutdatedResult
🛡️ ParseComposerAuditJSONfunc ParseComposerAuditJSON(output string) (*AuditInfoResult, error)解析 audit --format=json,等价 ParseAuditInfoResult
🔎 ParseComposerSearchJSONfunc ParseComposerSearchJSON(output string) (*SearchResult, error)解析 search --format=json,等价 ParseSearchResult
🌳 ParseDependencyTreeOutputfunc ParseDependencyTreeOutput(output string) ([]DependencyNode, error)解析 show --tree文本输出为依赖树
🌳 ParseDependencyTreeJSONfunc ParseDependencyTreeJSON(output string) ([]DependencyNode, error)解析 show --tree --format=json 的依赖树
⬇️ ParseInstallOutputfunc ParseInstallOutput(output string) *InstallResult解析 install 输出的安装/更新/移除计数与警告
🔄 ParseUpdateOutputfunc ParseUpdateOutput(output string) *UpdateResult解析 update 输出的更新计数与警告
➕ ParseRequireOutputfunc ParseRequireOutput(output string, packageName string) *RequireResult解析 require 输出,提取安装版本
➖ ParseRemoveOutputfunc ParseRemoveOutput(output string, packageName string) *RemoveResult解析 remove 输出与警告
⬆️ ParseSelfUpdateOutputfunc ParseSelfUpdateOutput(output string) (oldVersion, newVersion string, err error)解析 self-update 输出的旧/新版本
✅ ParseCheckPlatformReqsOutputfunc ParseCheckPlatformReqsOutput(output string) ([]PlatformRequirement, error)解析 check-platform-reqs 文本输出
💰 ParseFundOutputfunc ParseFundOutput(output string) []FundInfo解析 fund 文本输出为资金条目列表
🩺 ParseDiagnoseOutputAsChecksfunc ParseDiagnoseOutputAsChecks(output string) []DiagnoseCheck解析 diagnose 输出为检查项列表
⚙️ ParseConfigOutputfunc ParseConfigOutput(output string) (string, error)解析 config 单值输出(去空白)
🔢 ExtractVersionFromOutputfunc ExtractVersionFromOutput(output string) (string, bool)从任意文本中正则提取 X.Y.Z 版本号
📛 ExtractPackageNamesFromOutputfunc ExtractPackageNamesFromOutput(output string) []string从任意文本中正则提取 vendor/package 包名(去重)

结构化返回类型

PackageInfo / DependencyNode

ParseComposerShowJSON 返回 *PackageInfo(字段见 packages);ParseDependencyTreeOutput / ParseDependencyTreeJSON 返回 []DependencyNode

go
type DependencyNode struct {
    Name     string           `json:"name"`
    Version  string           `json:"version,omitempty"`
    Children []DependencyNode `json:"children,omitempty"`
}
字段类型说明
Namestring包名,如 symfony/console
Versionstring该节点版本(可空)
Children[]DependencyNode递归子依赖

InstallResult / UpdateResult / RequireResult / RemoveResult

go
type InstallResult struct {
    PackagesInstalled int      `json:"packages_installed"`
    PackagesUpdated   int      `json:"packages_updated"`
    PackagesRemoved   int      `json:"packages_removed"`
    Output            string   `json:"output"`
    Warnings          []string `json:"warnings,omitempty"`
}

type UpdateResult struct {
    PackagesUpdated int      `json:"packages_updated"`
    Output          string   `json:"output"`
    Warnings        []string `json:"warnings,omitempty"`
}

type RequireResult struct {
    PackageName string   `json:"package_name"`
    Version     string   `json:"version,omitempty"`
    Output      string   `json:"output"`
    Warnings    []string `json:"warnings,omitempty"`
}

type RemoveResult struct {
    PackageName string   `json:"package_name"`
    Output      string   `json:"output"`
    Warnings    []string `json:"warnings,omitempty"`
}

Output 字段保留原始命令输出,便于调试;Warnings 收集所有含 Warning 的行。

PlatformRequirement / FundInfo / DiagnoseCheck

go
type PlatformRequirement struct {
    Package  string `json:"package"`
    Version  string `json:"version,omitempty"`
    Status   string `json:"status"`              // "ok" / "missing" / "mismatch"
    Required string `json:"required,omitempty"`
}

type FundInfo struct {
    Package string `json:"package"`
    Type    string `json:"type,omitempty"`       // "github" / "patreon" / "tidelift" 等
    URL     string `json:"url,omitempty"`
}

type DiagnoseCheck struct {
    Name   string `json:"name"`
    Status string `json:"status"`                // "ok" / "warning" / "error" / "info"
    Detail string `json:"detail,omitempty"`
}

示例

解析 outdated 的 JSON 输出

go
package main

import (
	"fmt"
	"log"

	"github.com/scagogogo/composer-skills/pkg/composer"
)

func main() {
	comp, err := composer.New(composer.DefaultOptions())
	if err != nil {
		log.Fatal(err)
	}

	// 拿到 outdated 的 JSON 原始输出
	output, err := comp.Run("outdated", "--format", "json")
	if err != nil && output == "" {
		// 没有过时包时 composer 可能返回非零退出码,但输出仍含有效 JSON
		log.Fatalf("运行 outdated 失败: %v", err)
	}

	// 用解析函数把 JSON 转成结构体
	result, err := composer.ParseComposerOutdatedJSON(output)
	if err != nil {
		log.Fatalf("解析过时包失败: %v", err)
	}
	for _, pkg := range result.Installed {
		fmt.Printf("%s: 已装 %s -> 可升级 %s (%s)\n",
			pkg.Name, pkg.Installed, pkg.Latest, pkg.LatestStatus)
	}
	fmt.Printf("过时包总数: %d\n", result.Count)
}

也可以用高级方法

上面用解析函数直接解析字符串。如果你只想「跑命令拿结果」,用 comp.GetOutdatedInfo() 即可,它内部就是 c.Run("outdated", "--format", "json") + ParseOutdatedResult

把 install 输出转成统计

go
output := `Loading composer repositories with package information
Updating dependencies
Package operations: 3 installs, 0 updates, 0 removals
  - Installing symfony/console (v5.4.0)
Warning: ...`

result := composer.ParseInstallOutput(output)
fmt.Printf("安装 %d 个,更新 %d 个,移除 %d\n",
	result.PackagesInstalled, result.PackagesUpdated, result.PackagesRemoved)
// 安装 3 个,更新 0 个,移除 0 个
fmt.Printf("警告数: %d\n", len(result.Warnings))

解析依赖树文本输出

go
tree := `symfony/console v5.4.0
├── psr/log ~1.0
│   └── ...
└── symfony/polyfill-ctype v1.23.0`

nodes, err := composer.ParseDependencyTreeOutput(tree)
if err != nil {
	log.Fatal(err)
}
for _, root := range nodes {
	fmt.Printf("根: %s %s,子依赖 %d\n", root.Name, root.Version, len(root.Children))
}

文本 vs JSON

ParseDependencyTreeOutput 解析 composer show --tree 的缩进文本(按 4 空格缩进确定层级);ParseDependencyTreeJSON 解析 --tree --format=json。两者返回相同的 []DependencyNode,按你的命令调用方式选用。

解析 self-update 输出的新旧版本

go
output := "Upgrading to 2.6.6 (from 2.6.5)..."
oldVer, newVer, err := composer.ParseSelfUpdateOutput(output)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("从 %s 升级到 %s\n", oldVer, newVer)
// 从 2.6.5 升级到 2.6.6

ParseSelfUpdateOutput 支持三种格式:Upgrading to X (from Y)You are already using composer version XSuccessfully updated to X

从任意输出提取版本号与包名

go
text := "Detected symfony/console at version 5.4.0 in vendor/bin"

if ver, ok := composer.ExtractVersionFromOutput(text); ok {
	fmt.Println("版本:", ver) // 版本: 5.4.0
}

for _, name := range composer.ExtractPackageNamesFromOutput(text) {
	fmt.Println("包名:", name) // 包名: symfony/console
}

ExtractVersionFromOutput 用正则 (\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?) 匹配语义化版本号(含预发布后缀)。ExtractPackageNamesFromOutput 匹配 vendor/package 形态并去重。

解析 diagnose 为逐项检查

go
output := `[OK] Checking composer version
[OK] Checking platform settings
[WARNING] Checking git settings
[ERROR] Checking http connectivity`

checks := composer.ParseDiagnoseOutputAsChecks(output)
for _, c := range checks {
	fmt.Printf("[%s] %s\n", c.Status, c.Name)
}
// [ok] Checking composer version
// [ok] Checking platform settings
// [warning] Checking git settings
// [error] Checking http connectivity

进阶

两个 FundInfo 不要混淆

ParseFundOutput 返回的 FundInforesult_types.go,含 Package / Type / URL)来自 fund 文本输出的逐行解析;而 fund 页里的 FundingInfofund.go,含 URLs 切片)来自 --format=json。两者字段不同,按输出形态选用。

错误退出码也能解析

composer outdated / audit / validate 在发现问题时常返回非零退出码,但 stdout 仍含有效 JSON。对应的高级方法(GetOutdatedInfo 等)已处理这种情况并继续解析;如果你直接用本页 Parse* 函数,请确保传入的 output 是实际 stdout 内容,而非 err.Error()

DependencyNode 文本解析的缩进规则

ParseDependencyTreeOutput 把行首的 |`- 都视为缩进字符,每 4 个缩进字符算一层。因此它对 Composer 默认的树形渲染(├── / └── 前缀)兼容良好,但若你自定义了输出格式可能需要改用 JSON 解析。

基于 MIT 许可证发布