📦 popular_packages — 热门 Composer 包批量查询
本示例演示如何用 Composer Skills 客户端一次性抓取多个流行 PHP 包的元数据,并附带仓库统计与安全公告,最终落地为 JSON 文件。
🎯 示例定位
这个示例在学什么:
- 🏗️ 如何创建一个带超时配置的
ComposerClient客户端实例 - 📊 如何调用
GetStatistics()拿到 Packagist 仓库的总量级指标(下载量、包数、版本数) - 🛡️ 如何调用
GetSecurityAdvisories()获取全量安全公告,作为包选型的安全前置参考 - 🔁 如何在
for循环中批量调用GetPackage(),并妥善处理单个包失败而不中断整体流程 - 💾 如何用标准库
encoding/json+os.WriteFile把多源结果归并序列化为本地 JSON
它对应的 SDK 方法都属于 pkg/client 包(ComposerClient,本质是 Packagist API 的远程客户端):
| 用途 | 方法 |
|---|---|
| 仓库统计 | GetStatistics() |
| 安全公告 | GetSecurityAdvisories() |
| 单包详情 | GetPackage(packageName string) |
💻 完整代码
go
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"time"
"github.com/scagogogo/composer-skills/pkg/client"
)
// 一些流行的 Composer 包
var popularPackages = []string{
"symfony/symfony",
"laravel/framework",
"guzzlehttp/guzzle",
"monolog/monolog",
"phpunit/phpunit",
}
func main() {
// 创建一个 Composer 客户端
composerClient := client.NewComposerClient(30 * time.Second)
// 创建一个保存结果的映射
results := make(map[string]interface{})
// 获取统计数据
fmt.Println("获取 Composer 仓库统计数据...")
stats, err := composerClient.GetStatistics()
if err != nil {
log.Fatalf("获取统计数据失败: %v", err)
}
results["statistics"] = stats
fmt.Printf("总下载量: %d, 包数量: %d, 版本数量: %d\n",
stats.Totals.Downloads, stats.Totals.Packages, stats.Totals.Versions)
// 获取安全公告
fmt.Println("\n获取安全公告...")
advisories, err := composerClient.GetSecurityAdvisories()
if err != nil {
log.Fatalf("获取安全公告失败: %v", err)
}
results["advisories"] = advisories
fmt.Printf("获取到 %d 个包的安全公告\n", len(advisories.Advisories))
// 获取流行包的信息
fmt.Println("\n获取流行包的信息...")
packageInfos := make(map[string]interface{})
for _, pkgName := range popularPackages {
fmt.Printf(" 获取 %s 的信息...\n", pkgName)
pkgInfo, err := composerClient.GetPackage(pkgName)
if err != nil {
// 单包失败只跳过,不中断整个批处理
fmt.Printf(" 获取 %s 信息失败: %v\n", pkgName, err)
continue
}
packageInfos[pkgName] = pkgInfo
// 显示一些基本信息
p := pkgInfo.Package
fmt.Printf(" 名称: %s\n", p.Name)
fmt.Printf(" 描述: %s\n", p.Description)
fmt.Printf(" 类型: %s\n", p.Type)
fmt.Printf(" 下载量: %d\n", p.Downloads.Total)
fmt.Printf(" 版本数: %d\n", len(p.Versions))
fmt.Printf(" GitHub Stars: %d\n", p.GithubStars)
fmt.Println()
}
results["packages"] = packageInfos
// 将所有结果保存到文件
outputFile := "popular_packages_results.json"
jsonData, err := json.MarshalIndent(results, "", " ")
if err != nil {
log.Fatalf("序列化结果失败: %v", err)
}
err = os.WriteFile(outputFile, jsonData, 0644)
if err != nil {
log.Fatalf("保存结果到文件失败: %v", err)
}
fmt.Printf("\n结果已保存到 %s\n", outputFile)
}🧩 代码讲解
- 🏗️ 创建客户端:
client.NewComposerClient(30 * time.Second)传入 30 秒 HTTP 超时,足够覆盖 Packagist 某些大包(如symfony/symfony几百个版本)的响应耗时。客户端可复用,无需每次请求新建。 - 🗂️ 结果容器:
results := make(map[string]interface{})用一个聚合 map 把统计、公告、包详情三类异构数据装到一起,便于最后一次性序列化。 - 📊 拿仓库总览:
GetStatistics()返回*domain.StatisticsResponse,从stats.Totals.Downloads / Packages / Versions直接读总量,用于在抓单包前对仓库规模有个宏观认知。 - 🛡️ 拿安全公告:
GetSecurityAdvisories()返回全量公告,注意它的体积可能很大——示例只取len(advisories.Advisories)做计数展示,实际项目里建议按包名过滤后再用。 - 🔁 批量抓包:遍历
popularPackages切片逐个调用GetPackage(pkgName)。关键在于错误处理用的是continue而非log.Fatalf——单个包请求失败(网络抖动、包名下架)不应让整个批处理前功尽弃。 - 🏷️ 读取字段:
pkgInfo.Package是详情主体,p.Downloads.Total、p.GithubStars、len(p.Versions)都是选型时最关心的指标,可以直接拿去做对比报表。 - 💾 落地 JSON:
json.MarshalIndent带缩进序列化,os.WriteFile写入popular_packages_results.json,方便后续用jq或脚本二次分析。
▶️ 运行方式
在示例目录下执行(需联网访问 Packagist API):
bash
cd examples/popular_packages
go run main.go运行结束后,当前目录会生成 popular_packages_results.json,内含统计、公告、各包详情三段数据。由于示例会发起真实 API 请求,请避免高频运行以免给 Packagist 带来负担。
🔗 涉及的 SDK 方法
| 方法 | 所属包 | 文档 |
|---|---|---|
GetStatistics() | pkg/client (ComposerClient) | /sdk/packagist/methods/get-statistics |
GetSecurityAdvisories() | pkg/client (ComposerClient) | /sdk/packagist/methods/get-security-advisories |
GetPackage(packageName string) | pkg/client (ComposerClient) | /sdk/packagist/methods/get-package |
NewComposerClient(timeout time.Duration) | pkg/client | 客户端构造函数,见 client 包文档 |
🚀 进阶
- ⚡ 并发抓取:把
for循环改成errgroup+ goroutine,给每个GetPackage配一个信号量限流(如 5 并发),5 个包可从串行 ~5 倍延迟降到 1 倍。 - 🔍 精准公告:把全量的
GetSecurityAdvisories()换成GetSecurityAdvisoriesForPackages(popularPackages),只取你真正关心的包的公告,响应体大幅缩小。 - 📈 增量更新:用
GetSecurityAdvisoriesSince(updatedSince time.Time)替代全量拉取,配合本地时间戳做增量同步,适合长期运行的安全监控。 - 📊 指标对比:把
p.Downloads.Total与stats.Totals.Downloads做除法得到「该包占全仓库下载比例」,生成一张流行度排行表。 - 💾 结构化存储:把
os.WriteFile换成写 SQLite 或 CSV,并加一个--since参数支持只导出最近 N 天有更新的包,做成一个可复用的 CLI 工具。