直接结论:用 packagist-mock 最省事,它专为 composer 测试设计,支持动态注册包、生成合规 json 响应,并需通过 composer config 强制替换 packagist.org url 来触发完整发现流程。

用 packagist-mock 快速起一个本地 Packagist 响应服务
直接结论:别自己从零写 HTTP mock,用现成的 packagist-mock 最省事。它专为 Composer 安装流程测试设计,能返回真实结构的 JSON 包元数据,且支持动态包注册和版本控制。
常见错误是试图用 json-server 或 mockoon 手动拼 Packagist 的响应格式——结果发现 packages.json 的顶层结构、providers 分片、dist/source 字段嵌套规则全对不上,Composer 直接报 Invalid package information。
- 安装:
npm install -g packagist-mock(需 Node.js 16+) - 启动:
packagist-mock --port 8080,默认监听http://localhost:8080 - 它会自动提供根
/packages.json和模拟的/p/<vendor>/<package>.json</package></vendor>路径 - 首次请求任意包时,它会自动生成带
dev-main和1.0.0的最小有效响应
让 Composer 真实走你的本地 mock 服务(不是改 repositories)
关键点在于绕过 Packagist.org 的默认行为,而不是简单加个 path 或 vcs 类型仓库——那样只会让 Composer 跳过 metadata 请求,不触发你想要测的完整发现流程。
必须用 composer config 强制重写 Packagist 的 URL:
- 执行:
composer config repo.packagist.org.url http://localhost:8080 - 这会修改当前项目的
composer.json中的repositories,把官方源替换成你的地址 - 注意:不要加
--global,全局改会影响其他项目;也别手动编辑repositories数组,容易漏掉packagist.org这个 magic key - 验证是否生效:
composer config repo.packagist.org.url应输出http://localhost:8080
packagist-mock 返回的 JSON 为什么还是被 Composer 拒绝?
最常踩的坑是字段缺失或类型错位,Packagist 对响应体非常严格,哪怕 time 字段少一个 Z(时区标识),也会导致 Could not parse version constraint 或静默跳过该包。
检查三个硬性要求:
-
packages字段下每个包数组必须有name(字符串)、version(语义化字符串,如1.0.0)、dist(含type、url、reference) -
time必须是 ISO 8601 格式,例如"2024-01-01T00:00:00+00:00",不能是"2024-01-01" -
dist.url必须可访问(哪怕只是http://localhost:8080/dummy.zip),否则 Composer 会在 install 阶段报Failed to download
如果想快速验证结构,用 curl http://localhost:8080/p/myvendor/mypackage.json 看输出,再对照 Packagist 真实包的响应比对字段。
测试私有包安装时,如何让 packagist-mock 返回指定内容?
默认行为只生成占位包,但你可以用它的 API 注册真实元数据。比如你要测 acme/utils 的 2.1.0 版本安装:
- 准备一个 JSON 文件
acme-utils-2.1.0.json,包含完整name、version、dist、require等字段 - POST 进去:
curl -X POST http://localhost:8080/register -H "Content-Type: application/json" --data-binary @acme-utils-2.1.0.json - 之后
composer require acme/utils:^2.1就会拉这个版本,且composer show acme/utils能看到你定义的require关系 - 注意:不支持一次注册多个版本;每个版本要单独 POST,路径由
name+version自动推导
mock server 不处理 zip 下载,所以 dist.url 只要能被 Composer 发起 HTTP HEAD 请求成功就行——哪怕返回 404,只要不超时,就不会中断 metadata 流程。










