laybot/request-sdk

LayBot universal PHP HTTP request toolkit (Guzzle + Workerman + SSE)

Maintainers

Package info

github.com/laybot/request-sdk

pkg:composer/laybot/request-sdk

Statistics

Installs: 34

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.5.6 2026-03-28 05:49 UTC

This package is auto-updated.

Last update: 2026-03-28 05:56:26 UTC


README

通用 PHP HTTP 请求与基础流式工具库
稳定 · 易用 · 框架无关 · 适合作为第三方平台 SDK 与企业项目的网络底座

1. 为什么使用 Request-SDK?

在实际工程里,服务端项目很少只面对一种请求场景。

你既需要覆盖绝大多数普通 HTTP 请求,又经常会遇到下面这些现实问题:

  • 接第三方 OpenAPI
  • 调内部微服务
  • 做管理后台与定时同步
  • 上传下载文件
  • 记录请求日志
  • 处理复杂鉴权
  • JSON 编解码
  • 重试机制
  • 统一异常体系
  • 在 Webman / Workerman 环境下接基础流式响应

单独看,每一项都不复杂;但一旦落到真实项目里,问题往往不是“能不能发请求”,而是:

  • 请求方式是否统一
  • 鉴权逻辑是否可复用
  • JSON 编解码是否一致
  • 异常是否可控
  • 日志是否能脱敏
  • 文件下载是否会留下半文件
  • 流式请求在不同运行环境下是否还能保持一致调用方式

常见方案各有侧重:

  • Guzzle:成熟稳定,适合普通同步 HTTP 请求
  • Workerman:适合事件循环和长连接场景
  • 但在项目里,通常还需要把这些能力统一起来:
    • Header 鉴权
    • JSON 编解码
    • Retry
    • Trace 日志
    • 文件上传下载
    • 基础流式处理
    • 统一异常体系

此外,很多服务并不是标准 Bearer 或 Basic 鉴权,而是固定 Header Token,例如:

  • 内部微服务固定 Header Token
  • 导出服务 Token
  • 平台间服务身份标识
  • 任意 X-XXX-* 风格静态 Header 鉴权

laybot/request-sdk 的目的,就是把这些网络层能力收敛成一个可长期复用、可持续演进的基础组件库,适合作为:

  • OpenAI / Gemini / Claude 等管理类 SDK 的底层网络基座
  • Webman / Workerman 后台服务的通用 HTTP 组件
  • 内部 OpenAPI / 微服务调用工具
  • 定时同步、管理后台、Server-to-Server 通信基础库
  • 企业级 PHP 项目的统一网络访问层

它不仅解决“能发请求”的问题,更关注:

  • 统一调用方式
  • 统一鉴权入口
  • 统一异常模型
  • 统一日志与脱敏策略
  • 统一上传下载处理
  • 统一基础流式能力
  • 统一底层可扩展能力

换句话说,这不是一个只为单个项目服务的请求工具,而是一套适合在多个 PHP 项目、多个 SDK、多个服务之间长期复用的网络底座。

laybot/request-sdk 的目标不是替代业务 SDK,也不负责模型语义封装。它只解决一件事:把请求稳定地发出去,把响应按统一方式收回来

适用场景包括:

  • 内部微服务调用
  • 第三方 OpenAPI 接入
  • 管理后台服务端请求
  • 定时任务与数据同步
  • 文件上传下载
  • 基础流式响应处理
  • 作为上层 SDK 的网络底座

如果你的目标是做大模型对话、流式增量聚合、厂商协议适配,请使用上层 laybot/ai-sdk
request-sdk 只负责网络层,不负责模型语义层。

2. 定位与边界

laybot/request-sdk 的定位是:

  • 通用服务端 HTTP 请求组件库
  • 第三方平台 SDK 的底层网络基座
  • 适用于 Webman / Workerman / CLI / FPM 的网络通信层

本库主要负责:

  • HTTP 请求发送
  • JSON / Form / Upload / Download
  • Bearer / ApiKey / Basic / Hmac / Inner 鉴权
  • Retry / Trace / Timeout
  • 原始响应获取
  • 基础流式请求
  • Query 编码控制

本库不负责

  • 大模型对话语义封装
  • Chat / Messages / Tool Calls / Function Calls 抽象
  • 多模型厂商(OpenAI / Gemini / Claude)对话协议适配
  • 流式对话增量聚合与最终消息拼装
  • 模型调用层的业务语义封装

如果你的目标是:

  • 调用大模型对话接口
  • 统一封装 OpenAI / Gemini / Claude
  • 处理流式对话增量
  • 封装 Chat / Embedding / Image / Audio 等模型能力

请使用上层语义 SDK:

laybot/ai-sdk

也就是说:

  • request-sdk 负责:怎么请求
  • ai-sdk 负责:怎么调用大模型

3. 推荐分层

在实际项目中,建议按以下分层使用:

  • request-sdk:底层网络请求层
  • openai / gemini / 其他平台管理 SDK:平台接口层
  • laybot/ai-sdk:大模型调用语义层
  • 业务项目:业务逻辑层

这样做的好处是:

  • 网络层能力统一
  • 鉴权、重试、日志、下载等能力不重复实现
  • 上层 SDK 只关心业务语义,不重复处理底层传输细节
  • 后续扩展新的平台 SDK 时,可以直接复用同一套网络底座

4. 安装

composer require laybot/request-sdk:^0.5

5. 适用场景

适合:

  • 各类 AI 平台管理类 SDK 底座
  • Webman / Workerman 后台服务
  • 定时同步任务
  • 管理后台
  • 内部 OpenAPI / 微服务调用
  • 文件上传下载
  • 基础流式请求
  • 其他企业级 server-to-server 网络请求

不直接面向:

  • 大模型对话语义封装
  • Chat / Embedding / Tool Calls 等模型调用层
  • 厂商流式协议适配与消息聚合

普通请求默认使用 Guzzle,稳定优先。
流式请求支持 Guzzle 与 Workerman 两种 transport,适合在不同运行环境下统一调用方式。

6. 快速开始

6.1 普通 GET

use LayBot\Request\Client;

$http = Client::make([
    'base_uri' => 'https://httpbin.org',
]);

$res = $http->get('/get', [
    'foo' => 'bar',
]);

var_dump($res);

6.2 POST JSON

$res = $http->postJson('/post', [
    'name' => 'LayBot',
    'scene' => 'demo',
]);

var_dump($res);

6.3 POST Form

$res = $http->postForm('/post', [
    'username' => 'demo',
    'password' => '123456',
]);

6.4 获取原始响应

$raw = $http->requestRaw('GET', '/get', [
    'query' => ['a' => 1],
]);

/*
[
  'status' => 200,
  'headers' => [...],
  'body' => '...'
]
*/

6.5 获取 JSON(mixed)

对于底层网络场景,有些接口返回的 JSON 不一定是对象,也可能是:

  • 数组
  • 字符串
  • 数字
  • 布尔值
  • null

可以使用:

$res = $http->requestJsonAny('GET', '/anything');

或者:

$res = $http->getAny('/anything');

6.6 Bearer 鉴权

$http = Client::make([
    'base_uri' => 'https://api.openai.com/v1',
    'token' => 'sk-xxx',
    'timeout' => 30,
    'retry' => 2,
    'verify' => true,
]);

6.7 文件上传

$res = $http->upload(
    '/post',
    'file',
    __DIR__ . '/demo.txt',
    ['scene' => 'test']
);

6.8 文件下载

$path = $http->download(
    '/image/png',
    __DIR__ . '/runtime/demo.png'
);

echo $path;

download() 使用临时文件写入,成功后再原子替换目标文件,适合大文件下载场景。

6.9 基础流式请求

$http = Client::make([
    'base_uri' => 'https://api.openai.com',
    'token' => 'sk-xxx',
    'transport' => 'auto',
]);

$http->stream('/v1/chat/completions', [
    'model' => 'gpt-4o-mini',
    'stream' => true,
    'messages' => [
        ['role' => 'user', 'content' => 'Hello']
    ],
], function (string $chunk, bool $done) {
    if ($done) {
        echo PHP_EOL . '[DONE]' . PHP_EOL;
        return;
    }

    echo $chunk;
});

stream() 面向基础流式场景,默认适合 data: 行流。
若要进行完整的大模型流式协议处理,请使用 laybot/ai-sdk

7. 配置项

配置项 说明 默认值
base_uri 基础地址,必填 -
headers 默认请求头 []
custom_headers 附加静态请求头 []
timeout 请求超时(秒) 10.0
transport auto / guzzle / workerman auto
retry 重试次数 2
verify 是否校验证书 true
query_array_format brackets / repeat brackets
user_agent 自定义 UA null
token Bearer Token null
api_key API Key null
api_secret Hmac Secret null
username Basic 用户名 null
password Basic 密码 null
inner_token 内部服务 Token null
logger PSR-3 Logger null
signer 自定义 signer null

8. 鉴权方式

8.1 Bearer

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'token' => 'your-token',
]);

等价于:

Authorization: Bearer your-token

8.2 ApiKey

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'api_key' => 'your-api-key',
    'header' => 'X-API-Key',
]);

8.3 Basic

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'username' => 'demo',
    'password' => '123456',
]);

8.4 Hmac

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'api_key' => 'app-key',
    'api_secret' => 'secret',
]);

8.5 Inner

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'inner_token' => 'inner-token',
]);

8.6 自定义静态 Header

适用于以下场景:

  • X-Export-Token
  • X-Service-Token
  • X-Internal-App
  • 其他任意固定 Header 标识
$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'custom_headers' => [
        'X-Export-Token' => 'your-export-token',
        'X-Service-Name' => 'paper-export',
    ],
]);

8.7 tokencustom_headers 的区别

token 是语义化快捷配置,专门表示 Bearer Token:

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'token' => 'your-bearer-token',
]);

等价于:

Authorization: Bearer your-bearer-token

custom_headers 表示任意附加静态请求头,例如:

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'custom_headers' => [
        'X-Export-Token' => 'your-export-token',
    ],
]);

等价于:

X-Export-Token: your-export-token

8.8 多 token / 多 Header 共存

在实际项目中,常见场景是:

  • 同时需要 Authorization: Bearer xxx
  • 又需要额外的 X-Export-Token: yyy
  • 或其他自定义服务身份 Header

这种情况下,可以直接组合使用:

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'token' => 'bearer-xxx',
    'custom_headers' => [
        'X-Export-Token' => 'export-yyy',
        'X-Service-Name' => 'paper-export',
    ],
]);

最终会同时携带:

Authorization: Bearer bearer-xxx
X-Export-Token: export-yyy
X-Service-Name: paper-export

8.9 鉴权自动推断规则

当未显式传入 signer 时,Client 会按以下顺序自动推断:

  1. api_key + api_secretHmacSigner
  2. tokenBearerSigner
  3. username + passwordBasicSigner
  4. inner_tokenInnerSigner
  5. api_keyApiKeySigner
  6. 默认 NoneSigner

custom_headers 只作为附加请求头,不参与 signer 推断。

如果你希望完全控制行为,建议直接传入 signer

8.10 高级用法:自定义 signer

如果你需要完全控制签名逻辑,可以显式传入自定义 signer:

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'signer' => $yourSigner,
]);

这适合:

  • 自定义签名算法
  • 动态时间戳 / 摘要签名
  • 非标准平台鉴权协议
  • 需要 method / path / body 参与签名的场景

9. Query 数组格式

支持两种 query 数组编码方式。

9.1 brackets(默认)

适合常规数组 query:

[
    'group_by' => ['project_id', 'line_item']
]

编码后类似:

group_by[0]=project_id&group_by[1]=line_item

9.2 repeat

适合某些 API 需要重复参数形式:

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'query_array_format' => 'repeat',
]);

会编码为:

group_by=project_id&group_by=line_item

repeat 主要适合一级数组重复参数场景。

10. 链式配置

$http = Client::make([
    'base_uri' => 'https://api.example.com',
]);

$http2 = $http
    ->withTimeout(30)
    ->withRetry(3)
    ->withVerify(true)
    ->withUserAgent('AI-Admin-Client/1.0')
    ->withHeaders([
        'X-App-Name' => 'admin-service',
    ]);

支持:

  • withSigner()
  • withLogger()
  • withRetry()
  • withHeaders()
  • withTimeout()
  • withVerify()
  • withUserAgent()
  • withQueryArrayFormat()

这些方法返回的是新的 Client 实例,不会修改原对象。
适合在常驻进程和并发场景中复用。

11. 方法总览

11.1 返回 JSON array 的方法

这些方法默认要求响应 JSON 解码结果为数组:

方法 说明
send() 通用请求,JSON 解码为 array
requestJsonArray() 通用请求,JSON 解码为 array
get() GET
postJson() POST JSON
postForm() POST Form
post() POST,数组默认按 Form 发送
put() PUT,数组默认按 JSON 发送
patch() PATCH,数组默认按 JSON 发送
delete() DELETE

11.2 返回 JSON mixed 的方法

这些方法适合底层通用场景:

方法 说明
sendAny() 通用请求,JSON 解码为 mixed
requestJsonAny() 通用请求,JSON 解码为 mixed
getAny() GET
postJsonAny() POST JSON
postFormAny() POST Form
postAny() POST
putAny() PUT
patchAny() PATCH
deleteAny() DELETE

11.3 原始响应方法

方法 说明
requestRaw() 获取原始响应
head() HEAD,返回原始响应
options() OPTIONS,返回原始响应

11.4 文件与流式方法

方法 说明
upload() 文件上传
download() 下载到本地文件
stream() 基础流式请求

12. 关于 post() / put() / patch() 的数组行为

为了兼顾常见使用习惯,这几个方法对数组参数的处理规则不同:

  • post(array):按 form_params 发送
  • put(array):按 json 发送
  • patch(array):按 json 发送

如果你需要明确语义,建议优先使用:

  • postJson()
  • postForm()
  • postJsonAny()
  • postFormAny()

13. 文件上传与下载

13.1 文件上传

$res = $http->upload(
    '/upload',
    'file',
    __DIR__ . '/demo.txt',
    ['scene' => 'test']
);

说明:

  • 上传时不要手动设置 Content-Type
  • multipart boundary 由底层 HTTP 客户端自动处理
  • 上传结束后文件句柄会自动释放

13.2 文件下载

$path = $http->download(
    '/image/png',
    __DIR__ . '/runtime/demo.png'
);

echo $path;

说明:

  • 下载使用临时文件写入,成功后再原子替换目标文件
  • 失败时会清理 .part 临时文件
  • 适合大文件下载场景

14. 流式请求

14.1 最简单的用法

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'token' => 'sk-xxx',
]);

$http->stream('/v1/stream', [
    'stream' => true,
    'input' => 'hello',
], function (string $chunk, bool $done) {
    if ($done) {
        echo PHP_EOL . '[DONE]' . PHP_EOL;
        return;
    }

    echo $chunk . PHP_EOL;
});

14.2 指定 Guzzle 作为流式传输层

$http->stream('/v1/stream', [
    'stream' => true,
], function (string $chunk, bool $done) {
    // ...
}, [], [
    'transport' => 'guzzle',
]);

对于关键业务链路,建议优先使用 guzzle

14.3 指定 Workerman 作为流式传输层

$http->stream('/v1/stream', [
    'stream' => true,
], function (string $chunk, bool $done) {
    // ...
}, [], [
    'transport' => 'workerman',
]);

说明:

  • 仅适合事件循环环境
  • 适合简单文本流场景
  • 遇到复杂响应头时会自动回退到 Guzzle

14.4 流式解析模式

data-line(默认)

只提取以 data: 开头的行,适合常见 SSE 风格接口。

$http->stream('/v1/stream', $payload, $cb, [], [
    'decode' => [
        'mode' => 'data-line',
    ],
]);

raw-line

按行返回原始文本,不要求必须是 data: 行。

$http->stream('/v1/stream', $payload, $cb, [], [
    'decode' => [
        'mode' => 'raw-line',
    ],
]);

14.5 自定义结束标记

默认结束标记为 [DONE]

$http->stream('/v1/stream', $payload, $cb, [], [
    'decode' => [
        'done_token' => '[END]',
    ],
]);

如果不希望底层识别结束标记,可以传 null

$http->stream('/v1/stream', $payload, $cb, [], [
    'decode' => [
        'done_token' => null,
    ],
]);

14.6 流式超时控制

$http->stream('/v1/stream', $payload, $cb, [], [
    'connect_timeout' => 10,
    'idle_timeout' => 120,
]);

说明:

  • connect_timeout:建立连接超时
  • idle_timeout:流式过程中连续静默超时,0 表示不限制

14.7 关于流式能力的边界

stream() 提供的是基础流式能力,适合:

  • text/event-stream
  • data: 行流
  • 文本行流
  • 基础 JSON 行流

默认会发送:

Accept: text/event-stream

如果目标服务要求其他 Accept,可以通过请求头覆盖。

它不负责:

  • 厂商流式协议适配
  • 增量消息聚合
  • 对话消息拼装
  • 模型层结束语义处理

如果你的目标是做大模型流式对话,请在上层 SDK 中处理协议语义,而不是把这些逻辑放在 request-sdk 中。

15. Retry 与 Trace

15.1 Retry

默认支持:

  • 网络异常重试
  • 5xx 重试
  • 429 重试
  • 指数退避 + 抖动
  • Retry-After 识别

配置示例:

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'retry' => 3,
]);

15.2 Trace

如果传入 PSR-3 Logger,会记录请求与响应调试日志。

$http = Client::make([
    'base_uri' => 'https://api.example.com',
    'logger' => $logger,
]);

Trace 中间件会自动对敏感 Header 做脱敏处理。

当前脱敏规则包括:

  • 明确高风险头:AuthorizationProxy-Authorization
  • 以及 Header 名中包含以下关键词的请求头:
    • token
    • secret
    • key
    • signature
    • sign

此外:

  • 文本类型 body 会按长度截断记录
  • 二进制 body 不直接写日志

生产环境建议谨慎开启 debug 级别 trace。

16. 异常说明

LayBot\Request\Exception\RequestException

基础请求异常。

LayBot\Request\Exception\HttpException

HTTP 非 2xx 响应异常,可获取:

  • getStatusCode()
  • getResponseBody()
  • getResponseHeaders()
  • getMethod()
  • getUri()

LayBot\Request\Exception\JsonException

JSON 编解码异常。

LayBot\Request\Exception\StreamException

流式请求异常。

17. 设计说明

17.1 普通请求

普通请求默认走 Guzzle,稳定优先。

17.2 流式请求

流式请求支持 Guzzle 与 Workerman 两种 transport。

其中:

  • guzzle 适合作为默认和主力方案
  • workerman 适合事件循环环境下的简单文本流场景

当检测到复杂响应头时,Workerman transport 会优先回退到 Guzzle 处理。

17.3 下载

download() 使用临时文件写入并原子替换目标文件,适合大文件下载场景。

17.4 参数约束

同一次请求中,以下 payload 模式只能使用一种:

  • json
  • form_params
  • multipart
  • body

18. 模块总览

模块 组件 作用
Client Client 统一请求入口与便捷方法
Config Config 不可变配置对象
Transport GuzzleTransport / WorkermanTransport 普通请求与流式请求传输层
Signer BearerSigner / ApiKeySigner / BasicSigner / HmacSigner / InnerSigner / NoneSigner 鉴权插拔
Middleware Retry / Trace 重试、追踪
Support Json / Query / UserAgent / Env JSON、Query、UA、环境辅助
Util StreamDecoder 基础流式解码
Facade PartnerApi / InnerApi 固定场景快捷封装

19. 与 laybot/ai-sdk 的关系

建议按以下方式分层:

  • laybot/request-sdk:网络层
  • laybot/ai-sdk:模型语义层
  • 业务项目:业务逻辑层

职责划分如下:

request-sdk 负责

  • HTTP 请求
  • 鉴权
  • Retry / Trace / Timeout
  • 上传下载
  • 原始响应
  • 基础流式传输

ai-sdk 负责

  • 模型厂商协议适配
  • Chat / Embedding / Image / Audio 等语义封装
  • 流式增量 JSON 解析
  • [DONE] / finish_reason / 厂商结束语义
  • 对话消息聚合

这样可以避免网络层和模型语义层相互耦合。

如果你的目标是做大模型对话、流式增量聚合、厂商协议适配,请使用上层 laybot/ai-sdk
request-sdk 只负责网络层,不负责模型语义层。

20. 版本建议

当前建议版本:

0.5.x

如果你在大型项目中使用,建议固定小版本范围,并在升级前做一次最小回归验证。

21. 关于 LayBot

LayBot · 灵语智教 专注教育与知识管理的 AIGC 平台,
拥有自研大模型、矢量检索、知识图谱等核心能力,并陆续开源 LayBot 系列 SDK

  • laybot/ai-sdk:大模型调用语义层 SDK
  • laybot/request-sdk:通用网络通信底座
  • storage-sdk:存储相关能力

欢迎关注与 Star。

22. 贡献指南

git clone https://github.com/laybot/request-sdk.git
cd request-sdk
composer install --dev

# 单元测试
vendor/bin/phpunit

License

MIT License