Guzzle是一个PHP的HTTP客户端,用来轻而易举地发送请求,并集成到我们的WEB服务上。
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/user', [
'auth' => ['user', 'pass']
]);
echo $res->getStatusCode();
// "200"
echo $res->getHeader('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
// 发送一个异步请求
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$promise = $client->sendAsync($request)->then(function ($response) {
echo 'I completed! ' . $response->getBody();
});
$promise->wait();
allow_url_fopen
必须在php.ini中启用。注解
如果没有安装cURL,Guzzle处理HTTP请求的时候不再使用cURL,而是使用PHP流处理,或者你也可以提供自己的发送HTTP请求的处理方式。
推荐使用 Composer 安装Guzzle,Composer是PHP的依赖管理工具,允许你在项目中声明依赖关系,并安装这些依赖。
# 安装 Composer
curl -sS https://getcomposer.org/installer | php
你可以使用composer.phar客户端将Guzzle作为依赖添加到项目:
php composer.phar require guzzlehttp/guzzle:~6.0
或者,你可以编辑项目中已存在的composer.json文件,添加Guzzle作为依赖:
{
"require": {
"guzzlehttp/guzzle": "~6.0"
}
}
安装完毕后,你需要引入Composer的自动加载文件:
require 'vendor/autoload.php';
你可以在 getcomposer.org 发现更多关于怎样安装Composer、配置自动加载以及其他有用的东西。
开发期间,你也可以安装master分支下的最新内容,只需要将Guzzle版本设置成 ~6.0@dev
:
{
"require": {
"guzzlehttp/guzzle": "~6.0@dev"
}
}
Licensed using the MIT license.
Copyright (c) 2015 Michael Dowling <https://github.com/mtdowling>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
In order to contribute, you'll need to checkout the source from GitHub and install Guzzle's dependencies using Composer:
git clone https://github.com/guzzle/guzzle.git
cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev
Guzzle is unit tested with PHPUnit. Run the tests using the Makefile:
make test
注解
You'll need to install node.js v0.5.0 or newer in order to perform integration tests on Guzzle's HTTP handlers.
We want to ensure that Guzzle is a secure HTTP client library for everyone. If you've discovered a security vulnerability in Guzzle, we appreciate your help in disclosing it to us in a responsible manner.
Publicly disclosing a vulnerability can put the entire community at risk. If you've discovered a security concern, please email us at security@guzzlephp.org. We'll work with you to make sure that we understand the scope of the issue, and that we fully address your concern. We consider correspondence sent to security@guzzlephp.org our highest priority, and work to address any issues that arise as quickly as possible.
After a security vulnerability has been corrected, a security hotfix release will be deployed as soon as possible.
该页面提供了Guzzle的快速入门以及列子,如果你还没有安装Guzzle请前往 安装 页面。
你可以使用Guzzle的 GuzzleHttp\ClientInterface
对象来发送请求。
use GuzzleHttp\Client;
$client = new Client([
// Base URI is used with relative requests
'base_uri' => 'http://httpbin.org',
// You can set any number of default request options.
'timeout' => 2.0,
]);
Client对象可以接收一个包含参数的数组:
base_uri
(string|UriInterface) 基URI用来合并到相关URI,可以是一个字符串或者UriInterface的实例,当提供了相关uri,将合并到基URI,遵循的规则请参考 RFC 3986, section 2 章节。
// Create a client with a base URI
$client = new GuzzleHttp\Client(['base_uri' => 'https://foo.com/api/']);
// Send a request to https://foo.com/api/test
$response = $client->request('GET', 'test');
// Send a request to https://foo.com/root
$response = $client->request('GET', '/root');
不想阅读RFC 3986?这里有一些关于
base_uri
与其他URI处理器的快速例子:
base_uri | URI | Result |
---|---|---|
http://foo.com |
/bar |
http://foo.com/bar |
http://foo.com/foo |
/bar |
http://foo.com/bar |
http://foo.com/foo |
bar |
http://foo.com/bar |
http://foo.com/foo/ |
bar |
http://foo.com/foo/bar |
http://foo.com |
http://baz.com |
http://baz.com |
http://foo.com/?bar |
bar |
http://foo.com/bar |
handler
Psr7\Http\Message\RequestInterface
以及参数数组,必须返回
GuzzleHttp\Promise\PromiseInterface
,成功时满足
Psr7\Http\Message\ResponseInterface
。 handler
是一个构造方法,不能在请求参数里被重写。...
Client对象的方法可以很容易的发送请求:
$response = $client->get('http://httpbin.org/get');
$response = $client->delete('http://httpbin.org/delete');
$response = $client->head('http://httpbin.org/get');
$response = $client->options('http://httpbin.org/get');
$response = $client->patch('http://httpbin.org/patch');
$response = $client->post('http://httpbin.org/post');
$response = $client->put('http://httpbin.org/put');
你可以创建一个请求,一切就绪后将请求传送给client:
use GuzzleHttp\Psr7\Request;
$request = new Request('PUT', 'http://httpbin.org/put');
$response = $client->send($request, ['timeout' => 2]);
Client对象为传输请求提供了非常灵活的处理器方式,包括请求参数、每次请求使用的中间件以及传送多个相关请求的基URI。
你可以在 Handlers and Middleware 页面找到更多关于中间件的内容。
你可以使用Client提供的方法来创建异步请求:
$promise = $client->getAsync('http://httpbin.org/get');
$promise = $client->deleteAsync('http://httpbin.org/delete');
$promise = $client->headAsync('http://httpbin.org/get');
$promise = $client->optionsAsync('http://httpbin.org/get');
$promise = $client->patchAsync('http://httpbin.org/patch');
$promise = $client->postAsync('http://httpbin.org/post');
$promise = $client->putAsync('http://httpbin.org/put');
你也可以使用Client的 sendAsync() and requestAsync() 方法:
use GuzzleHttp\Psr7\Request;
// Create a PSR-7 request object to send
$headers = ['X-Foo' => 'Bar'];
$body = 'Hello!';
$request = new Request('HEAD', 'http://httpbin.org/head', $headers, $body);
// Or, if you don't need to pass in a request instance:
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
这些方法返回了Promise对象,该对象实现了由
Guzzle promises library 提供的
Promises/A+ spec ,这意味着你可以使用 then()
来调用返回值,成功使用 Psr\Http\Message\ResponseInterface
处理器,否则抛出一个异常。
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\RequestException;
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(
function (ResponseInterface $res) {
echo $res->getStatusCode() . "\n";
},
function (RequestException $e) {
echo $e->getMessage() . "\n";
echo $e->getRequest()->getMethod();
}
);
你可以使用Promise和异步请求来同时发送多个请求:
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
$client = new Client(['base_uri' => 'http://httpbin.org/']);
// Initiate each request but do not block
$promises = [
'image' => $client->getAsync('/image'),
'png' => $client->getAsync('/image/png'),
'jpeg' => $client->getAsync('/image/jpeg'),
'webp' => $client->getAsync('/image/webp')
];
// Wait on all of the requests to complete.
$results = Promise\unwrap($promises);
// You can access each result using the key provided to the unwrap
// function.
echo $results['image']->getHeader('Content-Length');
echo $results['png']->getHeader('Content-Length');
当你想发送不确定数量的请求时,可以使用 GuzzleHttp\Pool
对象:
use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
$client = new Client();
$requests = function ($total) {
$uri = 'http://127.0.0.1:8126/guzzle-server/perf';
for ($i = 0; $i < $total; $i++) {
yield new Request('GET', $uri);
}
};
$pool = new Pool($client, $requests(100), [
'concurrency' => 5,
'fulfilled' => function ($response, $index) {
// this is delivered each successful response
},
'rejected' => function ($reason, $index) {
// this is delivered each failed request
},
]);
// Initiate the transfers and create a promise
$promise = $pool->promise();
// Force the pool of requests to complete.
$promise->wait();
前面的例子里,我们取到了 $response
变量,或者从Promise得到了响应,Response对象实现了一个PSR-7接口 Psr\Http\Message\ResponseInterface
, 包含了很多有用的信息。
你可以获取这个响应的状态码和和原因短语(reason phrase):
$code = $response->getStatusCode(); // 200
$reason = $response->getReasonPhrase(); // OK
你可以从响应获取头信息(header):
// Check if a header exists.
if ($response->hasHeader('Content-Length')) {
echo "It exists";
}
// Get a header from the response.
echo $response->getHeader('Content-Length');
// Get all of the response headers.
foreach ($response->getHeaders() as $name => $values) {
echo $name . ': ' . implode(', ', $values) . "\r\n";
}
使用 getBody
方法可以获取响应的主体部分(body),主体可以当成一个字符串或流对象使用
$body = $response->getBody();
// Implicitly cast the body to a string and echo it
echo $body;
// Explicitly cast the body to a string
$stringBody = (string) $body;
// Read 10 bytes from the body
$tenBytes = $body->read(10);
// Read the remaining contents of the body as a string
$remainingBytes = $body->getContents();
你可以有多种方式来提供请求的查询字符串 你可以在请求的URI中设置查询字符串:
$response = $client->request('GET', 'http://httpbin.org?foo=bar');
你可以使用 query
请求参数来声明查询字符串参数:
$client->request('GET', 'http://httpbin.org', [
'query' => ['foo' => 'bar']
]);
提供的数组参数将会使用PHP的 http_build_query
:
最后,你可以提供一个字符串作为 query
请求参数:
$client->request('GET', 'http://httpbin.org', ['query' => 'foo=bar']);
Guzzle为上传数据提供了一些方法。
你可以发送一个包含数据流的请求,将 body
请求参数设置成一个字符串、 fopen
返回的资源、或者一个
Psr\Http\Message\StreamInterface
的实例。
// Provide the body as a string.
$r = $client->request('POST', 'http://httpbin.org/post', [
'body' => 'raw data'
]);
// Provide an fopen resource.
$body = fopen('/path/to/file', 'r');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);
// Use the stream_for() function to create a PSR-7 stream.
$body = \GuzzleHttp\Psr7\stream_for('hello!');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);
上传JSON数据以及设置合适的头信息可以使用
json
请求参数这个简单的方式:
$r = $client->request('PUT', 'http://httpbin.org/put', [
'json' => ['foo' => 'bar']
]);
除了使用 body
参数来指定请求数据外,Guzzle为发送POST数据提供了有用的方法。
发送 application/x-www-form-urlencoded
POST请求需要你传入 form_params
数组参数,数组内指定POST的字段。
$response = $client->request('POST', 'http://httpbin.org/post', [
'form_params' => [
'field_name' => 'abc',
'other_field' => '123',
'nested_field' => [
'nested' => 'hello'
]
]
]);
你可以通过使用 multipart
请求参数来发送表单(表单enctype属性需要设置 multipart/form-data
)文件,
该参数接收一个包含多个关联数组的数组,每个关联数组包含一下键名:
fopen
返回的资源、或者一个Psr\Http\Message\StreamInterface
的实例。
$response = $client->request('POST', 'http://httpbin.org/post', [
'multipart' => [
[
'name' => 'field_name',
'contents' => 'abc'
],
[
'name' => 'file_name',
'contents' => fopen('/path/to/file', 'r')
],
[
'name' => 'other_file',
'contents' => 'hello',
'filename' => 'filename.txt',
'headers' => [
'X-Foo' => 'this is an extra header to include'
]
]
]
]);
Guzzle可以使用 cookies
请求参数为你维护一个cookie会话,当发送一个请求时, cookies
选项必须设置成 GuzzleHttp\Cookie\CookieJarInterface
的实例。
// Use a specific cookie jar
$jar = new \GuzzleHttp\Cookie\CookieJar;
$r = $client->request('GET', 'http://httpbin.org/cookies', [
'cookies' => $jar
]);
You can set cookies
to true
in a client constructor if you would like
to use a shared cookie jar for all requests.
// Use a shared client cookie jar
$client = new \GuzzleHttp\Client(['cookies' => true]);
$r = $client->request('GET', 'http://httpbin.org/cookies');
如果你没有告诉Guzzle不要重定向,Guzzle会自动的进行重定向,你可以使用 allow_redirects
请求参数来自定义重定向行为。
true
时将启用最大数量为5的重定向,这是默认设置。false
来禁用重定向。max
键名的关联数组来声明最大重定向次数,提供可选的 strict
键名来声明是否使用严格的RFC标准重定向
(表示使用POST请求重定向POST请求 vs 大部分浏览器使用GET请求重定向POST请求)。$response = $client->request('GET', 'http://github.com');
echo $response->getStatusCode();
// 200
下面的列子表示重定向被禁止:
$response = $client->request('GET', 'http://github.com', [
'allow_redirects' => false
]);
echo $response->getStatusCode();
// 301
请求传输过程中出现的错误Guzzle将会抛出异常。
在发送网络错误(连接超时、DNS错误等)时,将会抛出 GuzzleHttp\Exception\RequestException
异常。
该异常继承自 GuzzleHttp\Exception\TransferException
,捕获这个异常可以在传输请求过程中抛出异常。
use GuzzleHttp\Exception\RequestException;
try {
$client->request('GET', 'https://github.com/_abc_123_404');
} catch (RequestException $e) {
echo $e->getRequest();
if ($e->hasResponse()) {
echo $e->getResponse();
}
}
GuzzleHttp\Exception\ConnectException
异常发生在网络错误时,
该异常继承自 GuzzleHttp\Exception\RequestException
。
如果 http_errors
请求参数设置成true,在400级别的错误的时候将会抛出 GuzzleHttp\Exception\ClientException
异常,
该异常继承自 GuzzleHttp\Exception\BadResponseException
GuzzleHttp\Exception\BadResponseException
继承自 GuzzleHttp\Exception\RequestException
。
use GuzzleHttp\Exception\ClientException;
try {
$client->request('GET', 'https://github.com/_abc_123_404');
} catch (ClientException $e) {
echo $e->getRequest();
echo $e->getResponse();
}
如果 http_errors
请求参数设置成true,在500级别的错误的时候将会抛出 GuzzleHttp\Exception\ServerException
异常。
该异常继承自 GuzzleHttp\Exception\BadResponseException
。
GuzzleHttp\Exception\TooManyRedirectsException
异常发生在重定向次数过多时,
该异常继承自 GuzzleHttp\Exception\RequestException
。
上述所有异常均继承自
GuzzleHttp\Exception\TransferException
。
Guzzle提供了一些可自定义的环境变量:
GUZZLE_CURL_SELECT_TIMEOUT
curl_multi_select()
控制了 curl_multi_* 需要使用到的持续时间,
有些系统实现PHP的 curl_multi_select()
存在问题,调用该函数时总是等待超时的最大值。HTTP_PROXY
HTTPS_PROXY
Guzzle配置客户端时可以利用PHP的ini配置。
openssl.cafile
你可以通过设置Client的 请求选项 来自定义请求,请求参数控制请求的各个方面,包括头信息、查询字符串参数、超时、请求主体等。
下述所有的列子都使用下面的Client:
$client = new GuzzleHttp\Client(['base_uri' => 'http://httpbin.org']);
描述请求的重定向行为
[
'max' => 5,
'strict' => false,
'referer' => true,
'protocols' => ['http', 'https'],
'track_redirects' => false
]
GuzzleHttp\RequestOptions::ALLOW_REDIRECTS
设置成 false
禁用重定向。
$res = $client->request('GET', '/redirect/3', ['allow_redirects' => false]);
echo $res->getStatusCode();
// 302
设置成 true
(默认设置) 来启用默认最大次数为5的重定向。
$res = $client->request('GET', '/redirect/3');
echo $res->getStatusCode();
// 200
你也可以传送一个包含了以下键值对的关联数组:
true
时,每个重定向的URI将会按序被跟踪头信息 X-Guzzle-Redirect-History
。use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
$onRedirect = function(
RequestInterface $request,
ResponseInterface $response,
UriInterface $uri
) {
echo 'Redirecting! ' . $request->getUri() . ' to ' . $uri . "\n";
};
$res = $client->request('GET', '/redirect/3', [
'allow_redirects' => [
'max' => 10, // allow at most 10 redirects.
'strict' => true, // use "strict" RFC compliant redirects.
'referer' => true, // add a Referer header
'protocols' => ['https'], // only allow https URLs
'on_redirect' => $onRedirect,
'track_redirects' => true
]
]);
echo $res->getStatusCode();
// 200
echo $res->getHeaderLine('X-Guzzle-Redirect-History');
// http://first-redirect, http://second-redirect, etc...
传入HTTP认证参数的数组来使用请求,该数组索引[0]为用户名、索引[1]为密码,索引[2]为可选的内置认证类型。传入 null
进入请求认证。
None
GuzzleHttp\RequestOptions::AUTH
内置认证类型如下:
Authorization
头信息使用 HTTP基础认证
(如果没有指定的话为默认设置)。$client->request('GET', '/get', ['auth' => ['username', 'password']]);
$client->request('GET', '/get', [
'auth' => ['username', 'password', 'digest']
]);
body
选项用来控制一个请求(比如:PUT, POST, PATCH)的主体部分。
fopen()
resourcePsr\Http\Message\StreamInterface
None
GuzzleHttp\RequestOptions::BODY
可以设置成下述类型:
string
// You can send requests that use a string as the message body.
$client->request('PUT', '/put', ['body' => 'foo']);
resource returned from fopen()
// You can send requests that use a stream resource as the body.
$resource = fopen('http://httpbin.org', 'r');
$client->request('PUT', '/put', ['body' => $resource]);
Psr\Http\Message\StreamInterface
// You can send requests that use a Guzzle stream object as the body
$stream = GuzzleHttp\Psr7\stream_for('contents...');
$client->request('POST', '/post', ['body' => $stream]);
设置成指定PEM格式认证文件的路径的字符串,如果需要密码,需要设置成一个数组,其中PEM文件在第一个元素,密码在第二个元素。
None
GuzzleHttp\RequestOptions::CERT
$client->request('GET', '/', ['cert' => ['/path/server.pem', 'password']]);
GuzzleHttp\Cookie\CookieJarInterface
GuzzleHttp\RequestOptions::COOKIES
你必须声明cookie选项为
GuzzleHttp\Cookie\CookieJarInterface
或 false
。
$jar = new \GuzzleHttp\Cookie\CookieJar();
$client->request('GET', '/get', ['cookies' => $jar]);
0
将无限等待 (默认行为).0
GuzzleHttp\RequestOptions::CONNECT_TIMEOUT
// Timeout if the client fails to connect to the server in 3.14 seconds.
$client->request('GET', '/delay/5', ['connect_timeout' => 3.14]);
设置成 true
或设置成一个 fopen()
返回的流来启用调试输出发送请求的处理器,
比如,当使用cURL传输请求,cURL的 CURLOPT_VERBOSE
的冗长将会发出,
当使用PHP流,流处理的提示将会发生。
如果设置为true,输出到PHP标准输出文件,如果提供了PHP流,将会输出到流。
fopen()
resourceNone
GuzzleHttp\RequestOptions::DEBUG
$client->request('GET', '/get', ['debug' => true]);
执行上面的例子将会输出类似下面的结果:
* About to connect() to httpbin.org port 80 (#0)
* Trying 107.21.213.98... * Connected to httpbin.org (107.21.213.98) port 80 (#0)
> GET /get HTTP/1.1
Host: httpbin.org
User-Agent: Guzzle/4.0 curl/7.21.4 PHP/5.5.7
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Content-Type: application/json
< Date: Sun, 16 Feb 2014 06:50:09 GMT
< Server: gunicorn/0.17.4
< Content-Length: 335
< Connection: keep-alive
<
* Connection #0 to host httpbin.org left intact
声明是否自动解码 Content-Encoding
响应 (gzip,
deflate等) 。
true
GuzzleHttp\RequestOptions::DECODE_CONTENT
该选项可以用来控制Content-Encoding如何响应主体的,默认 decode_content
设置为 true,表示Guzzle将自动解码gzip,deflate等响应。
当设置成 false
,响应的主体将不会被解码,意味着字节将毫无变化的通过处理器。
// Request gzipped data, but do not decode it while downloading
$client->request('GET', '/foo.js', [
'headers' => ['Accept-Encoding' => 'gzip'],
'decode_content' => false
]);
当设置成字符串,响应的字节将被解码,提供 decode_content
选项的字符串将作为请求的 Accept-Encoding
报文头。
// Pass "gzip" as the Accept-Encoding header.
$client->request('GET', '/foo.js', ['decode_content' => 'gzip']);
发送请求前延迟的毫秒数值
null
GuzzleHttp\RequestOptions::DELAY
控制"Expect: 100-Continue"报文头的行为。
1048576
GuzzleHttp\RequestOptions::EXPECT
设置成 true
来为所有发送主体的请求启用 "Expect: 100-Continue" 报文头;
设置成 false
来为所有的请求禁用 "Expect: 100-Continue" 报文头;
设置成一个数值,有效载荷的大小必须大于预计发送的值,设置成数值将会为所有不确定有效载荷大小或主体不能确定指针位置的请求发送Expect报文头,
默认情况下,当请求的主体大于1MB以及请求使用的HTTP/1.1,Guzzle将会添加 "Expect: 100-Continue" 报文头。
GuzzleHttp\RequestOptions::FORM_PARAMS
关联数组由表单字段键值对构成,每个字段值可以是一个字符串或一个包含字符串元素的数组。 当没有准备 "Content-Type" 报文头的时候,将设置为 "application/x-www-form-urlencoded"。
$client->request('POST', '/post', [
'form_params' => [
'foo' => 'bar',
'baz' => ['hi', 'there!']
]
]);
GuzzleHttp\RequestOptions::HEADERS
// Set various headers on a request
$client->request('GET', '/get', [
'headers' => [
'User-Agent' => 'testing/1.0',
'Accept' => 'application/json',
'X-Foo' => ['Bar', 'Baz']
]
]);
创建Client的时候头信息可以作为默认选项添加,当头信息使用默认选项时,它们只能在请求没有包含特殊头信息的时候生效,
这包括了Client的 send()
与 sendAsync()
方法,以及Client创建的请求(比如 request()
与 requestAsync()
)。
$client = new GuzzleHttp\Client(['headers' => ['X-Foo' => 'Bar']]);
// Will send a request with the X-Foo header.
$client->request('GET', '/get');
// Sets the X-Foo header to "test", which prevents the default header
// from being applied.
$client->request('GET', '/get', ['headers' => ['X-Foo' => 'test']);
// Will disable adding in default headers.
$client->request('GET', '/get', ['headers' => null]);
// Will not overwrite the X-Foo header because it is in the message.
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'http://foo.com', ['X-Foo' => 'test']);
$client->send($request);
// Will overwrite the X-Foo header with the request option provided in the
// send method.
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'http://foo.com', ['X-Foo' => 'test']);
$client->send($request, ['headers' => ['X-Foo' => 'overwrite']]);
false
来禁用HTTP协议抛出的异常(如 4xx 和 5xx 响应),默认情况下HTPP协议出错时会抛出异常。true
GuzzleHttp\RequestOptions::HTTP_ERRORS
$client->request('GET', '/status/500');
// Throws a GuzzleHttp\Exception\ServerException
$res = $client->request('GET', '/status/500', ['http_errors' => false]);
echo $res->getStatusCode();
// 500
json
选项用来轻松将JSON数据当成主体上传, 如果没有设置Content-Type头信息的时候会设置成 application/json
。json_encode()
操作的PHP类型。GuzzleHttp\RequestOptions::JSON
$response = $client->request('PUT', '/put', ['json' => ['foo' => 'bar']]);
这里的例子使用了 tap
中间件用来查看发送了什么请求。
use GuzzleHttp\Middleware;
// Grab the client's handler instance.
$clientHandler = $client->getConfig('handler');
// Create a middleware that echoes parts of the request.
$tapMiddleware = Middleware::tap(function ($request) {
echo $request->getHeader('Content-Type');
// application/json
echo $request->getBody();
// {"foo":"bar"}
});
$response = $client->request('PUT', '/put', [
'json' => ['foo' => 'bar'],
'handler' => $tapMiddleware($clientHandler)
]);
GuzzleHttp\RequestOptions::MULTIPART
multipart
的值是一个关联数组,每个元素包含以下键值对:
name
: (string, required) 表单字段名称contents
: (StreamInterface/resource/string, required) 表单元素中要使用的数据headers
: (array) 可选的表单元素要使用的键值对数组filename
: (string) 可选的作为要发送的文件名称$client->request('POST', '/post', [
'multipart' => [
[
'name' => 'foo',
'contents' => 'data',
'headers' => ['X-Baz' => 'bar']
],
[
'name' => 'baz',
'contents' => fopen('/path/to/file', 'r')
],
[
'name' => 'qux',
'contents' => fopen('/path/to/file', 'r'),
'filename' => 'custom_filename.txt'
],
]
]);
回调函数,当响应的HTTP头信息被接收且主体部分还未开始下载的时候调用。
GuzzleHttp\RequestOptions::ON_HEADERS
该回调接收 Psr\Http\ResponseInterface
对象。
如果该回调抛出异常,与响应相关的Promise将会接收到 GuzzleHttp\Exception\RequestException
抛出的异常。
在数据写入下游之前,你应该需要知道接收到的头信息与状态码。
// Reject responses that are greater than 1024 bytes.
$client->request('GET', 'http://httpbin.org/stream/1024', [
'on_headers' => function (ResponseInterface $response) {
if ($response->getHeaderLine('Content-Length') > 1024) {
throw new \Exception('The file is too big!');
}
}
]);
on_stats
允许你获取请求传输数据统计以及处理器在底层传输的详情.
on_stats
是个回调,当处理器完成传输一个请求的时候被调用。
该回调被调用请求传输数据统计、接收到响应,或遇到错误,包含发送请求数据时间的总量。
GuzzleHttp\RequestOptions::ON_STATS
该回调接收 GuzzleHttp\TransferStats
对象。
use GuzzleHttp\TransferStats;
$client = new GuzzleHttp\Client();
$client->request('GET', 'http://httpbin.org/stream/1024', [
'on_stats' => function (TransferStats $stats) {
echo $stats->getEffectiveUri() . "\n";
echo $stats->getTransferTime() . "\n";
var_dump($stats->getHandlerStats());
// You must check if a response was received before using the
// response object.
if ($stats->hasResponse()) {
echo $stats->getResponse()->getStatusCode();
} else {
// Error data is handler specific. You will need to know what
// type of error data your handler uses before using this
// value.
var_dump($stats->getHandlerErrorData());
}
}
]);
传入字符串来指定HTTP代理,或者为不同代理指定不同协议的数组。
None
GuzzleHttp\RequestOptions::PROXY
传入字符串为所有协议指定一个代理:
$client->request('GET', '/', ['proxy' => 'tcp://localhost:8125']);
传入关联数组来为特殊的URI Scheme指定特色的HTTP代理(比如"http", "https")
提供一个 no
键值对来提供一组不需要使用代理的主机名。
$client->request('GET', '/', [
'proxy' => [
'http' => 'tcp://localhost:8125', // Use this proxy with "http"
'https' => 'tcp://localhost:9124', // Use this proxy with "https",
'no' => ['.mit.edu', 'foo.com'] // Don't use a proxy with these
]
]);
要添加到请求的查询字符串的关联数组或查询字符串。
None
GuzzleHttp\RequestOptions::QUERY
// Send a GET request to /get?foo=bar
$client->request('GET', '/get', ['query' => ['foo' => 'bar']]);
在 query
选项查询字符串指定,将会覆盖在请求时提供的查询字符串值。
// Send a GET request to /get?foo=bar
$client->request('GET', '/get?abc=123', ['query' => ['foo' => 'bar']]);
声明响应的主体部分将要保存的位置。
fopen()
resourcePsr\Http\Message\StreamInterface
PHP temp stream
GuzzleHttp\RequestOptions::SINK
传入字符串来指定将要保存响应主体内容的文件的路径:
$client->request('GET', '/stream/20', ['sink' => '/path/to/file']);
传入 fopen()
返回的资源将响应写入PHP流:
$resource = fopen('/path/to/file', 'w');
$client->request('GET', '/stream/20', ['sink' => $resource]);
传入 Psr\Http\Message\StreamInterface
对象将响应写入打开的PSR-7流。
$resource = fopen('/path/to/file', 'w');
$stream = GuzzleHttp\Psr7\stream_for($resource);
$client->request('GET', '/stream/20', ['save_to' => $stream]);
指定一个链接到私有SSL密钥的PEM格式的文件的路径的字符串。 如果需要密码,设置成一个数组,数组第一个元素为链接到私有SSL密钥的PEM格式的文件的路径,第二个元素为认证密码。
None
GuzzleHttp\RequestOptions::SSL_KEY
true
流响应,而非下载响应。false
GuzzleHttp\RequestOptions::STREAM
$response = $client->request('GET', '/stream/20', ['stream' => true]);
// Read bytes off of the stream until the end of the stream is reached
$body = $response->getBody();
while (!$body->eof()) {
echo $body->read(1024);
}
GuzzleHttp\RequestOptions::SYNCHRONOUS
请求时验证SSL证书行为。
true
启用SSL证书验证,默认使用操作系统提供的CA包。false
禁用证书验证(这是不安全的!)。true
GuzzleHttp\RequestOptions::VERIFY
// Use the system's CA bundle (this is 默认设置)
$client->request('GET', '/', ['verify' => true]);
// Use a custom SSL certificate on disk.
$client->request('GET', '/', ['verify' => '/path/to/cert.pem']);
// Disable validation entirely (don't do this!).
$client->request('GET', '/', ['verify' => false]);
并非所有的系统磁盘上都存在CA包,比如,Windows和OS X并没有通用的本地CA包。
当设置"verify" 为 true
时,Guzzle将尽力在你的操作系统中找到合适的CA包,
当使用cURL或PHP 5.6以上版本的流时,Guzzle将按以下顺序尝试查找CA包:
openssl.cafile
。curl.cainfo
。/etc/pki/tls/certs/ca-bundle.crt
是否存在 (Red Hat, CentOS,
Fedora; 由ca-certificates包提供)/etc/ssl/certs/ca-certificates.crt
是否存在 (Ubuntu, Debian;
由ca-certificates包提供)/usr/local/share/certs/ca-root-nss.crt
是否存在 (FreeBSD;
由ca_root_nss包提供)/usr/local/etc/openssl/cert.pem
是否存在 (OS X; 由homebrew提供)C:\windows\system32\curl-ca-bundle.crt
是否存在 (Windows)C:\windows\curl-ca-bundle.crt
是否存在 (Windows)查询的结果将缓存在内存中,以便同一进程后续快速调用。
然而在有些服务器如Apache中每个请求都在独立的进程中,你应该考虑设置 openssl.cafile
环境变量,指定到磁盘文件,以便整个过程都跳过。
如果你不需要特殊的证书包,可以使用Mozilla提供的通用CA包,你可以在
这里 下载(由cURL的维护者提供)。
一旦磁盘有了CA包,你可以设置PHP ini配置文件,指定该文件的路径到变量 openssl.cafile
中,这样就可以在请求中省略 "verify" 参数。
你可以在 cURL 网站 发现更多关于SSL证书的细节。
0
无限期的等待(默认行为)。0
GuzzleHttp\RequestOptions::TIMEOUT
// Timeout if a server does not return a response in 3.14 seconds.
$client->request('GET', '/delay/5', ['timeout' => 3.14]);
// PHP Fatal error: Uncaught exception 'GuzzleHttp\Exception\RequestException'
1.1
GuzzleHttp\RequestOptions::VERSION
// Force HTTP/1.0
$request = $client->request('GET', '/get', ['version' => 1.0]);
Guzzle utilizes PSR-7 as the HTTP message interface. This allows Guzzle to work with any other library that utilizes PSR-7 message interfaces.
Guzzle is an HTTP client that sends HTTP requests to a server and receives HTTP responses. Both requests and responses are referred to as messages.
Guzzle relies on the guzzlehttp/psr7
Composer package for its message
implementation of PSR-7.
You can create a request using the GuzzleHttp\Psr7\Request
class:
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'http://httpbin.org/get');
// You can provide other optional constructor arguments.
$headers = ['X-Foo' => 'Bar'];
$body = 'hello!';
$request = new Request('PUT', 'http://httpbin.org/put', $headers, $body);
You can create a response using the GuzzleHttp\Psr7\Response
class:
use GuzzleHttp\Psr7\Response;
// The constructor requires no arguments.
$response = new Response();
echo $response->getStatusCode(); // 200
echo $response->getProtocolVersion(); // 1.1
// You can supply any number of optional arguments.
$status = 200;
$headers = ['X-Foo' => 'Bar'];
$body = 'hello!';
$protocol = '1.1';
$response = new Response($status, $headers, $body, $protocol);
Both request and response messages contain HTTP headers.
You can check if a request or response has a specific header using the
hasHeader()
method.
use GuzzleHttp\Psr7;
$request = new Psr7\Request('GET', '/', ['X-Foo' => 'bar']);
if ($request->hasHeader('X-Foo')) {
echo 'It is there';
}
You can retrieve all the header values as an array of strings using
getHeader()
.
$request->getHeader('X-Foo'); // ['bar']
// Retrieving a missing header returns an empty array.
$request->getHeader('X-Bar'); // []
You can iterate over the headers of a message using the getHeaders()
method.
foreach ($request->getHeaders() as $name => $values) {
echo $name . ': ' . implode(', ', $values) . "\r\n";
}
Some headers contain additional key value pair information. For example, Link headers contain a link and several key value pairs:
<http://foo.com>; rel="thing"; type="image/jpeg"
Guzzle provides a convenience feature that can be used to parse these types of headers:
use GuzzleHttp\Psr7;
$request = new Psr7\Request('GET', '/', [
'Link' => '<http:/.../front.jpeg>; rel="front"; type="image/jpeg"'
]);
$parsed = Psr7\parse_header($request->getHeader('Link'));
var_export($parsed);
Will output:
array (
0 =>
array (
0 => '<http:/.../front.jpeg>',
'rel' => 'front',
'type' => 'image/jpeg',
),
)
The result contains a hash of key value pairs. Header values that have no key (i.e., the link) are indexed numerically while headers parts that form a key value pair are added as a key value pair.
Both request and response messages can contain a body.
You can retrieve the body of a message using the getBody()
method:
$response = GuzzleHttp\get('http://httpbin.org/get');
echo $response->getBody();
// JSON string: { ... }
The body used in request and response objects is a
Psr\Http\Message\StreamInterface
. This stream is used for both
uploading data and downloading data. Guzzle will, by default, store the body of
a message in a stream that uses PHP temp streams. When the size of the body
exceeds 2 MB, the stream will automatically switch to storing data on disk
rather than in memory (protecting your application from memory exhaustion).
The easiest way to create a body for a message is using the stream_for
function from the GuzzleHttp\Psr7
namespace --
GuzzleHttp\Psr7\stream_for
. This function accepts strings, resources,
callables, iterators, other streamables, and returns an instance of
Psr\Http\Message\StreamInterface
.
The body of a request or response can be cast to a string or you can read and write bytes off of the stream as needed.
use GuzzleHttp\Stream\Stream;
$response = $client->request('GET', 'http://httpbin.org/get');
echo $response->getBody()->read(4);
echo $response->getBody()->read(4);
echo $response->getBody()->read(1024);
var_export($response->eof());
Requests are sent from a client to a server. Requests include the method to be applied to a resource, the identifier of the resource, and the protocol version to use.
When creating a request, you are expected to provide the HTTP method you wish to perform. You can specify any method you'd like, including a custom method that might not be part of RFC 7231 (like "MOVE").
// Create a request using a completely custom HTTP method
$request = new \GuzzleHttp\Psr7\Request('MOVE', 'http://httpbin.org/move');
echo $request->getMethod();
// MOVE
You can create and send a request using methods on a client that map to the HTTP method you wish to use.
$client->get('http://httpbin.org/get', [/** options **/])
$client->post('http://httpbin.org/post', [/** options **/])
$client->head('http://httpbin.org/get', [/** options **/])
$client->put('http://httpbin.org/put', [/** options **/])
$client->delete('http://httpbin.org/delete', [/** options **/])
$client->options('http://httpbin.org/get', [/** options **/])
$client->patch('http://httpbin.org/put', [/** options **/])
For example:
$response = $client->patch('http://httpbin.org/patch', ['body' => 'content']);
The request URI is represented by a Psr\Http\Message\UriInterface
object.
Guzzle provides an implementation of this interface using the
GuzzleHttp\Psr7\Uri
class.
When creating a request, you can provide the URI as a string or an instance of
Psr\Http\Message\UriInterface
.
$response = $client->request('GET', 'http://httpbin.org/get?q=foo');
The scheme of a request specifies the protocol to use when sending the request. When using Guzzle, the scheme can be set to "http" or "https".
$request = new Request('GET', 'http://httpbin.org');
echo $request->getUri()->getScheme(); // http
echo $request->getUri(); // http://httpbin.org/get
The host is accessible using the URI owned by the request or by accessing the Host header.
$request = new Request('GET', 'http://httpbin.org');
echo $request->getUri()->getHost(); // httpbin.org
echo $request->getHeader('Host'); // httpbin.org
No port is necessary when using the "http" or "https" schemes.
$request = $client->createRequest('GET', 'http://httpbin.org:8080');
echo $request->getUri()->getPort(); // 8080
echo $request->getUri(); // http://httpbin.org:8080
The path of a request is accessible via the URI object.
$request = new Request('GET', 'http://httpbin.org/get');
echo $request->getUri()->getPath(); // /get
The contents of the path will be automatically filtered to ensure that only allowed characters are present in the path. Any characters that are not allowed in the path will be percent-encoded according to RFC 3986 section 3.3
The query string of a request can be accessed using the getQuery()
of the
URI object owned by the request.
$request = new Request('GET', 'http://httpbin.org/?foo=bar');
echo $request->getUri()->getQuery(); // foo=bar
The contents of the query string will be automatically filtered to ensure that only allowed characters are present in the query string. Any characters that are not allowed in the query string will be percent-encoded according to RFC 3986 section 3.4
Responses are the HTTP messages a client receives from a server after sending an HTTP request message.
The start-line of a response contains the protocol and protocol version, status code, and reason phrase.
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'http://httpbin.org/get');
echo $response->getStatusCode(); // 200
echo $response->getReasonPhrase(); // OK
echo $response->getProtocolVersion(); // 1.1
As described earlier, you can get the body of a response using the
getBody()
method.
$body = $response->getBody()) {
echo $body;
// Cast to a string: { ... }
$body->seek(0);
// Rewind the body
$body->read(1024);
// Read bytes of the body
Guzzle uses PSR-7 stream objects to represent request and response message bodies. These stream objects allow you to work with various types of data all using a common interface.
HTTP messages consist of a start-line, headers, and a body. The body of an HTTP message can be very small or extremely large. Attempting to represent the body of a message as a string can easily consume more memory than intended because the body must be stored completely in memory. Attempting to store the body of a request or response in memory would preclude the use of that implementation from being able to work with large message bodies. The StreamInterface is used in order to hide the implementation details of where a stream of data is read from or written to.
The PSR-7 Psr\Http\Message\StreamInterface
exposes several methods
that enable streams to be read from, written to, and traversed effectively.
Streams expose their capabilities using three methods: isReadable()
,
isWritable()
, and isSeekable()
. These methods can be used by stream
collaborators to determine if a stream is capable of their requirements.
Each stream instance has various capabilities: they can be read-only, write-only, read-write, allow arbitrary random access (seeking forwards or backwards to any location), or only allow sequential access (for example in the case of a socket or pipe).
Guzzle uses the guzzlehttp/psr7
package to provide stream support. More
information on using streams, creating streams, converting streams to PHP
stream resource, and stream decorators can be found in the
Guzzle PSR-7 documentation.
The best way to create a stream is using the GuzzleHttp\Psr7\stream_for
function. This function accepts strings, resources returned from fopen()
,
an object that implements __toString()
, iterators, callables, and instances
of Psr\Http\Message\StreamInterface
.
use GuzzleHttp\Psr7;
$stream = Psr7\stream_for('string data');
echo $stream;
// string data
echo $stream->read(3);
// str
echo $stream->getContents();
// ing data
var_export($stream->eof());
// true
var_export($stream->tell());
// 11
You can create streams from iterators. The iterator can yield any number of bytes per iteration. Any excess bytes returned by the iterator that were not requested by a stream consumer will be buffered until a subsequent read.
use GuzzleHttp\Psr7;
$generator = function ($bytes) {
for ($i = 0; $i < $bytes; $i++) {
yield '.';
}
};
$iter = $generator(1024);
$stream = Psr7\stream_for($iter);
echo $stream->read(3); // ...
Streams expose stream metadata through the getMetadata()
method. This
method provides the data you would retrieve when calling PHP's
stream_get_meta_data() function,
and can optionally expose other custom data.
use GuzzleHttp\Psr7;
$resource = fopen('/path/to/file', 'r');
$stream = Psr7\stream_for($resource);
echo $stream->getMetadata('uri');
// /path/to/file
var_export($stream->isReadable());
// true
var_export($stream->isWritable());
// false
var_export($stream->isSeekable());
// true
Adding custom functionality to streams is very simple with stream decorators. Guzzle provides several built-in decorators that provide additional stream functionality.
Guzzle clients use a handler and middleware system to send HTTP requests.
A handler function accepts a Psr\Http\Message\RequestInterface
and array of
request options and returns a GuzzleHttp\Promise\PromiseInterface
that is
fulfilled with a Psr\Http\Message\ResponseInterface
or rejected with an
exception.
You can provide a custom handler to a client using the handler
option of
a client constructor. It is important to understand that several request
options used by Guzzle require that specific middlewares wrap the handler used
by the client. You can ensure that the handler you provide to a client uses the
default middlewares by wrapping the handler in the
GuzzleHttp\HandlerStack::create(callable $handler = null)
static method.
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
$handler = new CurlHandler();
$stack = HandlerStack::create($handler); // Wrap w/ middleware
$client = new Client(['handler' => $stack]);
The create
method adds default handlers to the HandlerStack
. When the
HandlerStack
is resolved, the handlers will execute in the following order:
http_errors
- No op when sending a request. The response status code is checked in the response processing when returning a response promise up the stack.allow_redirects
- No op when sending a request. Following redirects occurs when a response promise is being returned up the stack.cookies
- Adds cookies to requests.prepare_body
- The body of an HTTP request will be prepared (e.g., add default headers like Content-Length, Content-Type, etc.).- <send request with handler>
prepare_body
- no op on response processing.cookies
- extracts response cookies into the cookie jar.allow_redirects
- Follows redirects.http_errors
- throws exceptions when the response status code>=
300.
When provided no $handler
argument, GuzzleHttp\HandlerStack::create()
will choose the most appropriate handler based on the extensions available on
your system.
重要
The handler provided to a client determines how request options are applied
and utilized for each request sent by a client. For example, if you do not
have a cookie middleware associated with a client, then setting the
cookies
request option will have no effect on the request.
Middleware augments the functionality of handlers by invoking them in the process of generating responses. Middleware is implemented as a higher order function that takes the following form.
use Psr\Http\Message\RequestInterface;
function my_middleware()
{
return function (callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
return $handler($request, $options);
};
};
}
Middleware functions return a function that accepts the next handler to invoke. This returned function then returns another function that acts as a composed handler-- it accepts a request and options, and returns a promise that is fulfilled with a response. Your composed middleware can modify the request, add custom request options, and modify the promise returned by the downstream handler.
Here's an example of adding a header to each request.
use Psr\Http\Message\RequestInterface;
function add_header($header, $value)
{
return function (callable $handler) use ($header, $value) {
return function (
RequestInterface $request,
array $options
) use ($handler, $header, $value) {
$request = $request->withHeader($header, $value);
return $handler($request, $options);
};
};
}
Once a middleware has been created, you can add it to a client by either wrapping the handler used by the client or by decorating a handler stack.
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
$stack = new HandlerStack();
$stack->setHandler(new CurlHandler());
$stack->push(add_header('X-Foo', 'bar'));
$client = new Client(['handler' => $stack]);
Now when you send a request, the client will use a handler composed with your added middleware, adding a header to each request.
Here's an example of creating a middleware that modifies the response of the downstream handler. This example adds a header to the response.
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
function add_response_header($header, $value)
{
return function (callable $handler) use ($header, $value) {
return function (
RequestInterface $request,
array $options
) use ($handler, $header, $value) {
$promise = $handler($request, $options)
return $promise->then(
function (ResponseInterface $response) use ($header, $value) {
return $response->withHeader($header, $value);
}
);
}
};
}
$stack = new HandlerStack();
$stack->setHandler(new CurlHandler());
$stack->push(add_response_header('X-Foo', 'bar'));
$client = new Client(['handler' => $stack]);
Creating a middleware that modifies a request is made much simpler using the
GuzzleHttp\Middleware::mapRequest()
middleware. This middleware accepts
a function that takes the request argument and returns the request to send.
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
$stack = new HandlerStack();
$stack->setHandler(new CurlHandler());
$stack->push(Middleware::mapRequest(function (RequestInterface $request) {
return $request->withHeader('X-Foo', 'bar');
}));
$client = new Client(['handler' => $stack]);
Modifying a response is also much simpler using the
GuzzleHttp\Middleware::mapResponse()
middleware.
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
$stack = new HandlerStack();
$stack->setHandler(new CurlHandler());
$stack->push(Middleware::mapResponse(function (ResponseInterface $response) {
return $response->withHeader('X-Foo', 'bar');
}));
$client = new Client(['handler' => $stack]);
A handler stack represents a stack of middleware to apply to a base handler function. You can push middleware to the stack to add to the top of the stack, and unshift middleware onto the stack to add to the bottom of the stack. When the stack is resolved, the handler is pushed onto the stack. Each value is then popped off of the stack, wrapping the previous value popped off of the stack.
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Client;
$stack = new HandlerStack();
$stack->setHandler(\GuzzleHttp\choose_handler());
$stack->push(Middleware::mapRequest(function (RequestInterface $r) {
echo 'A';
return $r;
});
$stack->push(Middleware::mapRequest(function (RequestInterface $r) {
echo 'B';
return $r;
});
$stack->push(Middleware::mapRequest(function (RequestInterface $r) {
echo 'C';
return $r;
});
$client->request('GET', 'http://httpbin.org/');
// echoes 'ABC';
$stack->unshift(Middleware::mapRequest(function (RequestInterface $r) {
echo '0';
return $r;
});
$client = new Client(['handler' => $stack]);
$client->request('GET', 'http://httpbin.org/');
// echoes '0ABC';
You can give middleware a name, which allows you to add middleware before other named middleware, after other named middleware, or remove middleware by name.
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\Middleware;
// Add a middleware with a name
$stack->push(Middleware::mapRequest(function (RequestInterface $r) {
return $r->withHeader('X-Foo', 'Bar');
}, 'add_foo');
// Add a middleware before a named middleware (unshift before).
$stack->before('add_foo', Middleware::mapRequest(function (RequestInterface $r) {
return $r->withHeader('X-Baz', 'Qux');
}, 'add_baz');
// Add a middleware after a named middleware (pushed after).
$stack->after('add_baz', Middleware::mapRequest(function (RequestInterface $r) {
return $r->withHeader('X-Lorem', 'Ipsum');
});
// Remove a middleware by name
$stack->remove('add_foo');
As stated earlier, a handler is a function accepts a
Psr\Http\Message\RequestInterface
and array of request options and returns
a GuzzleHttp\Promise\PromiseInterface
that is fulfilled with a
Psr\Http\Message\ResponseInterface
or rejected with an exception.
A handler is responsible for applying the following 请求选项. These request options are a subset of request options called "transfer options".
Guzzle provides several tools that will enable you to easily mock the HTTP layer without needing to send requests over the internet.
When testing HTTP clients, you often need to simulate specific scenarios like returning a successful response, returning an error, or returning specific responses in a certain order. Because unit tests need to be predictable, easy to bootstrap, and fast, hitting an actual remote API is a test smell.
Guzzle provides a mock handler that can be used to fulfill HTTP requests with a response or exception by shifting return values off of a queue.
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\RequestException;
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200, ['X-Foo' => 'Bar']),
new Response(202, ['Content-Length' => 0]),
new RequestException("Error Communicating with Server", new Request('GET', 'test'))
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
// The first request is intercepted with the first response.
echo $client->request('GET', '/')->getStatusCode();
//> 200
// The second request is intercepted with the second response.
echo $client->request('GET', '/')->getStatusCode();
//> 202
When no more responses are in the queue and a request is sent, an
OutOfBoundsException
is thrown.
When using things like the Mock
handler, you often need to know if the
requests you expected to send were sent exactly as you intended. While the mock
handler responds with mocked responses, the history middleware maintains a
history of the requests that were sent by a client.
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
$container = [];
$history = Middleware::history($container);
$stack = HandlerStack::create();
// Add the history middleware to the handler stack.
$stack->push($history);
$client = new Client(['handler' => $stack]);
$client->request('GET', 'http://httpbin.org/get');
$client->request('HEAD', 'http://httpbin.org/get');
// Count the number of transactions
echo count($container);
//> 2
// Iterate over the requests and responses
foreach ($container as $transaction) {
echo $transaction['request']->getMethod();
//> GET, HEAD
if ($transaction['response']) {
echo $transaction['response']->getStatusCode();
//> 200, 200
} elseif ($transaction['error']) {
echo $transaction['error'];
//> exception
}
var_dump($transaction['options']);
//> dumps the request options of the sent request.
}
Using mock responses is almost always enough when testing a web service client. When implementing custom HTTP handlers, you'll need to send actual HTTP requests in order to sufficiently test the handler. However, a best practice is to contact a local web server rather than a server over the internet.
警告
The following functionality is provided to help developers of Guzzle
develop HTTP handlers. There is no promise of backwards compatibility
when it comes to the node.js test server or the GuzzleHttp\Tests\Server
class. If you are using the test server or Server
class outside of
guzzlehttp/guzzle, then you will need to configure autoloading and
ensure the web server is started manually.
提示
You almost never need to use this test web server. You should only ever consider using it when developing HTTP handlers. The test web server is not necessary for mocking requests. For that, please use the Mock handler and history middleware.
Guzzle ships with a node.js test server that receives requests and returns responses from a queue. The test server exposes a simple API that is used to enqueue responses and inspect the requests that it has received.
Any operation on the Server
object will ensure that
the server is running and wait until it is able to receive requests before
returning.
GuzzleHttp\Tests\Server
provides a static interface to the test server. You
can queue an HTTP response or an array of responses by calling
Server::enqueue()
. This method accepts an array of
Psr\Http\Message\ResponseInterface
and Exception
objects.
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Tests\Server;
// Start the server and queue a response
Server::enqueue([
new Response(200, ['Content-Length' => 0])
]);
$client = new Client(['base_uri' => Server::$url]);
echo $client->request('GET', '/foo')->getStatusCode();
// 200
When a response is queued on the test server, the test server will remove any previously queued responses. As the server receives requests, queued responses are dequeued and returned to the request. When the queue is empty, the server will return a 500 response.
You can inspect the requests that the server has retrieved by calling
Server::received()
.
foreach (Server::received() as $response) {
echo $response->getStatusCode();
}
You can clear the list of received requests from the web server using the
Server::flush()
method.
Server::flush();
echo count(Server::received());
// 0
No. Guzzle can use any HTTP handler to send requests. This means that Guzzle can be used with cURL, PHP's stream wrapper, sockets, and non-blocking libraries like React. You just need to configure an HTTP handler to use a different method of sending requests.
注解
Guzzle has historically only utilized cURL to send HTTP requests. cURL is an amazing HTTP client (arguably the best), and Guzzle will continue to use it by default when it is available. It is rare, but some developers don't have cURL installed on their systems or run into version specific issues. By allowing swappable HTTP handlers, Guzzle is now much more customizable and able to adapt to fit the needs of more developers.
Yes. You can use the requestAsync
, sendAsync
, getAsync
,
headAsync
, putAsync
, postAsync
, deleteAsync
, and patchAsync
methods of a client to send an asynchronous request. The client will return a
GuzzleHttp\Promise\PromiseInterface
object. You can chain then
functions off of the promise.
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$promise->then(function ($response) {
echo 'Got a response! ' . $response->getStatusCode();
});
You can force an asynchronous response to complete using the wait()
method
of the returned promise.
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$response = $promise->wait();
cURL offer a huge number of customizable options. While Guzzle normalizes many of these options across different handlers, there are times when you need to set custom cURL options. This can be accomplished by passing an associative array of cURL settings in the curl key of a request.
For example, let's say you need to customize the outgoing network interface used with a client.
$client->request('GET', '/', [
'curl' => [
CURLOPT_INTERFACE => 'xxx.xxx.xxx.xxx'
]
]);
You can pass custom stream context options using the stream_context key of the request option. The stream_context array is an associative array where each key is a PHP transport, and each value is an associative array of transport options.
For example, let's say you need to customize the outgoing network interface used with a client and allow self-signed certificates.
$client->request('GET', '/', [
'stream' => true,
'stream_context' => [
'ssl' => [
'allow_self_signed' => true
],
'socket' => [
'bindto' => 'xxx.xxx.xxx.xxx'
]
]
]);
You need to specify the path on disk to the CA bundle used by Guzzle for verifying the peer certificate. See verify.
Maximum function nesting level of '100' reached, aborting
You could run into this error if you have the XDebug extension installed and you execute a lot of requests in callbacks. This error message comes specifically from the XDebug extension. PHP itself does not have a function nesting limit. Change this setting in your php.ini to increase the limit:
xdebug.max_nesting_level = 1000
This can occur for a number of reasons, but if you are sending PUT, POST, or
PATCH requests with an Expect: 100-Continue
header, a server that does not
support this header will return a 417 response. You can work around this by
setting the expect
request option to false
:
$client = new GuzzleHttp\Client();
// Disable the expect header on a single request
$response = $client->request('PUT', '/', ['expect' => false]);
// Disable the expect header on all client requests
$client = new GuzzleHttp\Client(['expect' => false]);