Guzzle has almost become the de facto HTTP standard library in PHP. In newer versions of the WordPress JSON API plugin, WordPress has already stopped relying on the WP HTTP API and uses Guzzle for development instead. It is entirely possible that one day the WP HTTP API will be phased out and Guzzle will be used more broadly. If you have not worked with Guzzle before, the following notes provide a quick introduction and a set of practical examples.
Sending requests
We can use the GuzzleHttpClientInterface object provided by Guzzle to send requests.
Create a client
use GuzzleHttpClient;
$client = new Client([
'base_uri' => 'http://httpbin.org', // 相对请求的基础 URI
'timeout' => 2.0, // 可以设置请求的默认超时时间
]);
Clients in Guzzle are immutable. That means after a client is created, you cannot change the default values it uses. The Client object accepts an array of constructor options.
base_uri
The base URI can be either a string or an instance of UriInterface. It is used to merge with relative request URIs. When a relative URL is provided, it is combined with the base URI according to the rules defined in RFC 3986 section 2.
// 使用 基础 URI 创建一个客户端
$client = new GuzzleHttpClient(['base_uri' => 'https://foo.com/api/']);
// 发送一个请求到 https://foo.com/api/test
$response = $client->request('GET', 'test');
// 发送一个请求到 https://foo.com/root
$response = $client->request('GET', '/root');
Do not want to read RFC 3986? Here are a few quick examples showing how base_uri works together with other URIs:
| 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
The handler is the callback function that actually transfers HTTP requests. It receives a Psr7HttpMessageRequestInterface instance and an array of options, and it must return a GuzzleHttpPromisePromiseInterface that resolves successfully with a Psr7HttpMessageResponseInterface.
Because handler is a constructor option, it cannot be overridden in per-request options. All other constructor options can be treated as default options that apply to every request.
Sending requests
The methods on the Client object make it very easy to send requests:
$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');
You can also create a request object first and pass it to the client when everything is ready:
use GuzzleHttpPsr7Request;
$request = new Request('PUT', 'http://httpbin.org/put');
$response = $client->send($request, ['timeout' => 2]);
The Client object gives you a very flexible way to handle requests, including request options, middleware applied per request, and a base URI for a group of related requests.
You can find more middleware details on the Handlers and Middleware page.
Asynchronous requests
You can create asynchronous requests by using the methods provided by 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');
You can also use the client’s sendAsync() and requestAsync() methods:
use GuzzleHttpPsr7Request;
// 创建要发送的 PSR-7 请求对象
$headers = ['X-Foo' => 'Bar'];
$body = 'Hello!';
$request = new Request('HEAD', 'http://httpbin.org/head', $headers, $body);
// 或者,如果您不需要传入请求实例:
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
These methods return a Promise object that follows the Promises/A+ specification from the Guzzle promises library. That means you can use then() to register success and failure handlers. On success, the handler receives a PsrHttpMessageResponseInterface. On failure, an exception is thrown.
use PsrHttpMessageResponseInterface;
use GuzzleHttpExceptionRequestException;
$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();
}
);
Concurrent requests
You can use Promises and asynchronous requests to send multiple requests at the same time:
use GuzzleHttpClient;
use GuzzleHttpPromise;
$client = new Client(['base_uri' => 'http://httpbin.org/']);
// 开始每个请求,但是不阻塞
$promises = [
'image' => $client->getAsync('/image'),
'png' => $client->getAsync('/image/png'),
'jpeg' => $client->getAsync('/image/jpeg'),
'webp' => $client->getAsync('/image/webp')
];
// 等待所有请求完成
$results = Promiseunwrap($promises);
// 您可以使用提供给 unwrap 函数的键来访问每个结果。
echo $results['image']->getHeader('Content-Length');
echo $results['png']->getHeader('Content-Length');
If you need to send an unknown number of requests, you can use the GuzzleHttpPool object:
use GuzzleHttpPool;
use GuzzleHttpClient;
use GuzzleHttpPsr7Request;
$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) {
// 每个请求成功时执行
},
'rejected' => function ($reason, $index) {
// 每个请求失败时执行
},
]);
// 开始传输并创建一个 promise
$promise = $pool->promise();
// 等待请求池完成
$promise->wait();
Working with responses
In the earlier examples, we retrieved a $response variable, or got a response from a Promise. The Response object implements the PSR-7 PsrHttpMessageResponseInterface and contains a lot of useful information.
You can retrieve the status code and reason phrase of the response:
$code = $response->getStatusCode(); // 200
$reason = $response->getReasonPhrase(); // OK
You can also retrieve headers from the response:
// 检查 header 是否存在
if ($response->hasHeader('Content-Length')) {
echo "It exists";
}
// 从响应中获取请求
echo $response->getHeader('Content-Length');
// 获取所有响应头
foreach ($response->getHeaders() as $name => $values) {
echo $name . ': ' . implode(', ', $values) . "rn";
}
The getBody() method returns the response body. The body can be used as a string or as a stream object:
$body = $response->getBody();
// 直接作为字符串显示 body
echo $body;
// 作为一个字符串赋值 body 给一个变量
$stringBody = (string) $body;
// 读取 body 内容的前 10 字节
$tenBytes = $body->read(10);
// 作为字符串读取剩下的 body 内容Read
$remainingBytes = $body->getContents();
Query string parameters
You can provide query string parameters in several different ways. One simple option is to place the query string directly in the request URI:
$response = $client->request('GET', 'http://httpbin.org?foo=bar');
You can also declare query string parameters by using the query request option:
$client->request('GET', 'http://httpbin.org', [
'query' => ['foo' => 'bar']
]);
The array you provide will be converted using PHP’s http_build_query().
Finally, you can also pass a plain string as the query request option:
$client->request('GET', 'http://httpbin.org', ['query' => 'foo=bar']);
Uploading data
Guzzle offers several ways to upload data. You can send a request with a body stream by setting the body request option to a string, a resource returned by fopen(), or an instance of PsrHttpMessageStreamInterface.
// 作为字符串提供 body 内容
$r = $client->request('POST', 'http://httpbin.org/post', [
'body' => 'raw data'
]);
// 提供一个 fopen 资源
$body = fopen('/path/to/file', 'r');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);
// 使用 stream_for() 函数创建一个 PSR-7 stream.
$body = GuzzleHttpPsr7stream_for('hello!');
$r = $client->request('POST', 'http://httpbin.org/post', ['body' => $body]);
If you need to upload JSON and set the correct headers, the simplest way is to use the json request option:
$r = $client->request('PUT', 'http://httpbin.org/put', [
'json' => ['foo' => 'bar']
]);
POST/form requests
In addition to the body option, Guzzle provides useful dedicated methods for sending POST data.
Sending form fields
To send an application/x-www-form-urlencoded POST request, pass a form_params array that defines the POST fields.
$response = $client->request('POST', 'http://httpbin.org/post', [
'form_params' => [
'field_name' => 'abc',
'other_field' => '123',
'nested_field' => [
'nested' => 'hello'
]
]
]);
Sending form files
You can send form files by using the multipart request option. In this case, the form’s enctype should be set to multipart/form-data. The multipart option accepts an array of associative arrays, and each item can contain the following keys:
name: required, a string that maps to the form field name.contents: required, may be a string, a resource returned byfopen(), or an
PsrHttpMessageStreamInterface instance.
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'
]
]
]
]);
Cookies
Guzzle can maintain a cookie session for you through the cookies request option. When sending a request, the cookies option must be set to an instance of GuzzleHttpCookieCookieJarInterface.
// 使用一个指定的 cookie
$jar = new GuzzleHttpCookieCookieJar;
$r = $client->request('GET', 'http://httpbin.org/cookies', [
'cookies' => $jar
]);
If you want to share cookies across all requests, set cookies to true in the client constructor.
// 使用共 cookie 的客户端
$client = new GuzzleHttpClient(['cookies' => true]);
$r = $client->request('GET', 'http://httpbin.org/cookies');
Redirects
Unless you explicitly tell Guzzle not to follow redirects, it will do so automatically. You can customize redirect behavior through the allow_redirects request option.
- Set it to
trueto enable redirects with a default maximum of 5 redirects. - Set it to
falseto disable redirects. - Pass an associative array with a
maxkey to define the maximum redirect count, and optionally astrictkey to decide whether strict RFC-compliant redirects should be used.
$response = $client->request('GET', 'http://github.com');
echo $response->getStatusCode(); // 200
The following example shows redirects disabled:
$response = $client->request('GET', 'http://github.com', [
'allow_redirects' => false
]);
echo $response->getStatusCode();
Exceptions
If an error occurs while transferring a request, Guzzle throws an exception.
- When a network error occurs, such as a connection timeout or DNS error, Guzzle throws a
GuzzleHttpExceptionRequestException. This exception extendsGuzzleHttpExceptionTransferException, so catching it lets you handle transport-layer errors.use GuzzleHttpExceptionRequestException;
try {
$client->request('GET', 'https://github.com/_abc_123_404');
} catch (RequestException $e) {
echo $e->getRequest();
if ($e->hasResponse()) {
echo $e->getResponse();
}
} GuzzleHttpExceptionConnectExceptionoccurs on network errors and extendsGuzzleHttpExceptionRequestException.- If the
http_errorsrequest option is set totrue, then a 400-level error causes aGuzzleHttpExceptionClientExceptionto be thrown. It extendsGuzzleHttpExceptionBadResponseException, which in turn extendsGuzzleHttpExceptionRequestException.use GuzzleHttpExceptionClientException;
try {
$client->request('GET', 'https://github.com/_abc_123_404');
} catch (ClientException $e) {
echo $e->getRequest();
echo $e->getResponse();
} - If the
http_errorsrequest option is set totrue, then a 500-level error causes aGuzzleHttpExceptionServerException. GuzzleHttpExceptionTooManyRedirectsExceptionis thrown when there are too many redirects.
All of the exceptions above ultimately extend GuzzleHttpExceptionTransferException.
Environment variables
Guzzle provides several configurable environment variables:
GUZZLE_CURL_SELECT_TIMEOUT
When using the Curl handler, this controls the amount of time used by curl_multi_select() while managing curl_multi_* operations. On some systems, the PHP implementation of curl_multi_select() has issues and always waits for the maximum timeout value.
HTTP_PROXY
This defines the proxy used when sending requests over HTTP.
HTTPS_PROXY
This defines the proxy used when sending requests over HTTPS.
