PHP Guzzle HTTP Quick Start: Practical Usage Examples

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 by fopen(), 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 true to enable redirects with a default maximum of 5 redirects.
  • Set it to false to disable redirects.
  • Pass an associative array with a max key to define the maximum redirect count, and optionally a strict key 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 extends GuzzleHttpExceptionTransferException, 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();
    }
    }
  • GuzzleHttpExceptionConnectException occurs on network errors and extends GuzzleHttpExceptionRequestException.
  • If the http_errors request option is set to true, then a 400-level error causes a GuzzleHttpExceptionClientException to be thrown. It extends GuzzleHttpExceptionBadResponseException, which in turn extends GuzzleHttpExceptionRequestException.
    use GuzzleHttpExceptionClientException;

    try {
    $client->request('GET', 'https://github.com/_abc_123_404');
    } catch (ClientException $e) {
    echo $e->getRequest();
    echo $e->getResponse();
    }
  • If the http_errors request option is set to true, then a 500-level error causes a GuzzleHttpExceptionServerException.
  • GuzzleHttpExceptionTooManyRedirectsException is 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.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *