radiergummi / wander
适用于现代世界的PHP客户端
Requires
- php: >=7.4
- nyholm/psr7: ^1.3
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0
Requires (Dev)
- laminas/laminas-httphandlerrunner: ^1.1
- nyholm/psr7-server: ^1.0
- phpunit/phpunit: ^9.3
- symfony/process: ^5.1
- vimeo/psalm: ^3.14
Suggests
- ext-curl: To use the curl driver
This package is auto-updated.
Last update: 2024-09-24 22:10:10 UTC
README
PHP 7.4的现代、轻量级、快速且类型安全的HTTP客户端
注意:我仍在积极开发Wander。如果您想帮忙,可以,但建议您暂不使用它。
简介
在PHP中发送HTTP请求可能会很痛苦。要么您进入完全的低级模式并使用流或curl,要么您需要使用基于数组的配置、很多开销或缺少PSR兼容性的现有选择之一。Wander试图提供一个替代方案:它不会默认解决所有问题,而是保持简单和可扩展。Wander做出了一些合理的假设,但如果这些假设对于您的用例不正确,您仍然可以扩展它。
功能
- 简单、易发现API Wander将所有请求选项都公开为可链式方法。
- 完全符合标准 Wander依赖于PSR-17工厂、PSR-18客户端驱动程序和PSR-7请求/响应。使用我们的首选实现(nyholm/psr7)或自行提供。
- 可插拔序列化 请求和响应体根据内容类型进行序列化,透明且自动。如果您使用我们尚未知的格式?添加自己的(并提交一个PR!)
- 与其他解决方案兼容 由于驱动程序本质上是PSR-18客户端,因此您可以交换任何其他客户端库并使其立即工作。这提供了一条平滑的迁移路径。
- 广泛的异常 Wander抛出了几个异常,所有这些异常都遵循清晰的继承结构。这使得处理错误变得特别容易,可以根据需要以粗粒度或细粒度进行处理。
$responseBody = (new Wander()) ->patch('https://example.com') ->withQueryParameter('foo', 'bar') ->withBasicAuthorization('User', 'Pass') ->withHeaders([ Header::ACCEPT => MediaType::APPLICATION_JSON, ]) ->withoutHeader(Header::USER_AGENT) ->withBody([ 'test' => true ]) ->asJson() ->run() ->getBody() ->getContents();
安装
使用composer安装
composer require radiergummi/wander
用法
以下部分提供了几个具体的用法示例。对于完整参考,请参阅参考部分。
请求简写
Wander内置了多个简写层,使其尽可能简单。要执行简单的GET
请求,以下就足够了
$client = new Wander(); $response = $client ->get('https://example.com') ->run();
Wander在Wander
对象上内置了多个对常用HTTP方法(GET
、PUT
、POST
、DELETE
等)的简写。
创建请求上下文
以上示例的稍长版本,使用内部也使用的createContext
方法
$client = new Wander(); $response = $client ->createContext(Method::GET, 'https://example.com') ->run();
此处创建的上下文围绕PSR-7请求包装,并添加了一些辅助方法,使得可以链式调用方法。这样做需要创建请求实例,这当然依赖于您可以替换为自己的PSR-17工厂。下面将详细介绍。
直接发送PSR-7请求
Wander还支持直接处理请求实例
$client = new Wander(); $request = new Request(Method::GET, 'https://example.com'); $response = $client->request($request);
发送任意请求体
Wander接受任何类型的作为请求体的数据。它将在发送请求之前根据当时设置的Content-Type
头进行序列化。这意味着您不需要关心体序列化。
但是,如果您将PSR-7 StreamInterface
实例设置为体,Wander将不会尝试修改流并按原样使用它。
发送JSON请求
$client = new Wander(); $response = $client ->post('https://example.com') ->withBody([ 'anything' => true ]) ->asJson() ->run();
发送原始流
$client = new Wander(); $response = $client ->post('https://example.com') ->withBody(Stream::create('/home/moritz/large.mp4')) ->run();
检索解析后的响应体
随着请求上下文围绕着请求实例,还有响应上下文围绕着PSR-7 响应,提供额外的辅助工具,例如获取响应体的解析表示,如果可能的话。
$client = new Wander(); $response = $client ->get('https://example.com') ->run() ->getParsedBody();
异常处理
Wander遵循一个异常层次结构,代表不同类别的错误。与PSR-18客户端相反,我坚信400或500范围内的响应状态码应该抛出异常,因为你最终还是需要检查它们。异常是朋友!尤其是在HTTP的情况下,错误可能是流程中预期的部分。
异常树如下所示
WanderException (inherits from \RuntimeException)
├─ ClientException (implements PSR-18 ClientExceptionInterface)
├─ DriverException (implements PSR-18 RequestExceptionInterface)
├─ ConnectionException (implements PSR-18 NetworkExceptionInterface)
├─ SslCertificateException (implements PSR-18 NetworkExceptionInterface)
├─ UnresolvableHostException (implements PSR-18 NetworkExceptionInterface)
└─ ResponseErrorException
├─ ClientErrorException
│ ├─ BadRequestException
│ ├─ UnauthorizedException
│ ├─ PaymentRequiredException
│ ├─ ForbiddenException
│ ├─ NotFoundException
│ ├─ MethodNotAllowedException
│ ├─ NotAcceptableException
│ ├─ ProxyAuthenticationRequiredException
│ ├─ RequestTimeoutException
│ ├─ ConflictException
│ ├─ GoneException
│ ├─ LengthRequiredException
│ ├─ PreconditionFailedException
│ ├─ PayloadTooLargeException
│ ├─ UriTooLongException
│ ├─ UnsupportedMediaTypeException
│ ├─ RequestedRangeNotSatisfyableException
│ ├─ ExpectationFailedException
│ ├─ ImATeapotException
│ ├─ MisdirectedRequestException
│ ├─ UnprocessableEntityException
│ ├─ LockedException
│ ├─ FailedDependencyException
│ ├─ TooEarlyException
│ ├─ UpgradeRequiredException
│ ├─ PreconditionRequiredException
│ ├─ TooManyRequestsException
│ ├─ RequestHeaderFieldsTooLargeException
│ └─ UnavailableForLegalReasonsException
└─ ServerErrorException
├─ InternalServerErrorException
├─ NotImplementedException
├─ BadGatewayException
├─ ServiceUnavailableException
├─ GatewayTimeoutException
├─ HTTPVersionNotSupportedException
├─ VariantAlsoNegotiatesException
├─ InsufficientStorageException
├─ LoopDetectedException
├─ NotExtendedException
└─ NetworkAuthenticationRequiredException
所有响应错误异常都提供了请求和响应实例的getter方法,因此你可以轻松地做这样的事情
try { $request->run(); } catch (UnauthorizedException | ForbiddenException $e) { $this->refreshAccessToken(); return $this->retry(); } catch (GoneException $e) { throw new RecordDeletedExeption( $e->getRequest()->getUri()->getPath() ); } catch (BadRequestException $e) { $responseBody = $e->getResponse()->getBody()->getContents(); $error = json_decode($responseBody, JSON_THROW_ON_ERROR); $field = $error['field'] ?? null; if ($field) { throw new ValidatorException("Failed to validate {$field}"); } throw new UnknownException($error); } catch (WanderException $e) { // Simply catch all others throw new RuntimeException( 'Server returned an unknown error: ' . $e->getResponse()->getBody()->getContents() ); }
这只是处理这些异常类型错误的一种方法!
设置请求超时
请求超时可以在您的驱动实例上配置
$driver = new StreamDriver(); $driver->setTimeout(3000); // 3000ms / 3s $client = new Wander($driver);
注意
请求超时是驱动程序的可选功能,由SupportsTimeoutsInterface
表示。但是,所有默认驱动程序都实现了此接口,所以你只需要检查如果你使用其他实现。
禁用跟随重定向
默认情况下,驱动程序将跟随重定向。如果您想禁用此行为,请配置您的驱动实例
$driver = new StreamDriver(); $driver->followRedirects(false); $client = new Wander($driver);
注意
重定向是驱动程序的可选功能,由SupportsRedirectsInterface
表示。但是,所有默认驱动程序都实现了此接口,所以你只需要检查如果你使用其他实现。
限制最大重定向次数
默认情况下,驱动程序将无限期地跟随重定向。如果您想限制最大重定向次数,请在您的驱动实例上配置
$driver = new StreamDriver(); $driver->setMaximumRedirects(3); $client = new Wander($driver);
注意
重定向是驱动程序的可选功能,由SupportsRedirectsInterface
表示。但是,所有默认驱动程序都实现了此接口,所以你只需要检查如果你使用其他实现。
添加体序列化器
Wander通过通过序列化器类传递数据支持请求和响应的透明体序列化。开箱即用,Wander提供了纯文本、JSON、XML、表单数据和多部分体的序列化器。序列化器遵循一个定义良好的接口,因此您可以轻松地为任何数据格式添加自己的序列化器
$client = new Wander(); $client->addSerializer('your/media-type', new CustomSerializer());
序列化器将为任何具有此媒体类型设置为Content-Type
头部的请求和响应调用。
使用自定义驱动程序
驱动程序实际处理请求的调度和响应的处理。它们只有一个简单职责:将请求实例转换为响应实例。默认情况下,Wander使用一个包装流的驱动程序,但它也提供了一个curl驱动程序。如果您需要其他内容或需要默认驱动程序的变体,您可以创建一个新的实现DriverInterface
的驱动程序,或者扩展一个默认驱动程序。
$driver = new class implements DriverInterface { public function sendRequest(RequestInterface $request): ResponseInterface { // TODO: Implement sendRequest() method. } public function setResponseFactory(ResponseFactoryInterface $responseFactory): void { // TODO: Implement setResponseFactory() method. } }; $client = new Wander($driver);
参考
本参考展示了所有可用方法。
Wander: HTTP 客户端
本节描述了HTTP客户端的所有方法。在创建新实例时,您可以传递多个依赖项
new Wander( DriverInterface $driver = null, ?RequestFactoryInterface $requestFactory = null, ?ResponseFactoryInterface $responseFactory = null )
get
:创建上下文简写
为GET
请求创建新的请求上下文。
get(UriInterface|string $uri): Context
post
:创建上下文简写
为POST
请求创建新的请求上下文。
post(UriInterface|string $uri, ?mixed $body = null): Context
put
:创建上下文简写
为PUT
请求创建新的请求上下文。
put(UriInterface|string $uri, ?mixed $body = null): Context
patch
:创建上下文简写
为PATCH
请求创建新的请求上下文。
patch(UriInterface|string $uri, ?mixed $body = null): Context
delete
:创建上下文简写
为DELETE
请求创建新的请求上下文。
delete(UriInterface|string $uri, ?mixed $body = null): Context
head
:创建上下文简写
为HEAD
请求创建一个新的请求上下文。
head(UriInterface|string $uri, ?mixed $body = null): Context
options
:创建上下文简写
为OPTIONS
请求创建一个新的请求上下文。
options(UriInterface|string $uri, ?mixed $body = null): Context
createContext
允许创建任意请求方法的新的请求上下文。
createContext(string $method, UriInterface|string $uri): Context
createContextFromRequest
允许从现有的请求实例创建新的请求上下文。
createContextFromRequest(RequestInterface $request): Context
request
在客户端实例的驱动上调度请求实例,并返回响应。
request(RequestInterface $request): ResponseInterface
上下文:请求上下文
上下文对象对底层请求实例进行转换。遵循PSR-7的精神,请求当然是不可变的。上下文将仅保留对当前实例的引用。这使得我们可以链接所有方法调用和调度请求,而不离开“链”一次。我们还可以添加辅助方法并保留对其他对象的引用——例如客户端本身——使其非常易于使用和扩展。请注意,您应依赖客户端为您创建上下文;手动使用构造函数是不被推荐的。
new Context( HttpClientInterface $client, RequestInterface $request )
setRequest
替换请求实例。
getRequest
检索请求实例。
withMethod
替换HTTP请求方法。
getMethod
检索HTTP请求方法。
withUri
替换URI实例。
getUri
检索URI实例。
withQueryString
将查询字符串添加到URI中。
getQueryString
从URI中检索查询字符串。
withQueryParameters
将多个查询参数添加到URI中。
getQueryParameters
将URI的所有查询参数作为字典检索。
withQueryParameter
将查询参数添加到URI中。
withoutQueryParameter
从URI中移除单个查询参数。
getQueryParameter
通过名称从URI中检索单个查询参数。
withHeaders
向请求中添加多个标题。
getHeaders
作为字典检索所有请求标题。代理PSR-7请求方法。
withHeader
向请求中添加指定的标题。代理PSR-7请求方法。
withoutHeader
如果请求上设置了指定的标题,则移除它。代理PSR-7请求方法。
getHeader
检索所有标题值的数组。代理PSR-7请求方法。
getHeaderLine
将所有标题值,由逗号分隔,作为一个单独的字符串检索。代理PSR-7请求方法。
withAuthorization
将Authorization
标题设置为给定的身份验证类型和凭据。
withBasicAuthorization
将Authorization
标题设置为类型Basic
并将逗号分隔的凭据编码为Base64。
withBearerAuthorization
将Authorization
标题设置为类型Bearer
并使用令牌作为凭据。
withContentType
设置Content-Type
标题。
getContentType
如果设置了Content-Type
标题,则检索其值,否则返回null
。
asJson
将Content-Type
标题设置为JSON(application/json
)。
asXml
将Content-Type
标题设置为XML(text/xml
)。
asPlainText
将Content-Type
标题设置为纯文本(text/plain
)。
withBody
在上下文中设置(未序列化)的正文数据。在调度请求之前,这将根据Content-Type
标题进行序列化,自动处理序列化,因此您不必这样做。通过传递Stream实例,此过程将被跳过,正文将作为请求上的原始数据设置。
getBody
检索当前的正文数据。
hasBody
检查上下文是否在其正文中包含任何数据。
run
将请求调度到客户端实例并创建一个响应上下文。
贡献
欢迎所有贡献,但请注意以下几点要求
- 我们使用 psalm 进行静态分析,并希望保持至少 2级(但长远目标是达到1级)。任何分析结果下降的PR将不会被接受。要运行psalm,使用
composer run static-analysis
命令。 - 每个PR都必须提供单元和集成测试。要运行所有测试套件,使用
composer run test
命令。