一个小巧现代的 PSR-7 & PSR-18 实现

dev-trunk 2021-07-23 00:08 UTC

This package is auto-updated.

Last update: 2024-09-23 07:22:53 UTC


README

一个小巧现代的,兼容 PSR-7PSR-17PSR-18 的 PHP 网络库,灵感来源于 Go 的 net 包。

特性

  • 无硬依赖;
  • 采用 BYOB 方法处理请求和响应;
  • 零配置 - 纯数据和接口;
  • 使用第三方 API 进行测试变得简单;

设置

一些基本说明,说明如何在自己的项目中使用此库,或者作为开发者为其做出贡献。

要求

  • PHP >=8.0
  • 任何 PSR-7 HTTP 消息实现;

安装

使用 composer 并通过自动加载器访问。

composer require minibase-app/net

对贡献者

克隆仓库并安装开发工具以开始为您的功能运行测试和修复错误。

git clone https://github.com/minibase-app/net.git \
  && cd ./net \
  && make vendor;

用法

以下是一个关于库最基本设置和用法的片段。

如前所述,在 要求特性 中,您可以使用您选择的任何 PSR-7 实现。在此之后的所有示例中,我们使用 Laminas 进行演示,但任何东西都行,无论是手工制作的还是来自另一个库。

use Laminas\Diactoros\{ Request, Response, Uri };
use Minibase\Net\Http;
use Psr\Http\Message\{ RequestInterface, ResponseInterface, UriInterface };

# Initiate a new HTTP instance with PSR-17 requesst & response factory closures
$http = new Http(
    fn (string $method, UriInterface $uri): RequestInterface =>
        new Request($uri, $method),
    fn (mixed $body, int $code, string $reason): ResponseInterface =>
        new Response($body, $code),
);

# Store and fetch a URI instance of your API base URL
$api = new Uri('https:///api');

# Make a request to (any) API and specify resources, queries, etc.
$response = $http->get($api->withPath('users')->withQuery('?_limit=1'));

# Introspect your response
$successful = Http::OK === $response->getStatusCode();

如果这不是对如何工作的足够好的解释,请不要担心 - 我们将在下一节中详细说明每个部分。

创建新的 HTTP 实例

因为这个库不提供(又一)PSR-7 实现,你必须自己提供一个。这意味着你可以自己制作一个,使用你公司的实现,或者从社区中挑选你最喜欢的包 - 无论如何都无关紧要,我们只关心接口。

一旦你有了 PSR-7 实现,就是时候告诉 Http 类如何创建请求和响应了。这可能是库中最好的部分;与传统 PHP HTTP "客户端"库不同,这个库并不试图将请求/响应抽象到基于数组的配置中,而是使用它们 作为 配置。换句话说,你的请求就是它的命令,字面上的...

让我们看一下。

# Here we will make a simple JSON REST API implementation
# Notice that $request and $response are merely PSR-17 factories!
$request = fn (string $method, UriInterface $uri): RequestInterface =>
    new Request($method, $uri, headers: ['content-type' => 'application/json']);
$response = fn (array $body, int $code, string $reason): ResponseInterface =>
    new JsonResponse($body, $code);
$http = new Http($request, $response);

然而,敏锐的读者会发现,$response 闭包并没有完全符合 PSR-17 ResponseFactoryInterface,因为它实际上在收到代码和原因之前接收到了一个体。请放心,这种接口违规只存在于构造函数中 - Http::createResponse 方法按预期实现,并这样做是为了满足你的要求。

作为 URI 的 API

大多数 HTTP "客户端"库都会有一些方法签名,要求以字符串形式提供 URL/端点,并提供可选的查询参数(GET 参数)。这往往会导致开发人员错误地使用库,直接将查询参数放入 URL/端点参数中,使用各种字符串连接和插值方法,导致非标准化和混乱的代码。

为了防止这种不一致(以及其他你可能遇到的),已经取消使用 URL,转而使用第一级的 UriInterface 实例。

# Store this or a factory for it anywhere along with other API details, like
# auth, headers, etc.!
$github = new Uri('https://api.github.com');

一开始可能感觉平淡无奇,但与配置了某种JSON响应的Http实例一起使用时,我们可以在代码中获得流畅且易于阅读的一组API调用。

发起请求

继续上面的示例,让我们轻松且优雅地发起对GitHub用户资料的请求。

$user = $http
  ->get($github->withPath('users/tpope'))
  ->getPayload();

代码可读性极强,可以根据需要进一步抽象,提供与传统HTTP“客户端”包相比,更接近Redis、查询构建器和流畅接口的体验。

动机

是的,但问题是...为什么?

这是一个好问题,回答也类似;为什么不?更具体地说,也许更加严重的是,PHP中的HTTP客户端似乎有着设计得非常相似的历史,创建了基于数组的自配置,对有效的HTTP响应抛出异常,并且采用“自上而下”的方法创建客户端(例如new Client('http://api.acme.io')),这可能会迫使你为多个API需要多个实例,所有这些最终导致开发者和维护体验笨拙。

这个库试图解决的其他问题是“客户端”。我们都经历过导入多个Client类,到处别名和扩展的经历。“客户端”这个术语模糊不清,类似于dataparams,本质上没有意义,甚至不是HTTP特定的(客户端什么?)。受Go的net包的启发,Http似乎是一个完美的选择。

除了设计和使用上的差异之外,Net还试图保持一个瘦、具体、无依赖(除了PSR之外)的基于API的架构,这不会在大规模项目升级期间导致依赖冲突,这在遗留项目追赶最新版本时经常发生。