PHPackages                             yunadmin/http-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. yunadmin/http-client

ActiveLibrary[HTTP &amp; Networking](/categories/http)

yunadmin/http-client
====================

零依赖轻量级 HTTP 客户端类库，支持 cURL/Stream 双引擎、中间件、重定向、重试、JSON/FORM 表单、文件上传。

v1.0.0(today)01↑2900%MITPHPPHP ^8.0

Since Aug 25Pushed todayCompare

[ Source](https://github.com/yunadmin-cn/http-client)[ Packagist](https://packagist.org/packages/yunadmin/http-client)[ Docs](https://github.com/yunadmin-cn/http-client)[ RSS](/packages/yunadmin-http-client/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (2)Used By (0)

yunadmin/http-client
====================

[](#yunadminhttp-client)

[![Packagist Version](https://camo.githubusercontent.com/9d2328a363d570ab13052ace19c2e0b3c6b340342683d0dfd2e2a8e240ec8d65/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f79756e61646d696e2f687474702d636c69656e743f6c6162656c3d737461626c65266c6f676f3d7061636b6167697374266c6f676f436f6c6f723d7768697465)](https://packagist.org/packages/yunadmin/http-client)[![PHP Version](https://camo.githubusercontent.com/2e853f38b6e6df08a260e71bb7f098d7aaf74844f5546e72cd8c8171f9cd55db/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e302d3838393262663f6c6f676f3d706870)](https://packagist.org/packages/yunadmin/http-client)[![License: MIT](https://camo.githubusercontent.com/d6b3de96741102bb7a333d2daab8917055d960f75ba38506f638e45bd6cfd990/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f79756e61646d696e2d636e2f687474702d636c69656e743f636f6c6f723d336461363339266c6162656c3d6c6963656e7365)](LICENSE)[![Stars](https://camo.githubusercontent.com/fd6eeb07e42e98660b8e0d14418abd183d0519c034f6e226d7a5934ac69b79d7/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f79756e61646d696e2d636e2f687474702d636c69656e743f7374796c653d666c6174266c6f676f3d67697468756226636f6c6f723d653061383030)](https://github.com/yunadmin-cn/http-client)[![Issues](https://camo.githubusercontent.com/ccb78c6c6e77975e91f4271adbf62e5ea8d12acb3643656b596a2b0c69d8c129/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f79756e61646d696e2d636e2f687474702d636c69656e743f7374796c653d666c6174266c6f676f3d67697468756226636f6c6f723d636233383337)](https://github.com/yunadmin-cn/http-client/issues)

> 仓库：
> 安装：`composer require yunadmin/http-client`

零依赖、轻量级的 PHP HTTP 客户端。仅依赖 **PHP ≥ 8.0** 的原生扩展（curl / json），无任何 Composer 第三方包。

---

特性
--

[](#特性)

能力说明**双引擎**cURL (`CurlHandler`) / Socket Stream (`StreamHandler`) 自动选择**异步并发**`CurlMultiHandler` 基于 cURL Multi；`Future`、`requestAsync()`、`pool()`（并发度控制）**流式上传**按 8KB chunk 读取 body，未知长度自动走 `Transfer-Encoding: chunked`，大文件友好**中间件系统**`HandlerStack` 管道；内置重定向、HTTP 错误抛出、指数退避重试、历史记录**Cookie 会话**`CookieJar` + 标准 `Set-Cookie` 解析（支持引号内嵌分号）**可测试**`MockHandler` 响应队列 + `HistoryMiddleware` 历史记录，不发真实请求即可测业务**常用选项对齐**`decode_content`、`force_ip_resolve`、`timeout`、`connect_timeout`、`verify`、`cert`/`ssl_key`、`proxy`、`auth`、`on_stats`、`sink`、`json`/`form_params`/`multipart`/`body`、`query`、`base_uri`、`curl`**PSR-7 风格对象**`Request` / `Response` / `Uri` / `Stream` / `StreamInterface`要求：`php ^8.0`、`ext-curl`、`ext-json`（已在 `composer.json` 声明）。

---

安装
--

[](#安装)

```
composer require yunadmin/http-client
```

或手动引入：只需注册 PSR-4 前缀 `Yunadmin\HttpClient\` 指向 `src/`，并 `require 'src/functions.php'`。

---

1. 快速开始
-------

[](#1-快速开始)

```
use Yunadmin\HttpClient\Client;

$client = new Client([
    'base_uri' => 'https://api.example.com/v1/',
    'timeout'  => 5.0,            // 总超时（秒，支持小数）
    'headers'  => ['Accept' => 'application/json'],
]);

// GET + query
$resp = $client->get('users/123', ['query' => ['include' => 'roles,perms']]);

echo $resp->getStatusCode();          // 200
echo $resp->getHeaderLine('Content-Type'); // application/json; charset=utf-8

// 两种取 body 方式
$raw = (string)$resp->getBody();      // 原始字符串
$data = $resp->getJsonBody();         // stdClass 或 array（JSON）
```

---

2. HTTP 方法
----------

[](#2-http-方法)

便捷方法：`get / head / post / put / patch / delete / options`，每个都接受 `(uri, options)`。

```
$client = new Client();

$client->head('https://example.com');
$client->post('https://api.example.com/items',   ['json' => ['name'=>'x']]);
$client->put('https://api.example.com/items/1',  ['json' => ['name'=>'y']]);
$client->patch('https://api.example.com/items/1',['json' => ['name'=>'z']]);
$client->delete('https://api.example.com/items/1');
$client->options('https://api.example.com/');
```

对应异步版：`getAsync / postAsync / putAsync / patchAsync / deleteAsync / requestAsync`，返回 `Future`（见第 6 节）。

---

3. 请求体：JSON / 表单 / 文件上传
-----------------------

[](#3-请求体json--表单--文件上传)

### 3.1 JSON（`json`）

[](#31-jsonjson)

```
$resp = $client->post('https://api.example.com/articles', [
    'json'    => ['title' => '你好', 'tags' => ['php','http']],
    'headers' => ['X-Trace' => 'abc'],
]);
// 自动：Content-Type: application/json + json_encode
```

### 3.2 URL 编码表单（`form_params`）

[](#32-url-编码表单form_params)

```
$client->post('https://sso.example.com/login', [
    'form_params' => [
        'username' => 'admin',
        'password' => '123456',
        'remember' => 1,
    ],
]);
// Content-Type: application/x-www-form-urlencoded
```

### 3.3 multipart 表单 &amp; 文件上传（`multipart`）

[](#33-multipart-表单--文件上传multipart)

```
$client->post('https://cdn.example.com/upload', [
    'multipart' => [
        [
            'name'     => 'avatar',
            'contents' => fopen(__DIR__ . '/face.png', 'r'),
            'filename' => 'face.png',
            'headers'  => ['Content-Type' => 'image/png'],
        ],
        [
            'name'     => 'album',
            'contents' => fopen(__DIR__ . '/album.zip', 'r'),
            'filename' => 'album.zip',
        ],
        [
            'name'     => 'desc',
            'contents' => '我的头像 & 相册',
        ],
    ],
]);
```

说明：

- `contents` 支持 **string / resource / StreamInterface** 三种。
- `filename` 省略时，若 `contents` 是文件资源会自动用 `stream_get_meta_data()['uri']`，否则不生成 `filename=`。
- 会自动生成 boundary，`Content-Type: multipart/form-data; boundary=...`。
- 大文件按 **8KB chunk** 读取，不会一次性进内存。

### 3.4 原始 body（`body`）

[](#34-原始-bodybody)

```
// 字符串
$client->post('https://api.example.com/raw', [
    'body'    => '任何原始字节...',
    'headers' => ['Content-Type' => 'text/plain'],
]);

// 大文件 — 使用 LazyOpenStream，按需打开、流式发送
use function Yunadmin\HttpClient\lazy_open_stream;

$body = lazy_open_stream('/data/huge.zip', 'r');
$client->post('https://cdn.example.com/upload', [
    'body'    => $body,
    'headers' => ['Content-Type' => 'application/zip'],
]);
```

- `getSize() != null` → 自动设置 `Content-Length`
- `getSize() == null` → 自动使用 `Transfer-Encoding: chunked`

---

4. URI 处理
---------

[](#4-uri-处理)

### 4.1 `base_uri` + 相对 URI

[](#41-base_uri--相对-uri)

```
$client = new Client(['base_uri' => 'https://api.example.com/v1/']);
// 注意 base_uri 以 / 结尾，否则把相对 uri 当作 path 拼接

$client->get('users');                // → /v1/users
$client->get('/users');               // → /users   (以 / 开头会覆盖 path)
$client->get('https://other.com/x');  // → 完整 URI，忽略 base_uri
```

### 4.2 `query` 选项

[](#42-query-选项)

```
$client->get('https://api.example.com/search', [
    'query' => ['q' => 'php 8', 'page' => 1, 'tags' => ['a','b']],
]);
// 自动 http_build_query，也支持自己传字符串：'q=php+8&page=1'
```

### 4.3 `Uri` 类（PSR-7 风格）

[](#43-uri-类psr-7-风格)

```
use Yunadmin\HttpClient\Uri;

$u = new Uri('https://user:pass@api.example.com:8443/v1?q=1#frag');
echo $u->getScheme();        // https
echo $u->getHost();          // api.example.com
echo $u->getPort();          // 8443
echo $u->getPath();          // /v1
echo $u->getQuery();         // q=1
echo $u->getUserInfo();      // user:pass
echo (string)$u->withScheme('http')->withPort(null)->withQuery('x=2');
// → http://user:pass@api.example.com/v1?x=2#frag
```

---

5. 认证、代理、超时、TLS
---------------

[](#5-认证代理超时tls)

```
$client = new Client([
    // 1. Basic / Digest / Bearer / 自定义
    'auth' => ['user', 'pass'],                   // Basic
    //'auth' => ['user', 'pass', 'digest'],       // Digest（cURL 支持）
    //'auth' => ['xxxxxxxx-token', '', 'bearer'], // Bearer
    //'auth' => 'Basic dXNlcjpwYXNz',             // 直接写字符串

    // 2. 超时
    'timeout'         => 10.0,    // 总超时秒（支持小数）
    'connect_timeout' => 2.0,     // 连接超时秒

    // 3. 强制 IP 版本
    //'force_ip_resolve' => 'v4', // 'v4' | 'v6' | null

    // 4. TLS
    'verify' => true,                             // 默认 true，校验证书
    //'verify' => '/etc/ssl/certs/ca-bundle.crt', // CA 路径
    //'cert'    => '/path/client.crt',            // 客户端证书
    //'ssl_key' => '/path/client.key',            // 客户端私钥

    // 5. 代理
    //'proxy' => 'http://127.0.0.1:8080',         // 单个
    // 或按 scheme 分别设置
    //'proxy' => [
    //    'http'  => 'http://10.0.0.1:8080',
    //    'https' => 'http://10.0.0.1:8443',
    //    'no'    => ['.intranet.local', 'localhost'],
    //],

    // 6. 解压缩
    'decode_content' => true,    // 默认 true：加 Accept-Encoding，并自动 gzip/deflate 解响应体
]);
```

---

6. 异步 &amp; 并发（cURL Multi）
--------------------------

[](#6-异步--并发curl-multi)

### 6.1 `getAsync / postAsync / requestAsync` → 返回 `Future`

[](#61-getasync--postasync--requestasync--返回-future)

```
$client = new Client();

$f1 = $client->getAsync('https://example.com/a');
$f2 = $client->getAsync('https://example.com/b');
$f3 = $client->postAsync('https://example.com/c', ['json' => ['k' => 'v']]);

// 阻塞等到各自完成（内部 tick Multi，三条是并发的）
$r1 = $f1->wait();
$r2 = $f2->wait();
$r3 = $f3->wait();
```

### 6.2 `Future` 链式回调

[](#62-future-链式回调)

```
$client
    ->getAsync('https://api.example.com/items/1')
    ->then(static fn($resp) => $resp->getJsonBody())   // 成功：转换
    ->then(static fn($data) => process($data))         // 继续转
    ->otherwise(static fn(\Throwable $e) => log($e))   // 失败兜底
    ->wait();
```

> 链式回调会在 `wait()` 时按顺序执行（轻量实现，不依赖 Promise/A+ 库）。

### 6.3 `pool()` — 大列表 + 并发度控制

[](#63-pool--大列表--并发度控制)

```
$urls = [
    'https://example.com/p1',
    'https://example.com/p2',
    'https://example.com/p3',
    // ... 可几百上千条
];

$requests = [];
foreach ($urls as $u) {
    $requests[] = ['GET', $u];
}

$client->pool($requests, [
    'concurrency' => 10,          // 同时最多 10 条
    'fulfilled'   => static function ($resp, $index) use ($urls) {
        echo "[OK]  #{$index} {$urls[$index]} => {$resp->getStatusCode()}\n";
    },
    'rejected'    => static function (\Throwable $e, $index) use ($urls) {
        echo "[ERR] #{$index} {$urls[$index]} => {$e->getMessage()}\n";
    },
]);
```

`$requests` 每一项支持：

- `['GET',  $uri]`
- `['POST', $uri, ['json' => [...]]]`
- 直接传 `RequestInterface` 实例

---

7. 重定向、HTTP 错误、重试
-----------------

[](#7-重定向http-错误重试)

### 7.1 重定向（`allow_redirects`）

[](#71-重定向allow_redirects)

```
$client = new Client([
    'allow_redirects' => [
        'max'       => 5,       // 最大重定向次数
        'strict'    => true,    // 严格模式：302/303 POST → 改 GET；false 保持原方法
        'keep_body' => false,   // 重定向后是否保留 body
    ],
]);
// 或简单关闭：
// $client = new Client(['allow_redirects' => false]);
```

内置 `RedirectMiddleware`；超过 `max` 抛 `TooManyRedirectsException`。

### 7.2 HTTP 错误（`http_errors`）

[](#72-http-错误http_errors)

默认 4xx / 5xx 会抛异常：

```
use Yunadmin\HttpClient\Exception\ClientException;
use Yunadmin\HttpClient\Exception\ServerException;
use Yunadmin\HttpClient\Exception\RequestException;

try {
    $client->get('https://api.example.com/404-thing');
} catch (ClientException $e) {  // 4xx
    echo "4xx: " . $e->getResponse()?->getStatusCode();
} catch (ServerException $e) {  // 5xx
    echo "5xx: " . $e->getResponse()?->getStatusCode();
} catch (RequestException $e) { // 基类（含连接失败等）
    echo "req failed: " . $e->getMessage();
}
```

关闭：`new Client(['http_errors' => false])`，自己判断 `getStatusCode()`。

### 7.3 重试（指数退避，`retry`）

[](#73-重试指数退避retry)

```
$client = new Client([
    'retry' => [
        'retries'    => 3,          // 最多重试 3 次（共 1 + 3 = 4 次请求）
        'delay'      => 300,        // 初始延迟 ms
        'multiplier' => 2.0,        // 2 次幂退避：300 → 600 → 1200 ms
        // 'when' 可选：callable(次数, Request, Response?, e?) : bool
        //   未传时，默认：连接错误重试；5xx 重试；幂等方法的 429/503 重试
    ],
]);
```

关闭：`'retry' => false`。

---

8. Cookie 会话（`CookieJar`）
-------------------------

[](#8-cookie-会话cookiejar)

```
use Yunadmin\HttpClient\Cookie\CookieJar;

$jar = new CookieJar($strictMode = true);
$client = new Client(['cookies' => $jar]);

// 1. 登录，Set-Cookie 自动写入 $jar
$client->post('https://sso.example.com/login', [
    'form_params' => ['u' => 'admin', 'p' => '123456'],
]);

// 2. 后续请求自动带上匹配的 Cookie
$me = $client->get('https://sso.example.com/me')->getJsonBody();
```

`CookieJar` 常用方法：

```
$jar->set($name, $value, ['domain'=>'example.com','path'=>'/','secure'=>true,'expires'=>time()+3600]);
$jar->get('session_id', 'example.com', '/');           // Cookie 对象或 null
$jar->toArray();                                        // 导出数组
$jar->toHeaderString('https://api.example.com/x');      // 给某个 URL 拼 Cookie 请求头
```

> `Set-Cookie` 解析器支持：引号内含分号的 value（例如 `tk="abc;foo=bar"; Path=/api`），不会被错误切断。

单次请求也可以直接传非 Jar 的 cookies：

```
$client->get('https://example.com/x', [
    'cookies' => ['sid' => 'abc'],         // array → 转 k=v
    //'cookies' => 'sid=abc; other=xyz',   // string → 直接 Cookie 头
]);
```

---

9. `sink` 下载、`on_stats` 统计、调试
-----------------------------

[](#9-sink-下载on_stats-统计调试)

### 9.1 文件下载（`sink`）

[](#91-文件下载sink)

```
// 直接写文件路径
$client->get('https://example.com/big.zip', [
    'sink' => __DIR__ . '/tmp/big.zip',
]);

// 或写已打开的资源流
$fp = fopen('php://temp', 'w+b');
$client->get('https://example.com/x', ['sink' => $fp]);
rewind($fp);
echo stream_get_contents($fp);
fclose($fp);
```

使用 `sink` 后，`$resp->getBody()` 会被清空（返回空字符串），避免内存保留一份。

### 9.2 `on_stats` 统计回调

[](#92-on_stats-统计回调)

```
$client->get('https://example.com', [
    'on_stats' => static function (object $s) {
        echo "url: "     . $s->url . "\n";
        echo "status: "  . ($s->response?->getStatusCode() ?? '-') . "\n";
        echo "time(s): " . $s->time . "\n";
        if (isset($s->handler_data)) {
            // cURL: curl_getinfo 结果数组
            // Stream: 包含 dns/connect/starttransfer/ttfb_ms 等分段耗时（毫秒）
        }
    },
]);
```

### 9.3 debug：输出 HTTP 原始报文

[](#93-debug输出-http-原始报文)

```
$client->get('https://example.com', [
    'debug' => true,             // 输出到 stdout
    //'debug' => fopen('trace.log', 'ab'),  // 写文件
]);
```

### 9.4 全局 `http_*` 快捷函数

[](#94-全局-http_-快捷函数)

```
use function Yunadmin\HttpClient\http_get;
use function Yunadmin\HttpClient\http_post;
use function Yunadmin\HttpClient\http_json;

$html  = http_get('https://example.com');                             // 直接 string body
$html2 = http_post('https://example.com/form', ['a' => 1]);           // application/x-www-form-urlencoded
$data  = http_json('https://api.example.com/items');                  // 自动 json_decode → array/stdClass
$resp  = http_json('https://api.example.com/items', true);            // true = 返回 Response 对象
```

这些函数会复用同一个静态 `Client` 实例。

---

10. 可测试：Mock + 历史记录
-------------------

[](#10-可测试mock--历史记录)

```
use Yunadmin\HttpClient\Client;
use Yunadmin\HttpClient\Handler\HandlerStack;
use Yunadmin\HttpClient\Handler\MockHandler;
use Yunadmin\HttpClient\Middleware\HistoryMiddleware;
use Yunadmin\HttpClient\Response;

// 1. 准备响应队列
$mock = new MockHandler();
$mock->append(new Response(200, ['Content-Type' => 'application/json'], '{"id":1,"name":"a"}'));
$mock->append(new Response(201, ['X-Id' => '2'], ''));
$mock->append(new \RuntimeException('Network down'));  // 也可以排队 Exception

// 2. 建 HandlerStack，推送 HistoryMiddleware
$history = [];
$stack = HandlerStack::createWithMock($mock);
$stack->push(new HistoryMiddleware($history), 'history');

// 3. 业务代码里注入 client，不发真实请求
$client = new Client(['handler' => $stack, 'base_uri' => 'https://api.example.com/']);
$a = $client->get('items/1')->getJsonBody();
$b = $client->post('items', ['json' => ['name' => 'x']]);
try {
    $client->get('items/3');
} catch (\RuntimeException $e) {
    // Net down
}

// 4. 断言
echo $history[0]['request']->getMethod();     // GET
echo $history[0]['request']->getUri();        // https://api.example.com/items/1
echo $history[0]['response']->getStatusCode();// 200
echo $history[1]['request']->getMethod();     // POST
echo $history[1]['options']['json']['name'];  // x
echo count($history);                         // 3
```

---

11. 自定义中间件 &amp; HandlerStack
-----------------------------

[](#11-自定义中间件--handlerstack)

每个中间件签名：`callable(RequestInterface, array): (ResponseInterface|FutureInterface)`。

```
use Yunadmin\HttpClient\Handler\HandlerStack;
use Yunadmin\HttpClient\Contract\RequestInterface;
use Yunadmin\HttpClient\Response;

$stack = HandlerStack::create();    // 自动挑 CurlHandler / StreamHandler

$stack->push(static function (callable $next) {
    return static function (RequestInterface $req, array $opts) use ($next) {
        // 进入：加 trace header
        $req = $req->withHeader('X-Trace-Id', bin2hex(random_bytes(8)));
        $t1 = microtime(true);

        // 交给下一层
        $result = $next($req, $opts);

        // 出来：打点（支持同步 Response 或异步 Future）
        $log = static function ($r) use ($t1, $req) {
            $ms = round((microtime(true) - $t1) * 1000, 1);
            $code = $r instanceof Response ? $r->getStatusCode() : 'ERR';
            file_put_contents('request.log', "{$req->getMethod()} {$req->getUri()} => {$code} ({$ms}ms)\n", FILE_APPEND);
            return $r;
        };

        if ($result instanceof \Yunadmin\HttpClient\Future) {
            return $result->then($log, $log);
        }
        return $log($result);
    };
}, 'trace');

$client = new Client(['handler' => $stack]);
$client->get('https://example.com');
```

---

12. 选项速查表
---------

[](#12-选项速查表)

选项类型说明`base_uri`string / UriInterface相对 URI 基准`handler`HandlerStack / HandlerInterface自定义底层或中间件栈`timeout` / `connect_timeout`float总 / 连接超时秒（&lt;1 可用）`verify`bool / string`false` 关校验，或 CA 文件/目录`cert` / `ssl_key`string / `[path, pass]`客户端证书/私钥`proxy`string / `[http,https,no]`代理；`no` 为不走代理的域名数组`force_ip_resolve``'v4'` / `'v6'` / null强制 IPv4/6`decode_content`bool默认 `true`：自动压缩&amp;解压`auth``[u,p]` / `[t,'','bearer']` / stringAuthorization 头`headers`array合并进请求头`query`array / string追加到 URI query`json`mixedJSON 请求体`form_params`arrayURL 编码表单`multipart`array`[{name, contents, filename?, headers?}, ...]``body`string / resource / StreamInterface原始请求体`cookies`CookieJar / array / stringJar=会话保持；其他=单次`allow_redirects`bool / `[max,strict,keep_body]`重定向控制`http_errors`bool默认 `true`，4xx/5xx 抛异常`retry`false / `[retries,delay,multiplier,when?]`指数退避重试`on_stats`callable(object)请求完成统计回调`sink`string / resource响应体直接写文件/流`debug`bool / resource原始 HTTP 报文输出`curl``[CURLOPT_* => v]`CurlHandler 直接追加 CURLOPT---

13. 目录结构
--------

[](#13-目录结构)

```
src/
├── Client.php               主入口：便捷方法、选项归一化、sync/async/pool
├── Future.php               异步结果：pending/fulfilled/rejected + then/otherwise/wait
├── Message.php              Request/Response 公共头操作
├── Request.php              请求对象
├── Response.php             响应对象（带 getJsonBody()）
├── Uri.php                  URI 解析与拼接
├── Stream.php               数据流（资源包装 + 静态工厂 fromString/fopen）
├── Contract/                HandlerInterface、MessageInterface、RequestInterface、ResponseInterface、StreamInterface、UriInterface
├── Exception/               RequestException（基类）、ConnectException、ClientException、ServerException、TooManyRedirectsException
├── Handler/
│   ├── CurlHandler.php          同步 cURL：流式上传、on_stats、debug、curl 扩展
│   ├── CurlMultiHandler.php     并发 cURL Multi：enqueue/tick/execute + selectTimeout
│   ├── StreamHandler.php        Socket Stream：流式上传、chunked、分段耗时、force_ip_resolve
│   ├── MockHandler.php          单元测试：响应/异常队列
│   └── HandlerStack.php         中间件管道：create / createWithMock / push / resolve / __invoke / getMultiHandler / enqueue
├── Middleware/
│   ├── RedirectMiddleware.php   3xx 处理（match 表达式分派方法/body/状态）
│   ├── HttpErrorsMiddleware.php 4xx/5xx 抛 ClientException/ServerException
│   ├── RetryMiddleware.php      指数退避重试（默认策略 + 自定义 when）
│   └── HistoryMiddleware.php    记录 {request,response,options,error} 历史
├── Cookie/
│   ├── CookieJar.php            会话存取 + 匹配 domain/path + 请求前/响应后挂钩
│   └── SetCookie.php            Set-Cookie 解析：引号内嵌分号状态机 splitParts
├── Stream/
│   └── LazyOpenStream.php       懒加载文件流（首次读写才 fopen）
└── functions.php                全局辅助：stream_for() / uri_for() / build_query() / lazy_open_stream()
                                 + 便捷：http_get() / http_post() / http_json()
                                 + 调试：dump_request() / dump_response()

```

版本要求
----

[](#版本要求)

- PHP **^8.0**（用到构造函数属性提升、`match`、`str_contains`/`str_starts_with`/`str_ends_with`、nullsafe `?->`、`??=`、`mixed` 类型等）
- `ext-curl`、`ext-json`（composer 自动校验）

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5f8db138f73aa89ab85daf4751524c3fd4c5d47e090a4a36182194d88d1ef683?d=identicon)[yunadmin-cn](/maintainers/yunadmin-cn)

---

Top Contributors

[![kemicms](https://avatars.githubusercontent.com/u/63289622?v=4)](https://github.com/kemicms "kemicms (1 commits)")

---

Tags

httpphpmiddlewarecurlhttp client

### Embed Badge

![Health badge](/badges/yunadmin-http-client/health.svg)

```
[![Health](https://phpackages.com/badges/yunadmin-http-client/health.svg)](https://phpackages.com/packages/yunadmin-http-client)
```

###  Alternatives

[smi2/phpclickhouse

PHP ClickHouse Client

85112.2M85](/packages/smi2-phpclickhouse)[hannesvdvreken/guzzle-debugbar

A Guzzle middleware that logs requests to debugbar's timeline

76433.4k2](/packages/hannesvdvreken-guzzle-debugbar)[popphp/pop-http

Pop Http Component for Pop PHP Framework

1021.5k15](/packages/popphp-pop-http)[openapi/openapi-sdk

Minimal and agnostic PHP SDK for Openapi® (https://openapi.com)

164.6k1](/packages/openapi-openapi-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
