clue / buzz-react
Requires
- php: >=5.3
- psr/http-message: ^1.0
- react/event-loop: ^1.0 || ^0.5
- react/http-client: ^0.5.10
- react/promise: ^2.2.1 || ^1.2.1
- react/promise-stream: ^1.0 || ^0.1.2
- react/socket: ^1.1
- react/stream: ^1.0 || ^0.7
- ringcentral/psr7: ^1.2
Requires (Dev)
- clue/block-react: ^1.0
- clue/http-proxy-react: ^1.3
- clue/reactphp-ssh-proxy: ^1.0
- clue/socks-react: ^1.0
- phpunit/phpunit: ^9.0 || ^5.7 || ^4.8.35
- react/http: ^0.8
README
此包已迁移到 react/http,仅存在以支持向下兼容。
$ composer require react/http
如果您之前使用过此包,升级不应超过几分钟。所有类都已从最新的 v2.9.0
版本直接合并,没有其他重大更改,因此您可以简单地更新代码以使用更新的命名空间,如下所示
// old $browser = new Clue\React\Buzz\Browser($loop); $browser->get($url); // new $browser = new React\Http\Browser($loop); $browser->get($url);
有关更多详细信息,请参阅 react/http。
以下文档适用于此包的最后一个版本。进一步的开发将在更新的 react/http 中进行,因此强烈建议您尽快升级。
遗留的 clue/reactphp-buzz 
简单的异步PSR-7 HTTP客户端,用于并发处理任意数量的HTTP请求,基于 ReactPHP。
此库深受伟大的 kriswallsmith/Buzz 项目的启发。然而,它不阻塞在每个请求上,而是依赖于 ReactPHP的EventLoop 来并行处理多个请求。这允许您同时与多个HTTP服务器交互(获取URL,与RESTful API通信,跟踪重定向等)。与底层的 react/http-client 不同,此项目旨在提供一个高级API,易于使用,以并发处理多个HTTP请求,而不必处理大多数底层细节。
- 异步执行HTTP请求 - 并行向任意数量的HTTP服务器发送任意数量的HTTP请求,并在结果到来时立即处理它们的响应。基于Promise的设计提供了一种处理超出范围响应的合理的接口。
- 标准接口 - 通过实现 PSR-7 (http-message) 接口,ReactPHP的标准 promises 和 流接口,允许轻松集成现有的高级组件。
- 轻量级,SOLID设计 - 提供了一个薄薄的抽象,它足够合适并且不会妨碍您。建立在经过良好测试的组件和已确立的概念之上,而不是重新发明轮子。
- 良好的测试覆盖率 - 包含自动测试套件,并在现实世界中进行定期测试。
目录
支持我们
我们在开发、维护和更新我们的优秀开源项目上投入了大量的时间。您可以通过成为GitHub上的赞助商来帮助我们保持我们工作的这种高质量。赞助商将获得许多回报,有关详细信息,请参阅我们的赞助页面。
让我们共同努力将这些项目提升到下一个水平!🚀
快速入门示例
安装后,您可以使用以下代码访问HTTP网络服务器并发送一些简单的HTTP GET请求
$loop = React\EventLoop\Factory::create(); $client = new Clue\React\Buzz\Browser($loop); $client->get('http://www.google.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders(), (string)$response->getBody()); }); $loop->run();
另请参阅示例。
用法
请求方法
最重要的是,该项目提供了一个Browser
对象,该对象提供了几个类似于HTTP协议方法的函数
$browser->get($url, array $headers = array()); $browser->head($url, array $headers = array()); $browser->post($url, array $headers = array(), string|ReadableStreamInterface $contents = ''); $browser->delete($url, array $headers = array(), string|ReadableStreamInterface $contents = ''); $browser->put($url, array $headers = array(), string|ReadableStreamInterface $contents = ''); $browser->patch($url, array $headers = array(), string|ReadableStreamInterface $contents = '');
这些函数中的每一个都需要一个$url
和一些可选参数来发送HTTP请求。每个方法名都与相应的HTTP请求方法相匹配,例如get()
方法发送HTTP GET
请求。
您可以可选地传递一个包含附加$headers
的关联数组,这些headers将随此HTTP请求一起发送。此外,每个方法都会自动添加一个匹配的Content-Length
请求头,如果提供了出站请求体且其大小已知且非空。对于空请求体,如果请求方法通常期望请求体(仅适用于POST
、PUT
和PATCH
HTTP请求方法),则将仅包含Content-Length: 0
请求头。
如果您正在使用流式请求体,它将默认使用Transfer-Encoding: chunked
,除非您明确传递一个匹配的Content-Length
请求头。有关更多详细信息,请参阅流式请求。
默认情况下,上述所有方法都使用HTTP/1.1协议版本发送请求。如果您想显式使用旧的HTTP/1.0协议版本,可以使用withProtocolVersion()
方法。如果您想使用任何其他或自定义的HTTP请求方法,可以使用request()
方法。
上述每种方法都支持异步操作,并且要么以ResponseInterface
方式fulfills,要么以rejects方式返回一个Exception
。请参阅以下章节关于promises的更多细节。
promises
发送请求是异步的(非阻塞),因此您实际上可以并行发送多个请求。浏览器会对每个请求响应一个ResponseInterface
消息,顺序没有保证。发送请求使用基于Promise的接口,这使得在HTTP请求完成时(即成功满足或因错误而被拒绝)很容易做出反应。
$browser->get($url)->then( function (Psr\Http\Message\ResponseInterface $response) { var_dump('Response received', $response); }, function (Exception $error) { var_dump('There was an error', $error->getMessage()); } );
如果您觉得这很奇怪,也可以使用更传统的阻塞API。
请注意,使用完整响应消息解析Promise意味着整个响应体必须保存在内存中。这很容易开始,并且对于较小的响应(如常见的HTML页面或RESTful或JSON API请求)来说效果相当好。
您还可以查看流API
- 如果您正在处理大量的并发请求(100+)或
- 如果您想在数据块发生时处理它们(而不必等待完整的响应体)或
- 如果您预计响应体很大(例如,1 MiB或更多,当下载二进制文件时)或
- 如果您不确定响应体的尺寸(当访问任意远程HTTP端点且响应体尺寸事先未知时,最好是谨慎行事)。
取消
返回的Promise是以一种可以在挂起时取消的方式实现的。取消挂起的Promise会使用Exception拒绝其值,并清理任何底层资源。
$promise = $browser->get($url); $loop->addTimer(2.0, function () use ($promise) { $promise->cancel(); });
超时
此库使用一个非常高效的HTTP实现,因此大多数HTTP请求通常只需几毫秒即可完成。然而,当在不可靠的网络(互联网)上发送HTTP请求时,有各种可能出错的事情,可能会在一段时间后导致请求失败。因此,此库尊重PHP的default_socket_timeout
设置(默认60秒)作为发送出站HTTP请求和等待成功响应的超时,否则将取消挂起的请求,并使用Exception拒绝其值。
请注意,此超时值涵盖了创建底层传输连接、发送HTTP请求、接收HTTP响应头和完整响应体以及遵循任何最终的重定向。有关配置要遵循的重定向次数(或完全禁用重定向)的详细信息,请参阅下面的重定向,以及下面的流,以不考虑接收大响应体。
您可以使用 withTimeout()
方法 来传递自定义的超时时间(以秒为单位),例如这样
$browser = $browser->withTimeout(10.0); $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { // response received within 10 seconds maximum var_dump($response->getHeaders()); });
类似地,您可以使用布尔值 false
来完全不应用超时,或者使用布尔值 true
来恢复默认处理。有关详细信息,请参阅 withTimeout()
。
如果您使用的是 流式响应体,则接收响应体流所需的时间将不计入超时。这允许您更长时间地保持此传入流打开,例如在下载非常大的流或通过长期连接传输数据时。
如果您使用的是 流式请求体,则发送请求体流所需的时间将不计入超时。这允许您更长时间地保持此传出流打开,例如在上传非常大的流时。
请注意,此超时处理适用于高级HTTP层。例如,socket和DNS等低层可能也会应用(不同的)超时值。特别是,底层socket连接使用相同的 default_socket_timeout
设置来建立底层传输连接。要控制此连接超时行为,您可以像这样 注入自定义的 Connector
$browser = new Clue\React\Buzz\Browser( $loop, new React\Socket\Connector( $loop, array( 'timeout' => 5 ) ) );
身份验证
此库支持使用 Authorization: Basic …
请求头进行 HTTP基本身份验证,或者允许您显式设置 Authorization
请求头。
默认情况下,此库不包含出站的 Authorization
请求头。如果服务器需要身份验证,它可能会返回 401
(未授权)状态码,默认情况下将拒绝请求(也请参阅下文的 withRejectErrorResponse()
方法)。
为了传递身份验证详情,您可以将用户名和密码作为请求URL的一部分传递,例如这样
$promise = $browser->get('https://user:pass@example.com/api');
请注意,身份验证详情中的特殊字符必须进行百分号编码,也请参阅 rawurlencode()
。此示例将自动使用出站的 Authorization: Basic …
请求头传递base64编码的身份验证详情。如果您与之通信的HTTP端点需要任何其他身份验证方案,您也可以显式传递此头。这在使用(RESTful)HTTP API时很常见,这些API使用OAuth访问令牌或JSON Web Tokens (JWT)。
$token = 'abc123'; $promise = $browser->get( 'https://example.com/api', array( 'Authorization' => 'Bearer ' . $token ) );
在跟随重定向时,默认情况下不会将 Authorization
请求头发送到任何远程主机。当跟随包含身份验证详情的 Location
响应头时的重定向,这些详情将被发送到后续请求。有关详细信息,请参阅下文的 重定向。
重定向
默认情况下,此库会跟随任何重定向并遵守远程服务器通过 Location
响应头返回的 3xx
(重定向)状态码。Promise将使用重定向链中的最后一个响应来履行。
$browser->get($url, $headers)->then(function (Psr\Http\Message\ResponseInterface $response) { // the final response will end up here var_dump($response->getHeaders()); });
任何重定向请求都将遵循原始请求的语义,并将包括与原始请求相同的请求头,除了以下列出的那些。如果原始请求包含请求体,则此请求体将永远不会传递给重定向请求。因此,每个重定向请求都将删除任何 Content-Length
和 Content-Type
请求头。
如果原始请求使用了带有 Authorization
请求头的 HTTP 认证,那么只有当重定向的 URL 使用相同的域名时,这个请求头才会作为重定向请求的一部分传递。换句话说,由于可能存在的隐私/安全顾虑,Authorization
请求头不会被转发到其他外国主机。当跟随重定向时,如果 Location
响应头包含了认证信息,这些信息将被发送到后续请求。
您可以使用 withFollowRedirects()
方法来控制要跟随的最大重定向数,或者以原样返回任何重定向响应并应用自定义重定向逻辑,如下所示:
$browser = $browser->withFollowRedirects(false); $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { // any redirects will now end up here var_dump($response->getHeaders()); });
有关更多详细信息,请参阅 withFollowRedirects()
。
阻止
如上所述,这个库默认提供了一个强大的、异步的 API。
然而,如果您想将其集成到传统的、阻塞的环境中,您应该考虑同时使用 clue/reactphp-block。
生成的阻塞代码可能看起来像这样:
use Clue\React\Block; $loop = React\EventLoop\Factory::create(); $browser = new Clue\React\Buzz\Browser($loop); $promise = $browser->get('http://example.com/'); try { $response = Block\await($promise, $loop); // response successfully received } catch (Exception $e) { // an error occured while performing the request }
同样,您还可以并发处理多个请求,并等待一个 Response
对象数组。
$promises = array( $browser->get('http://example.com/'), $browser->get('http://www.example.org/'), ); $responses = Block\awaitAll($promises, $loop);
有关更多详细信息,请参阅 clue/reactphp-block。
请记住上述关于在内存中缓冲整个响应消息的说明。作为替代方案,您还可以查看以下章节中的 流式 API。
并发
如上所述,这个库提供了一个强大的异步 API。能够一次发送大量请求是这个项目的一个核心特性。例如,您可以在同时处理 SQL 查询的同时轻松并发发送 100 个请求。
请记住,力量越大,责任越大。发送过多的请求可能会耗尽您这边的所有资源,或者如果远程方看到您这边的请求数量不合理,甚至可能会被禁用。
// watch out if array contains many elements foreach ($urls as $url) { $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders()); }); }
因此,通常建议对发送端的并发性进行限制,以合理的值。通常使用一个相对较小的限制,因为同时做超过十几件事情可能会轻易压倒接收方。您可以使用 clue/reactphp-mq 作为轻量级的内存队列,以并发执行许多(但不是太多)事情。
// wraps Browser in a Queue object that executes no more than 10 operations at once $q = new Clue\React\Mq\Queue(10, null, function ($url) use ($browser) { return $browser->get($url); }); foreach ($urls as $url) { $q($url)->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders()); }); }
超过并发限制的附加请求将自动入队,直到其中一个挂起的请求完成。这很好地与现有的基于 Promise 的 API 集成。有关更多详细信息,请参阅 clue/reactphp-mq。
这种方法对于处理数千个挂起的请求来说是合理的。如果您正在处理非常大的输入列表(例如 CSV 或 NDJSON 文件中的数百万行),您可能需要考虑使用流式方法。有关详细信息,请参阅 clue/reactphp-flux。
流式响应
上述所有示例都假设您想要将整个响应体存储在内存中。这很容易入门,并且对于较小的响应来说表现良好。
然而,在以下几种情况下,通常使用流式处理方法会更好,这种方法只需要在内存中保留小块数据。
- 如果您正在处理大量的并发请求(100+)或
- 如果您想在数据块发生时处理它们(而不必等待完整的响应体)或
- 如果您预计响应体很大(例如,1 MiB或更多,当下载二进制文件时)或
- 如果您不确定响应体的尺寸(当访问任意远程HTTP端点且响应体尺寸事先未知时,最好是谨慎行事)。
您可以使用requestStreaming()
方法发送任意HTTP请求并接收流式响应。它使用相同的HTTP消息API,但不会在内存中缓冲响应体。它只在小块数据接收时处理响应体,并通过ReactPHP的Stream API转发这些数据。这对于任意大小的响应都是有效的。
这意味着它使用正常的ResponseInterface
来解析,可以像通常一样访问响应消息参数。您像通常一样访问消息体,但现在它还实现了ReactPHP的ReadableStreamInterface
以及PSR-7的StreamInterface
的部分。
$browser->requestStreaming('GET', $url)->then(function (Psr\Http\Message\ResponseInterface $response) { $body = $response->getBody(); assert($body instanceof Psr\Http\Message\StreamInterface); assert($body instanceof React\Stream\ReadableStreamInterface); $body->on('data', function ($chunk) { echo $chunk; }); $body->on('error', function (Exception $error) { echo 'Error: ' . $error->getMessage() . PHP_EOL; }); $body->on('close', function () { echo '[DONE]' . PHP_EOL; }); });
您可以在消息体上调用以下方法
$body->on($event, $callback); $body->eof(); $body->isReadable(); $body->pipe(React\Stream\WritableStreamInterface $dest, array $options = array()); $body->close(); $body->pause(); $body->resume();
由于消息体处于流式状态,调用以下方法没有太多意义
$body->__toString(); // '' $body->detach(); // throws BadMethodCallException $body->getSize(); // null $body->tell(); // throws BadMethodCallException $body->isSeekable(); // false $body->seek(); // throws BadMethodCallException $body->rewind(); // throws BadMethodCallException $body->isWritable(); // false $body->write(); // throws BadMethodCallException $body->read(); // throws BadMethodCallException $body->getContents(); // throws BadMethodCallException
请注意,在流式处理中使用超时时,其应用方式略有不同。在流式模式下,超时值覆盖创建底层传输连接、发送HTTP请求、接收HTTP响应头以及任何可能的重定向。特别是,超时值不考虑接收(可能很大的)响应体。
如果您想将流式响应集成到高级API中,那么使用解析为流对象的Promise对象通常不方便。考虑查看react/promise-stream。生成的流式代码可能看起来像这样
use React\Promise\Stream; function download(Browser $browser, string $url): React\Stream\ReadableStreamInterface { return Stream\unwrapReadable( $browser->requestStreaming('GET', $url)->then(function (Psr\Http\Message\ResponseInterface $response) { return $response->getBody(); }) ); } $stream = download($browser, $url); $stream->on('data', function ($data) { echo $data; });
有关更多详细信息,请参阅requestStreaming()
方法。
遗留信息:v2.9.0之前的遗留版本使用了遗留的
streaming
选项。此选项已弃用,但行为上仍然保持一致。
流式请求
除了流式传输响应体外,您还可以流式传输请求体。如果您想发送大的POST请求(上传文件等)或同时处理许多传出流,这可能很有用。您只需将实现ReactPHP的ReadableStreamInterface
的实例传递给请求方法即可,如下所示
$browser->post($url, array(), $stream)->then(function (Psr\Http\Message\ResponseInterface $response) { echo 'Successfully sent.'; });
如果您使用流式请求体(React\Stream\ReadableStreamInterface
),它将默认使用Transfer-Encoding: chunked
,或者您必须显式传递一个匹配的Content-Length
请求头,如下所示
$body = new React\Stream\ThroughStream(); $loop->addTimer(1.0, function () use ($body) { $body->end("hello world"); }); $browser->post($url, array('Content-Length' => '11'), $body);
如果流式请求体发出error
事件或在没有首先发出成功的end
事件的情况下显式关闭,请求将自动关闭并拒绝。
HTTP代理
您还可以通过添加对clue/reactphp-http-proxy的依赖来通过HTTP CONNECT代理服务器建立传出连接。
HTTP CONNECT代理服务器(也常被称为“HTTPS代理”或“SSL代理”)通常用于通过中间代理(“代理”)将HTTPS流量隧道传输,以隐藏原始地址(匿名性)或绕过地址封锁(地理封锁)。虽然许多(公共)HTTP CONNECT代理服务器通常仅限制为HTTPS端口443
,但技术上这可以用于隧道任何基于TCP/IP的协议,例如纯HTTP和TLS加密的HTTPS。
$proxy = new Clue\React\HttpProxy\ProxyConnector( 'http://127.0.0.1:8080', new React\Socket\Connector($loop) ); $connector = new React\Socket\Connector($loop, array( 'tcp' => $proxy, 'dns' => false )); $browser = new Clue\React\Buzz\Browser($loop, $connector);
请参阅HTTP CONNECT代理示例。
SOCKS代理
您还可以通过添加对clue/reactphp-socks的依赖来通过SOCKS代理服务器建立您的出站连接。
SOCKS代理协议族(SOCKS5、SOCKS4和SOCKS4a)通常用于通过中间代理(“代理”)隧道HTTP(S)流量,以隐藏原始地址(匿名性)或绕过地址封锁(地理封锁)。虽然许多(公共)SOCKS代理服务器通常仅限制为HTTP(S)端口80
和443
,但技术上这可以用于隧道任何基于TCP/IP的协议。
$proxy = new Clue\React\Socks\Client( 'socks://127.0.0.1:1080', new React\Socket\Connector($loop) ); $connector = new React\Socket\Connector($loop, array( 'tcp' => $proxy, 'dns' => false )); $browser = new Clue\React\Buzz\Browser($loop, $connector);
请参阅SOCKS代理示例。
SSH代理
您还可以通过添加对clue/reactphp-ssh-proxy的依赖来通过SSH服务器建立您的出站连接。
安全壳(SSH)是一种安全网络协议,最常用于访问远程服务器上的登录外壳。其架构允许它在一个连接上使用多个安全通道。其中之一,这也可以用来创建“SSH隧道”,通常用于通过中间代理(“代理”)隧道HTTP(S)流量,以隐藏原始地址(匿名性)或绕过地址封锁(地理封锁)。这可以用于隧道任何基于TCP/IP的协议(HTTP、SMTP、IMAP等),允许您访问否则无法从外部访问的本地服务(防火墙后面的数据库),因此也可以用于纯HTTP和TLS加密的HTTPS。
$proxy = new Clue\React\SshProxy\SshSocksConnector('me@localhost:22', $loop); $connector = new React\Socket\Connector($loop, array( 'tcp' => $proxy, 'dns' => false )); $browser = new Clue\React\Buzz\Browser($loop, $connector);
请参阅SSH代理示例。
Unix域套接字
默认情况下,此库支持http://
和https://
URL方案分别使用纯文本TCP/IP和安全的TLS连接进行传输。此库还支持在明确配置时使用Unix域套接字(UDS)。
为了使用UDS路径,您必须明确配置连接器以覆盖目标URL,这样请求URL中提供的主机名将不再用于建立连接。
$connector = new React\Socket\FixedUriConnector( 'unix:///var/run/docker.sock', new React\Socket\UnixConnector($loop) ); $browser = new Browser($loop, $connector); $client->get('http://localhost/info')->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders(), (string)$response->getBody()); });
请参阅Unix Domain Sockets (UDS) 示例。
API
浏览器
Clue\React\Buzz\Browser
负责向您的HTTP服务器发送HTTP请求,并跟踪待处理的传入HTTP响应。它还将所有内容注册到主EventLoop
。
$loop = React\EventLoop\Factory::create(); $browser = new Clue\React\Buzz\Browser($loop);
如果您需要自定义连接器设置(DNS解析、TLS参数、超时、代理服务器等),您可以显式传递一个自定义的 ConnectorInterface
实例。
$connector = new React\Socket\Connector($loop, array( 'dns' => '127.0.0.1', 'tcp' => array( 'bindto' => '192.168.10.1:0' ), 'tls' => array( 'verify_peer' => false, 'verify_peer_name' => false ) )); $browser = new Clue\React\Buzz\Browser($loop, $connector);
get()
可以使用 get(string|UriInterface $url, array $headers = array()): PromiseInterface<ResponseInterface>
方法发送 HTTP GET 请求。
$browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump((string)$response->getBody()); });
另请参阅 示例 01。
出于向后兼容性原因,此方法接受
$url
作为string
值或作为UriInterface
。建议显式将实现UriInterface
的任何对象转换为string
。
post()
可以使用 post(string|UriInterface $url, array $headers = array(), string|ReadableStreamInterface $contents = ''): PromiseInterface<ResponseInterface>
方法发送 HTTP POST 请求。
$browser->post( $url, [ 'Content-Type' => 'application/json' ], json_encode($data) )->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump(json_decode((string)$response->getBody())); });
另请参阅 示例 04。
此方法也常用于提交 HTML 表单数据。
$data = [ 'user' => 'Alice', 'password' => 'secret' ]; $browser->post( $url, [ 'Content-Type' => 'application/x-www-form-urlencoded' ], http_build_query($data) );
如果出站请求体是 string
,则此方法将自动添加匹配的 Content-Length
请求头。如果您正在使用流式请求体 (ReadableStreamInterface
),则默认使用 Transfer-Encoding: chunked
,或者您必须显式传递匹配的 Content-Length
请求头,如下所示:
$body = new React\Stream\ThroughStream(); $loop->addTimer(1.0, function () use ($body) { $body->end("hello world"); }); $browser->post($url, array('Content-Length' => '11'), $body);
出于向后兼容性原因,此方法接受
$url
作为string
值或作为UriInterface
。建议显式将实现UriInterface
的任何对象转换为string
。
head()
可以使用 head(string|UriInterface $url, array $headers = array()): PromiseInterface<ResponseInterface>
方法发送 HTTP HEAD 请求。
$browser->head($url)->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders()); });
出于向后兼容性原因,此方法接受
$url
作为string
值或作为UriInterface
。建议显式将实现UriInterface
的任何对象转换为string
。
patch()
可以使用 patch(string|UriInterface $url, array $headers = array(), string|ReadableStreamInterface $contents = ''): PromiseInterface<ResponseInterface>
方法发送 HTTP PATCH 请求。
$browser->patch( $url, [ 'Content-Type' => 'application/json' ], json_encode($data) )->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump(json_decode((string)$response->getBody())); });
如果出站请求体是 string
,则此方法将自动添加匹配的 Content-Length
请求头。如果您正在使用流式请求体 (ReadableStreamInterface
),则默认使用 Transfer-Encoding: chunked
,或者您必须显式传递匹配的 Content-Length
请求头,如下所示:
$body = new React\Stream\ThroughStream(); $loop->addTimer(1.0, function () use ($body) { $body->end("hello world"); }); $browser->patch($url, array('Content-Length' => '11'), $body);
出于向后兼容性原因,此方法接受
$url
作为string
值或作为UriInterface
。建议显式将实现UriInterface
的任何对象转换为string
。
put()
可以使用 put(string|UriInterface $url, array $headers = array()): PromiseInterface<ResponseInterface>
方法发送 HTTP PUT 请求。
$browser->put( $url, [ 'Content-Type' => 'text/xml' ], $xml->asXML() )->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump((string)$response->getBody()); });
另请参阅 示例 05。
如果出站请求体是 string
,则此方法将自动添加匹配的 Content-Length
请求头。如果您正在使用流式请求体 (ReadableStreamInterface
),则默认使用 Transfer-Encoding: chunked
,或者您必须显式传递匹配的 Content-Length
请求头,如下所示:
$body = new React\Stream\ThroughStream(); $loop->addTimer(1.0, function () use ($body) { $body->end("hello world"); }); $browser->put($url, array('Content-Length' => '11'), $body);
出于向后兼容性原因,此方法接受
$url
作为string
值或作为UriInterface
。建议显式将实现UriInterface
的任何对象转换为string
。
delete()
可以使用 delete(string|UriInterface $url, array $headers = array()): PromiseInterface<ResponseInterface>
方法发送 HTTP DELETE 请求。
$browser->delete($url)->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump((string)$response->getBody()); });
出于向后兼容性原因,此方法接受
$url
作为string
值或作为UriInterface
。建议显式将实现UriInterface
的任何对象转换为string
。
request()
可以使用 request(string $method, string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface<ResponseInterface>
方法发送任意 HTTP 请求。
发送 HTTP 请求的首选方法是使用上述 请求方法,例如使用 get()
方法发送 HTTP GET
请求。
作为替代,如果您想使用自定义 HTTP 请求方法,可以使用此方法
$browser->request('OPTIONS', $url)->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump((string)$response->getBody()); });
如果已知的出站请求体大小非空,则此方法将自动添加匹配的 Content-Length
请求头。对于空请求体,如果请求方法通常期望请求体(仅适用于 POST
、PUT
和 PATCH
),则将仅包含 Content-Length: 0
请求头。
如果您正在使用流式请求体 (ReadableStreamInterface
),则默认使用 Transfer-Encoding: chunked
或您必须显式传递匹配的 Content-Length
请求头,如下所示:
$body = new React\Stream\ThroughStream(); $loop->addTimer(1.0, function () use ($body) { $body->end("hello world"); }); $browser->request('POST', $url, array('Content-Length' => '11'), $body);
请注意,此方法从v2.9.0版本开始可用,并在解析前始终缓冲响应体。它不尊重已弃用的
streaming
选项。如果您想流式传输响应体,请使用requestStreaming()
方法。
requestStreaming()
可以使用 requestStreaming(string $method, string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface<ResponseInterface>
方法发送任意HTTP请求并接收流式响应,而不缓冲响应体。
发送HTTP请求的首选方法是使用上述 请求方法,例如使用 get()
方法发送HTTP GET
请求。默认情况下,这些方法会将整个响应体缓存在内存中。这对于入门来说很容易,并且对于较小的响应来说效果相当不错。
在某些情况下,使用流式传输方法会更好,其中只需要在内存中保持少量数据。您可以使用此方法发送任意HTTP请求并接收流式响应。它使用相同的HTTP消息API,但不将响应体缓冲在内存中。它仅在接收到数据时以小块形式处理响应体,并通过 ReactPHP的Stream API 将这些数据传递出去。这对于任意大小(任意数量)的响应都有效。
$browser->requestStreaming('GET', $url)->then(function (Psr\Http\Message\ResponseInterface $response) { $body = $response->getBody(); assert($body instanceof Psr\Http\Message\StreamInterface); assert($body instanceof React\Stream\ReadableStreamInterface); $body->on('data', function ($chunk) { echo $chunk; }); $body->on('error', function (Exception $error) { echo 'Error: ' . $error->getMessage() . PHP_EOL; }); $body->on('close', function () { echo '[DONE]' . PHP_EOL; }); });
有关更多详细信息、示例和可能的用例,请参阅 ReadableStreamInterface
和 流式响应。
如果已知的出站请求体大小非空,则此方法将自动添加匹配的 Content-Length
请求头。对于空请求体,如果请求方法通常期望请求体(仅适用于 POST
、PUT
和 PATCH
),则将仅包含 Content-Length: 0
请求头。
如果您正在使用流式请求体 (ReadableStreamInterface
),则默认使用 Transfer-Encoding: chunked
或您必须显式传递匹配的 Content-Length
请求头,如下所示:
$body = new React\Stream\ThroughStream(); $loop->addTimer(1.0, function () use ($body) { $body->end("hello world"); }); $browser->requestStreaming('POST', $url, array('Content-Length' => '11'), $body);
请注意,此方法从v2.9.0版本开始可用,并且始终在不缓冲响应体的情况下解析响应。它不尊重已弃用的
streaming
选项。如果您想缓冲响应体,请使用request()
方法。
submit()
自v2.9.0版本以来已弃用,请改用
post()
。
可以使用已弃用的 submit(string|UriInterface $url, array $fields, array $headers = array(), string $method = 'POST'): PromiseInterface<ResponseInterface>
方法提交与表单提交类似的一组字段值(application/x-www-form-urlencoded
)。
// deprecated: see post() instead $browser->submit($url, array('user' => 'test', 'password' => 'secret'));
出于向后兼容性原因,此方法接受
$url
作为string
值或作为UriInterface
。建议显式将实现UriInterface
的任何对象转换为string
。
send()
自v2.9.0版本以来已弃用,请改用
request()
。
可以使用已弃用的 send(RequestInterface $request): PromiseInterface<ResponseInterface>
方法发送实现 RequestInterface
(PSR-7)的任意实例。
发送 HTTP 请求的首选方法是使用上述 请求方法,例如使用 get()
方法发送 HTTP GET
请求。
作为替代,如果您想使用自定义 HTTP 请求方法,可以使用此方法
$request = new Request('OPTIONS', $url); // deprecated: see request() instead $browser->send($request)->then(…);
如果已知的出站请求体大小非空,则此方法将自动添加匹配的 Content-Length
请求头。对于空请求体,如果请求方法通常期望请求体(仅适用于 POST
、PUT
和 PATCH
),则将仅包含 Content-Length: 0
请求头。
withTimeout()
可以使用 withTimeout(bool|number $timeout): Browser
方法更改用于等待挂起请求的最大超时时间。
您可以将要使用的秒数作为新的超时值传入
$browser = $browser->withTimeout(10.0);
您可以将布尔值 false
传入以禁用任何超时。在这种情况下,请求可以无限期地挂起
$browser = $browser->withTimeout(false);
您可以将布尔值 true
传入以重新启用默认超时处理。这将尊重PHP的 default_socket_timeout
设置(默认60秒)
$browser = $browser->withTimeout(true);
有关超时处理的更多详细信息,请参阅 超时。
请注意,Browser
是一个不可变对象,即此方法实际上返回了一个具有应用指定超时值的新Browser
实例。
withFollowRedirects()
使用withTimeout(bool|int $$followRedirects): Browser
方法可以更改HTTP重定向的跟随方式。
您可以传递要跟随的最大重定向数
$new = $browser->withFollowRedirects(5);
当超过重定向数时,请求将自动被拒绝。您可以传递一个0
来拒绝遇到任何重定向的请求
$browser = $browser->withFollowRedirects(0); $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { // only non-redirected responses will now end up here var_dump($response->getHeaders()); });
您可以传递一个布尔值false
来禁用跟随任何重定向。在这种情况下,请求将使用重定向响应而不是跟随Location
响应头
$browser = $browser->withFollowRedirects(false); $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { // any redirects will now end up here var_dump($response->getHeaderLine('Location')); });
您可以传递一个布尔值true
来重新启用默认的重定向处理。默认情况下,最多跟随10个重定向
$browser = $browser->withFollowRedirects(true);
有关重定向处理的更多详细信息,请参阅重定向。
请注意,Browser
是一个不可变对象,即此方法实际上返回了一个应用了给定重定向设置的新Browser
实例。
withRejectErrorResponse()
使用withRejectErrorResponse(bool $obeySuccessCode): Browser
方法可以更改是否拒绝非成功的HTTP响应状态代码(4xx和5xx)。
您可以传递一个布尔值false
来禁用拒绝使用4xx或5xx响应状态代码的传入响应。在这种情况下,请求将使用指示错误条件的响应消息解决
$browser = $browser->withRejectErrorResponse(false); $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { // any HTTP response will now end up here var_dump($response->getStatusCode(), $response->getReasonPhrase()); });
您可以传递一个布尔值true
来重新启用默认的状态代码处理。默认情况下,拒绝任何在4xx或5xx范围内的响应状态代码,并抛出一个ResponseException
$browser = $browser->withRejectErrorResponse(true); $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { // any successful HTTP response will now end up here var_dump($response->getStatusCode(), $response->getReasonPhrase()); }, function (Exception $e) { if ($e instanceof Clue\React\Buzz\Message\ResponseException) { // any HTTP response error message will now end up here $response = $e->getResponse(); var_dump($response->getStatusCode(), $response->getReasonPhrase()); } else { var_dump($e->getMessage()); } });
请注意,Browser
是一个不可变对象,即此方法实际上返回了一个应用了给定设置的新Browser
实例。
withBase()
使用withBase(string|null|UriInterface $baseUrl): Browser
方法可以更改用于解析相对URL的基本URL。
如果您配置了基本URL,任何请求相对URL的请求都将首先使用此绝对基本URL进行处理。请注意,这仅预置基本URL,并不会解析任何相对路径引用(如../
等)。这对于所有端点(URL)都位于公共基本URL下的(RESTful)API调用非常有用。
$browser = $browser->withBase('http://api.example.com/v3'); // will request http://api.example.com/v3/example $browser->get('/example')->then(…);
您可以通过传递一个null
基本URL来返回一个不使用基本URL的新实例
$browser = $browser->withBase(null);
因此,任何使用相对URL请求不使用基本URL的浏览器的请求都无法完成,并且将在不发送请求的情况下被拒绝。
如果给定的$baseUrl
参数不是一个有效的URL,此方法将抛出一个InvalidArgumentException
。
请注意,Browser
是一个不可变对象,即withBase()
方法实际上返回了一个应用了给定基本URL的新Browser
实例。
出于BC的原因,此方法接受
$baseUrl
作为字符串值或UriInterface
。建议显式地将实现UriInterface
的对象转换为字符串。
更新日志:从v2.9.0版本开始,此方法接受一个
null
值以重置基本URL。早期版本必须使用已弃用的withoutBase()
方法来重置基本URL。
withProtocolVersion()
withProtocolVersion(string $protocolVersion): Browser
方法可用于更改所有后续请求将使用的HTTP协议版本。
上述所有请求方法默认以HTTP/1.1发送请求。这是首选的HTTP协议版本,它还提供了与旧版HTTP/1.0服务器的良好向后兼容性。因此,通常无需显式更改此协议版本。
如果您想显式使用旧版HTTP/1.0协议版本,可以使用此方法
$newBrowser = $browser->withProtocolVersion('1.0'); $newBrowser->get($url)->then(…);
请注意,Browser
是一个不可变对象,即此方法实际上返回一个应用了新协议版本的新Browser
实例。
withResponseBuffer()
withRespomseBuffer(int $maximumSize): Browser
方法可用于更改响应体缓冲区的最大大小。
发送HTTP请求的首选方法是使用上述 请求方法,例如使用 get()
方法发送HTTP GET
请求。默认情况下,这些方法会将整个响应体缓存在内存中。这对于入门来说很容易,并且对于较小的响应来说效果相当不错。
默认情况下,响应体缓冲区的大小限制为16 MiB。如果响应体超过此最大大小,则请求将被拒绝。
您可以传入要缓冲的最大字节数
$browser = $browser->withResponseBuffer(1024 * 1024); $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { // response body will not exceed 1 MiB var_dump($response->getHeaders(), (string) $response->getBody()); });
请注意,响应体缓冲区必须为每个挂起的请求保留在内存中,直到其传输完成,它将在挂起的请求得到满足后才会被释放。因此,通常不推荐增加此最大缓冲区大小以允许更大的响应体。相反,您可以使用requestStreaming()
方法接收任意大小的响应,而不进行缓冲。因此,此最大缓冲区设置对流式响应没有影响。
请注意,Browser
是一个不可变对象,即此方法实际上返回了一个应用了给定设置的新Browser
实例。
withOptions()
自v2.9.0版本起已弃用,请参阅
withTimeout()
、withFollowRedirects()
和withRejectErrorResponse()
。
已弃用的withOptions(array $options): Browser
方法可用于更改要使用的选项
Browser
类公开了用于处理HTTP事务的几个选项。这些选项类似于PHP的一些HTTP上下文选项,可以通过以下API(及其默认值)进行控制
// deprecated $newBrowser = $browser->withOptions(array( 'timeout' => null, // see withTimeout() instead 'followRedirects' => true, // see withFollowRedirects() instead 'maxRedirects' => 10, // see withFollowRedirects() instead 'obeySuccessCode' => true, // see withRejectErrorResponse() instead 'streaming' => false, // deprecated, see requestStreaming() instead ));
请注意,Browser
是一个不可变对象,即此方法实际上返回一个应用了选项的新Browser
实例。
withoutBase()
自v2.9.0版本起已弃用,请参阅
withBase()
。
已弃用的 withoutBase(): Browser
方法可以用来移除基本 URL。
// deprecated: see withBase() instead $newBrowser = $browser->withoutBase();
注意,Browser
是一个不可变对象,也就是说,withoutBase()
方法实际上返回了一个没有应用任何基本 URL 的新 Browser
实例。
另请参阅 withBase()
。
ResponseInterface
Psr\Http\Message\ResponseInterface
表示从 Browser
接收到的响应。
这是一个在 PSR-7: HTTP 消息接口 中定义的标准接口,请参阅其 ResponseInterface
定义,该定义又扩展了 MessageInterface
定义。
RequestInterface
Psr\Http\Message\RequestInterface
表示将通过 Browser
发送的出站请求。
这是一个在 PSR-7: HTTP 消息接口 中定义的标准接口,请参阅其 RequestInterface
定义,该定义又扩展了 MessageInterface
定义。
UriInterface
Psr\Http\Message\UriInterface
表示一个绝对或相对 URI(即 URL)。
这是一个在 PSR-7: HTTP 消息接口 中定义的标准接口,请参阅其 UriInterface
定义。
出于向后兼容性原因,请求方法接受 URL 作为
string
值或作为UriInterface
。建议显式地将实现UriInterface
的任何对象转换为string
。
ResponseException
ResponseException
是一个 Exception
子类,将在远程服务器返回非成功状态码(不是 2xx 或 3xx)时拒绝请求承诺。您可以通过 withRejectErrorResponse()
方法 控制此行为。
可以使用 getCode(): int
方法来返回 HTTP 响应状态码。
可以使用 getResponse(): ResponseInterface
方法来访问其底层的 ResponseInterface
对象。
安装
推荐通过 Composer 安装此库。对 Composer 陌生?新用户?
该项目遵循 SemVer。这将安装最新支持的版本。
$ composer require clue/buzz-react:^2.9
有关版本升级的详细信息,请参阅 CHANGELOG。
本项目的目标是能够在任何平台上运行,因此不需要任何PHP扩展,并且支持在旧版PHP 5.3到当前PHP 7+和HHVM上运行。强烈推荐使用PHP 7+。
测试
要运行测试套件,首先需要克隆此仓库,然后通过Composer安装所有依赖项(Composer)
$ composer install
要运行测试套件,请进入项目根目录并运行
$ php vendor/bin/phpunit
测试套件还包含一些功能集成测试,这些测试会向在线服务http://httpbin.org发送测试HTTP请求,因此依赖于稳定的网络连接。如果您不想运行这些测试,可以像这样简单地跳过
$ php vendor/bin/phpunit --exclude-group online
许可证
本项目采用宽松的MIT许可证发布。
你知道吗?我提供定制开发服务,并为发行版赞助和贡献发放发票。如需详细信息,请联系我(@clue)。