# php-httpclient **Repository Path**: liuxiaojinla/php-httpclient ## Basic Information - **Project Name**: php-httpclient - **Description**: 基于 guzzlehttp/guzzle 实现的一个轻量级的Http客户端。 - **Primary Language**: PHP - **License**: Apache-2.0 - **Default Branch**: 2.x - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-05-07 - **Last Updated**: 2026-04-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: Http, client, guzzlehttp ## README # Xin HTTP Client 一个基于适配器模式的轻量级 PHP HTTP 客户端库,支持多种底层实现(Guzzle、AmpPHP、Workerman),提供简洁的 API 和强大的拦截器机制。 ## ✨ 特性 - 🚀 **多适配器支持**:自动识别或手动指定底层 HTTP 客户端(Guzzle、AmpPHP、Workerman) - 🎯 **简洁 API**:链式调用,支持同步/异步请求 - 🔧 **拦截器机制**:灵活请求/响应拦截器,轻松处理认证、日志等横切关注点 - 📦 **丰富的响应处理**:JSON/XML 自动解析、流式响应、文件下载等 - ⚡ **异步支持**:原生支持异步请求和 SSE 流式处理 - 🛡️ **完善的错误处理**:友好的异常体系和错误检查方法 ## 📋 环境要求 - PHP >= 8.1 - Composer ## 🚀 安装 ```bash composer require xin/http ``` ### 可选依赖 根据需要使用不同的适配器: ```bash # Guzzle 适配器(默认) composer require guzzlehttp/guzzle # AmpPHP 适配器(协程) composer require amphp/http-client # Workerman 适配器 composer require workerman/http-client workerman/workerman ``` ## 💡 快速开始 ### 基础用法 ```php ['name' => 'John', 'email' => 'john@example.com'] ]); // 获取响应数据 echo $response->body(); // 原始响应体 $data = $response->json(); // JSON 解析为数组 $status = $response->status(); // 状态码 $success = $response->successful(); // 是否成功 (2xx) ``` ### 常用 HTTP 方法 ```php // 同步请求 HttpClient::get($uri, $options = []); HttpClient::post($uri, $options = []); HttpClient::put($uri, $options = []); HttpClient::patch($uri, $options = []); HttpClient::delete($uri, $options = []); HttpClient::head($uri, $options = []); // 异步请求 HttpClient::getAsync($uri, $options = []); HttpClient::postAsync($uri, $options = []); HttpClient::putAsync($uri, $options = []); ``` ## 📖 详细用法 ### 创建客户端实例 ```php // 使用默认单例 $client = HttpClient::default(); // 创建新实例 $client = HttpClient::create([ 'timeout' => 30, 'connect_timeout' => 5, ]); // 自定义配置 $client = new HttpClient([ 'base_uri' => 'https://api.example.com', 'headers' => [ 'Accept' => 'application/json', ], ]); ``` ### 配置选项 ```php // 设置全局默认配置 HttpClient::setDefaultOptions([ 'timeout' => 30, 'verify' => false, 'headers' => [ 'X-API-Key' => 'your-api-key', ], ]); // 合并默认配置 HttpClient::mergeDefaultOptions([ 'timeout' => 60, ]); // 实例级别配置 $client = HttpClient::create(); $client->mergeOptions([ 'timeout' => 10, 'headers' => ['Custom-Header' => 'value'], ]); // 链式配置 $client->withTimeout(30) ->withConnectTimeout(5) ->withHeaders(['Authorization' => 'Bearer token']) ->withAllowRedirects(false) ->withVerify(true); ``` ### 请求选项 ```php $response = HttpClient::post('https://api.example.com/users', [ // Query 参数 'query' => [ 'page' => 1, 'limit' => 20, ], // JSON 数据 'json' => [ 'name' => 'John', 'email' => 'john@example.com', ], // 表单数据 'form_params' => [ 'field' => 'value', ], // 请求头 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer token', ], // 超时设置 'timeout' => 30, 'connect_timeout' => 5, // 其他 Guzzle 兼容选项 'allow_redirects' => true, 'verify' => false, ]); ``` ### 响应处理 ```php $response = HttpClient::get('https://api.example.com/users/1'); // 状态检查 $response->successful(); // 2xx 成功 $response->ok(); // 200 OK $response->redirect(); // 3xx 重定向 $response->failed(); // 4xx/5xx 失败 $response->isClientError(); // 4xx 客户端错误 $response->isServerError(); // 5xx 服务器错误 // 获取数据 $response->body(); // 原始响应体字符串 $response->json(); // JSON 解析为数组 $response->json(false); // JSON 解析为对象 $response->xml(); // XML 解析为数组 $response->object(); // 自动解析 JSON/XML 为对象 $response->data('user.name'); // 点符号访问嵌套数据 $response->toArray(); // 转换为数组 // 响应信息 $response->status(); // HTTP 状态码 $response->headers(); // 所有响应头 $response->header('Content-Type'); // 指定响应头 $response->contentType(); // Content-Type $response->cookies(); // Cookies $response->url(); // 请求 URL // 内容类型判断 $response->isJson(); // 是否为 JSON $response->isXml(); // 是否为 XML $response->isSSE(); // 是否为 SSE 流 $response->isStream(); // 是否为流式响应 $response->isAudio(); // 是否为音频 $response->isVideo(); // 是否为视频 // 数组式访问(针对 JSON 响应) $name = $response['user']['name']; $email = $response['user.email']; ``` ### 错误处理 ```php use Xin\HttpClient\Exceptions\ResponseException; use Xin\HttpClient\Exceptions\RequestException; // 方式 1: 自动抛出异常 try { $response = HttpClient::get('https://api.example.com/not-found'); $response->throw(); // 失败时抛出异常 } catch (ResponseException $e) { echo "HTTP Error: " . $e->getResponse()->status(); } catch (RequestException $e) { echo "Request Error: " . $e->getMessage(); } // 方式 2: 条件抛出 $response->throwIf($response->status() === 404); // 方式 3: 除非 HTTP 错误才抛出 $response->throwUnlessHttpError(); // 方式 4: 错误回调 $response->onError(function ($response) { Log::error("Request failed: " . $response->status()); }); // 方式 5: 带回调的 throw $response->throw(function ($response, $exception) { Log::error("Error: " . $exception->getMessage()); }); ``` ### 文件上传 ```php // 同步上传 $response = HttpClient::upload('https://api.example.com/upload', [ 'file' => fopen('/path/to/file.jpg', 'r'), ], [ 'description' => 'File description', ]); // 异步上传 $promise = HttpClient::uploadAsync('https://api.example.com/upload', [ 'file' => fopen('/path/to/file.jpg', 'r'), ]); $promise->then(function ($response) { echo "Upload successful: " . $response->body(); }); ``` ### 文件下载 ```php $response = HttpClient::get('https://example.com/large-file.zip'); // 保存到文件 $response->store('/path/to/save/file.zip'); // 保存解码后的数据 $response->storeAsData('/path/to/data.json'); ``` ### 流式响应 ```php // SSE (Server-Sent Events) 流式处理 $response = HttpClient::post('https://api.example.com/chat', [ 'json' => [ 'message' => 'Hello', 'stream' => true, ], ]); if ($response->isSSE()) { foreach ($response->streamIterator() as $line) { $data = json_decode($line, true); echo $data['choices'][0]['delta']['content'] ?? ''; } } // 自定义流处理回调 $response->setOnStreamIteratorChunk(function ($chunk) { echo $chunk; flush(); }); ``` ### 异步请求 ```php use Xin\HttpClient\Promise; // 单个异步请求 $promise = HttpClient::getAsync('https://api.example.com/users'); $promise->then( function ($response) { echo "Success: " . $response->body(); }, function ($exception) { echo "Error: " . $exception->getMessage(); } ); // 并发请求 $promises = [ 'users' => HttpClient::getAsync('https://api.example.com/users'), 'posts' => HttpClient::getAsync('https://api.example.com/posts'), ]; Promise::all($promises)->then(function ($results) { $users = $results['users']->json(); $posts = $results['posts']->json(); }); ``` ## 🔌 拦截器 拦截器允许你在请求发送前和响应接收后执行自定义逻辑。 ### 请求拦截器 ```php use Xin\HttpClient\HttpClient; // 添加请求拦截器 HttpClient::default()->addRequestInterceptor(function (array $options) { // 添加认证头 $options['headers']['Authorization'] = 'Bearer ' . getToken(); // 添加请求 ID $options['headers']['X-Request-ID'] = uniqid(); // 记录日志 Log::info("Sending request to: " . $options['uri']); return $options; // 必须返回修改后的 options }); // 移除拦截器 $client->removeRequestInterceptor($callback); ``` ### 响应拦截器 ```php // 添加响应拦截器 HttpClient::default()->addResponseInterceptor(function ($response) { // 自动解析 JSON if ($response->isJson()) { $response->decoded(); } // 记录响应日志 Log::info("Response status: " . $response->status()); return $response; // 必须返回 response }); // 移除拦截器 $client->removeResponseInterceptor($callback); ``` ### 内置拦截器 ```php use Xin\HttpClient\Interceptors\Requests\BodyJsonParametersInterceptor; use Xin\HttpClient\Interceptors\Responses\DecodedInterceptor; // 自动将数组 body 转为 JSON HttpClient::default()->addRequestInterceptor(new BodyJsonParametersInterceptor()); // 自动解码 JSON/XML 响应 HttpClient::default()->addResponseInterceptor(new DecodedInterceptor()); ``` ## 🔄 适配器 ### 自动选择适配器 库会自动检测环境并选择合适的适配器: 1. **Workerman** - 如果在 Workerman 环境中 2. **AmpPHP** - 如果安装了 AmpPHP 3. **Guzzle** - 默认回退方案 ### 手动指定适配器 ```php // 通过配置指定 $client = HttpClient::create([ 'adapter' => 'guzzle', // 或 'workerman', 'ampphp' ]); // 使用类名 $client = HttpClient::create([ 'adapter' => \Xin\HttpClient\Adapters\GuzzleHttpClientAdapter::class, ]); // 动态切换适配器 $client->useAdapter('workerman'); $client->useAdapter(new AmpHttpClientAdapter($options)); ``` ### 创建特定适配器 ```php // Guzzle 适配器 $client = HttpClient::createGuzzleHttpClient([ 'timeout' => 30, ]); // Workerman 适配器 $client = HttpClient::createWorkermanHttpClient([ 'timeout' => 30, ]); // AmpPHP 适配器 $client = HttpClient::createAmpHttpClient([ 'timeout' => 30, ]); ``` ## 🎨 高级用法 ### 在类中使用 ```php use Xin\HttpClient\HasHttpClient; class ApiService { use HasHttpClient; protected $baseUri = 'https://api.example.com'; public function getUsers() { return $this->client()->get('/users')->json(); } public function createUser(array $data) { return $this->client()->post('/users', [ 'json' => $data, ])->throw()->json(); } // 自定义 HTTP 客户端配置 protected function httpClientOptions(): array { return [ 'timeout' => 30, 'base_uri' => $this->baseUri, 'headers' => [ 'Accept' => 'application/json', ], ]; } // 请求拦截器 protected function onHttpClientRequest(array $options): array { $options['headers']['Authorization'] = 'Bearer ' . $this->getToken(); return $options; } } ``` ### Cookie 管理 ```php $client = HttpClient::create([ 'cookies' => true, // 启用 Cookie ]); $response = $client->get('https://example.com/login', [ 'form_params' => [ 'username' => 'user', 'password' => 'pass', ], ]); // 获取 Cookies $cookies = $client->cookies(); // 携带 Cookie 访问 $response = $client->get('https://example.com/dashboard'); ``` ### 获取底层客户端 ```php // 获取原生 HTTP 客户端实例 $guzzleClient = HttpClient::default()->client(); // 可以直接使用原生客户端 $psr7Response = $guzzleClient->send($psr7Request); ``` ## 📝 完整示例 ### RESTful API 调用 ```php 'https://api.example.com/v1', 'timeout' => 30, 'headers' => [ 'Accept' => 'application/json', ], ]); // 添加拦截器自动处理 JSON $api->addRequestInterceptor(new BodyJsonParametersInterceptor()); $api->addRequestInterceptor(function (array $options) { $options['headers']['Authorization'] = 'Bearer YOUR_API_TOKEN'; return $options; }); try { // 获取资源列表 $users = $api->get('/users', [ 'query' => ['page' => 1, 'limit' => 20], ])->throw()->json(); // 创建资源 $newUser = $api->post('/users', [ 'json' => [ 'name' => 'John Doe', 'email' => 'john@example.com', ], ])->throw()->json(); // 更新资源 $api->put("/users/{$newUser['id']}", [ 'json' => ['name' => 'Jane Doe'], ])->throw(); // 删除资源 $api->delete("/users/{$newUser['id']}")->throw(); echo "All operations completed successfully!"; } catch (\Exception $e) { echo "Error: " . $e->getMessage(); } ``` ### AI 流式对话(SSE) ```php 'https://api.openai.com/v1', 'headers' => [ 'Authorization' => 'Bearer YOUR_API_KEY', 'Content-Type' => 'application/json', ], ]); $response = $client->post('/chat/completions', [ 'json' => [ 'model' => 'gpt-4', 'messages' => [ ['role' => 'user', 'content' => 'Hello!'], ], 'stream' => true, ], ]); if ($response->isSSE()) { foreach ($response->streamIterator() as $line) { if (str_starts_with($line, 'data: ')) { $data = json_decode(substr($line, 6), true); $content = $data['choices'][0]['delta']['content'] ?? ''; echo $content; flush(); } } } ``` ## 📚 API 参考 ### HttpClient 主要方法 | 方法 | 说明 | |------|------| | `HttpClient::default()` | 获取默认单例 | | `HttpClient::create($options)` | 创建新实例 | | `HttpClient::get/post/put/delete/patch/head()` | 同步请求 | | `HttpClient::getAsync/postAsync/...()` | 异步请求 | | `HttpClient::upload()` | 文件上传 | | `HttpClient::request($method, $uri, $options)` | 通用请求方法 | | `$client->mergeOptions($options)` | 合并配置 | | `$client->with*()` | 链式配置方法 | | `$client->addRequestInterceptor()` | 添加请求拦截器 | | `$client->addResponseInterceptor()` | 添加响应拦截器 | | `$client->getOptions()` | 获取配置 | | `$client->client()` | 获取底层客户端 | ### Response 主要方法 | 方法 | 说明 | |------|------| | `$response->body()` | 获取响应体 | | `$response->json($asArray)` | JSON 解析 | | `$response->xml($asArray)` | XML 解析 | | `$response->data($key)` | 获取解码数据 | | `$response->status()` | 状态码 | | `$response->successful()` | 是否成功 | | `$response->failed()` | 是否失败 | | `$response->throw()` | 失败时抛出异常 | | `$response->headers()` | 获取响应头 | | `$response->cookies()` | 获取 Cookies | | `$response->store($path)` | 保存到文件 | | `$response->streamIterator()` | 获取流迭代器 | | `$response->isJson/isXml/isSSE()` | 类型判断 | ## 🤝 贡献 欢迎提交 Issue 和 Pull Request! ## 📄 许可证 Apache-2.0 License ## 👤 作者 晋 <657306123@qq.com>