minibase-app / net
一个小巧现代的 PSR-7 & PSR-18 实现
Requires
- php: ^8.0
- ext-curl: *
- ext-json: *
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.15
- friendsofphp/php-cs-fixer: ^3.0
- infection/infection: ^0.23.0
- laminas/laminas-diactoros: ^2.6
- marcocesarato/php-conventional-changelog: ^1.10
- nunomaduro/patrol: ^1.0
- nunomaduro/phpinsights: ^2.0
- pestphp/pest: ^1.10
- phpbench/phpbench: ^1.0
- phpunit/phpunit: ^9.5
- react/react: ^1.2
- symfony/var-dumper: ^5.3
README
一个小巧现代的,兼容 PSR-7、PSR-17 和 PSR-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
类,到处别名和扩展的经历。“客户端”这个术语模糊不清,类似于data
和params
,本质上没有意义,甚至不是HTTP特定的(客户端的什么?)。受Go的net
包的启发,Http
似乎是一个完美的选择。
除了设计和使用上的差异之外,Net还试图保持一个瘦、具体、无依赖(除了PSR之外)的基于API的架构,这不会在大规模项目升级期间导致依赖冲突,这在遗留项目追赶最新版本时经常发生。