PHP code example of yunadmin / http-client

1. Go to this page and download the library: Download yunadmin/http-client library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

yunadmin / http-client example snippets


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' => ['

$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/');

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

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

$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' => '我的头像 & 相册',
        ],
    ],
]);

// 字符串
$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'],
]);

$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

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

use Yunadmin\HttpClient\Uri;

$u = new Uri('https://user:[email protected]: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:[email protected]/v1?x=2#frag

$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 解响应体
]);

$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();

$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();

$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";
    },
]);

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

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();
}

$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 重试
    ],
]);

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();

$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 请求头

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

// 直接写文件路径
$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);

$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 等分段耗时(毫秒)
        }
    },
]);

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

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 对象

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

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');