composer的http-proxy配置仅作用于packagist元数据请求,zip包下载(如github)默认绕过代理,需通过插件劫持filedownloader并注入代理客户端,或显式配置extra.http-proxy供插件读取。

Composer 本身不内置网络代理支持,但可通过插件在下载阶段注入代理逻辑——关键不是改 Composer 源码,而是劫持 FileDownloader 实例并替换其底层 HTTP 客户端。
为什么直接配置 http-proxy 不总是有效
Composer 的 http-proxy 配置(通过 composer config -g http-proxy http://127.0.0.1:8888)只影响部分请求,比如 Packagist 元数据获取;但对 ZIP 包下载(尤其是 GitHub/GitLab 等非 Packagist 源),默认仍走原生 cURL 或 PHP stream,绕过代理设置。常见现象是:composer install 卡在 “Downloading …”、超时或 403 错误,而 curl -x http://127.0.0.1:8888 https://github.com/xxx.zip 却能成功。
- GitHub Releases、GitLab artifacts、私有 Git 仓库的 ZIP 下载不走 Composer 的 proxy 配置
- 某些企业环境禁用 TLS SNI 或强制中间人证书,需额外注入 CA 或跳过验证(需谨慎)
-
composer-plugin-apiv2.x 中,FileDownloader已抽象为可替换服务,但官方未暴露 setter 接口
如何在插件中替换 FileDownloader 的 HTTP 客户端
核心思路:在插件 activate() 中,通过反射或 Composer 内部服务注册机制,将默认的 FileDownloader 替换为带代理的自定义实现。最稳妥方式是重写 getDownloader() 方法并 patch InstallationManager 的 downloader factory。
- 必须依赖
"composer/composer": "^2.5"(v2.5+ 提供更稳定的 downloader 注入点) - 主类中不要直接 new
FileDownloader,而是继承它并覆盖download()方法,内部使用HttpClient并传入['proxy' => 'http://...']选项 - 用
$composer->getDownloadManager()->setDownloader('zip', $myProxyDownloader)注册(注意类型字符串必须匹配,如'zip','tar') - 若目标是 Git 源,还需 patch
GitDownloader的 clone 命令,添加-c http.proxy=...参数
extra.http-proxy 配置如何被插件读取
插件不能直接读 composer config http-proxy,因为该配置在插件激活时尚未完全加载。正确做法是:在插件 composer.json 的 extra 字段声明代理,再由插件解析:
{
"extra": {
"http-proxy": "http://127.0.0.1:8888",
"no-proxy": ["localhost", "127.0.0.1"]
}
}
- 在
activate()中用$composer->getConfig()->get('extra')取出该结构 - 检查环境变量
HTTP_PROXY/NO_PROXY,优先级高于extra(符合 Unix 习惯) - 对
no-proxy列表做域名/子网匹配(如"*.internal"或"10.0.0.0/8"),避免误代理内网地址
调试与兼容性陷阱
插件上线前最容易翻车的地方不是逻辑,而是加载时机和类加载冲突:
- 插件类必须用 PSR-4 autoload,且命名空间与
extra.class完全一致,否则activate()根本不会被调用 - 不要在插件中 require
symfony/http-client—— Composer 自身用的是composer/xdebug-handler+ cURL 封装,引入第三方 HTTP 库易导致版本冲突 - PHP 8.2+ 的
#[\ReturnTypeWillChange]属性可能在旧版插件中引发 warning,需显式声明返回类型(如public function activate(Composer $composer, IOInterface $io): void) - 本地测试务必用
"repositories": [{"type":"path","url":"./my-proxy-plugin"}],避免 Packagist 缓存掩盖 autoload 错误
真正难的不是写代理逻辑,而是让代理在所有 downloader 类型(zip/tar/git/hg)和所有网络路径(HTTPS redirect、302 跳转、chunked transfer)下都稳定生效——这需要大量真实网络环境验证,而非仅单元测试。











