Guzzle中文文档

Guzzle是一个PHP的HTTP客户端,用来轻而易举地发送请求,并集成到我们的WEB服务上。

  • 接口简单:构建查询语句、POST请求、分流上传下载大文件、使用HTTP cookies、上传JSON数据等等。
  • 发送同步或异步的请求均使用相同的接口。
  • 使用PSR-7接口来请求、响应、分流,允许你使用其他兼容的PSR-7类库与Guzzle共同开发。
  • 抽象了底层的HTTP传输,允许你改变环境以及其他的代码,如:对cURL与PHP的流或socket并非重度依赖,非阻塞事件循环。
  • 中间件系统允许你创建构成客户端行为。
$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();

用户指南

概述

需求

  1. PHP 5.5.0
  2. 使用PHP的流, allow_url_fopen 必须在php.ini中启用。
  3. 要使用cURL,你必须已经有版本cURL >= 7.19.4,并且编译了OpenSSL 与 zlib。

注解

如果没有安装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.

贡献

指导方针
  1. Guzzle utilizes PSR-1, PSR-2, PSR-4, and PSR-7.
  2. Guzzle is meant to be lean and fast with very few dependencies. This means that not every feature request will be accepted.
  3. Guzzle has a minimum PHP version requirement of PHP 5.5. Pull requests must not require a PHP version greater than PHP 5.5 unless the feature is only utilized conditionally.
  4. All pull requests must include unit tests to ensure the change works as expected and to prevent regressions.
测试

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
传输HTTP请求的(回调)函数。 该函数被调用的时候包含 Psr7\Http\Message\RequestInterface 以及参数数组,必须返回 GuzzleHttp\Promise\PromiseInterface ,成功时满足 Psr7\Http\Message\ResponseInterfacehandler 是一个构造方法,不能在请求参数里被重写。
...
(混合) 构造方法中传入的其他所有参数用来当作每次请求的默认参数。
发送请求

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']
]);
POST/表单请求

除了使用 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 )文件, 该参数接收一个包含多个关联数组的数组,每个关联数组包含一下键名:

  • name: (必须,字符串) 映射到表单字段的名称。
  • contents: (必须,混合) 提供一个字符串,可以是 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'
            ]
        ]
    ]
]);

Cookies

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处理器时使用 curl_multi_select() 控制了 curl_multi_* 需要使用到的持续时间, 有些系统实现PHP的 curl_multi_select() 存在问题,调用该函数时总是等待超时的最大值。
HTTP_PROXY
定义了使用http协议发送请求时使用的代理。
HTTPS_PROXY
定义了使用https协议发送请求时使用的代理。
相关ini设置

Guzzle配置客户端时可以利用PHP的ini配置。

openssl.cafile
当发送到"https"协议的请求时需要用到指定磁盘上PEM格式的CA文件,参考: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults

请求选项

你可以通过设置Client的 请求选项 来自定义请求,请求参数控制请求的各个方面,包括头信息、查询字符串参数、超时、请求主体等。

下述所有的列子都使用下面的Client:

$client = new GuzzleHttp\Client(['base_uri' => 'http://httpbin.org']);

allow_redirects

摘要

描述请求的重定向行为

类型
  • bool
  • array
默认值
[
    '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

你也可以传送一个包含了以下键值对的关联数组:

  • max: (int, 默认为5) 允许重定向次数的最大值。
  • strict: (bool, 默认为false) 设置成 true 使用严格模式重定向 严格RFC模式重定向表示使用POST请求重定向POST请求 vs 大部分浏览器使用GET请求重定向POST请求。
  • referer: (bool, 默认为true) 设置成 false 重定向时禁止添加Refer头信息。
  • protocols: (array, 默认为['http', 'https']) 指定允许重定向的协议。
  • on_redirect: (callable) 发生重定向时调用PHP回调,包含了原始的请求以及接收到重定向的响应,on_redirect的任何返回将会被忽略。
  • track_redirects: (bool) 当设置成 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...

auth

摘要

传入HTTP认证参数的数组来使用请求,该数组索引[0]为用户名、索引[1]为密码,索引[2]为可选的内置认证类型。传入 null 进入请求认证。

类型
  • array
  • string
  • null
默认值

None

常量

GuzzleHttp\RequestOptions::AUTH

内置认证类型如下:

basic
Authorization 头信息使用 HTTP基础认证 (如果没有指定的话为默认设置)。
$client->request('GET', '/get', ['auth' => ['username', 'password']]);
digest
使用 摘要式认证 (必须被HTTP处理器支持)。
$client->request('GET', '/get', [
    'auth' => ['username', 'password', 'digest']
]);

body

摘要

body 选项用来控制一个请求(比如:PUT, POST, PATCH)的主体部分。

类型
  • string
  • fopen() resource
  • Psr\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]);
    

cert

摘要

设置成指定PEM格式认证文件的路径的字符串,如果需要密码,需要设置成一个数组,其中PEM文件在第一个元素,密码在第二个元素。

类型
  • string
  • array
默认值

None

常量

GuzzleHttp\RequestOptions::CERT

$client->request('GET', '/', ['cert' => ['/path/server.pem', 'password']]);

cookies

摘要
声明是否在请求中使用cookie,或者要使用的cookie jar,或者要发送的cookie。
类型
GuzzleHttp\Cookie\CookieJarInterface
默认值
None
常量
GuzzleHttp\RequestOptions::COOKIES

你必须声明cookie选项为 GuzzleHttp\Cookie\CookieJarInterfacefalse

$jar = new \GuzzleHttp\Cookie\CookieJar();
$client->request('GET', '/get', ['cookies' => $jar]);

connect_timeout

摘要
表示等待服务器响应超时的最大值,使用 0 将无限等待 (默认行为).
类型
float
默认值
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]);

debug

摘要

设置成 true 或设置成一个 fopen() 返回的流来启用调试输出发送请求的处理器, 比如,当使用cURL传输请求,cURL的 CURLOPT_VERBOSE 的冗长将会发出, 当使用PHP流,流处理的提示将会发生。 如果设置为true,输出到PHP标准输出文件,如果提供了PHP流,将会输出到流。

类型
  • bool
  • fopen() resource
默认值

None

常量

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

decode_content

摘要

声明是否自动解码 Content-Encoding 响应 (gzip, deflate等) 。

类型
  • string
  • bool
默认值

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']);

delay

摘要

发送请求前延迟的毫秒数值

类型
  • integer
  • float
默认值

null

常量

GuzzleHttp\RequestOptions::DELAY

expect

摘要

控制"Expect: 100-Continue"报文头的行为。

类型
  • bool
  • integer
默认值

1048576

常量

GuzzleHttp\RequestOptions::EXPECT

设置成 true 来为所有发送主体的请求启用 "Expect: 100-Continue" 报文头; 设置成 false 来为所有的请求禁用 "Expect: 100-Continue" 报文头; 设置成一个数值,有效载荷的大小必须大于预计发送的值,设置成数值将会为所有不确定有效载荷大小或主体不能确定指针位置的请求发送Expect报文头,

默认情况下,当请求的主体大于1MB以及请求使用的HTTP/1.1,Guzzle将会添加 "Expect: 100-Continue" 报文头。

form_params

摘要
用来发送一个 application/x-www-form-urlencoded POST请求.
类型
array
常量
GuzzleHttp\RequestOptions::FORM_PARAMS

关联数组由表单字段键值对构成,每个字段值可以是一个字符串或一个包含字符串元素的数组。 当没有准备 "Content-Type" 报文头的时候,将设置为 "application/x-www-form-urlencoded"。

$client->request('POST', '/post', [
    'form_params' => [
        'foo' => 'bar',
        'baz' => ['hi', 'there!']
    ]
]);

headers

摘要
要添加到请求的报文头的关联数组,每个键名是header的名称,每个键值是一个字符串或包含代表头字段字符串的数组。
类型
array
Defaults
None
常量
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']]);

http_errors

摘要
设置成 false 来禁用HTTP协议抛出的异常(如 4xx 和 5xx 响应),默认情况下HTPP协议出错时会抛出异常。
类型
bool
默认值
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 选项用来轻松将JSON数据当成主体上传, 如果没有设置Content-Type头信息的时候会设置成 application/json
类型
能够 json_encode() 操作的PHP类型。
默认值
None
常量
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)
]);

multipart

摘要
设置请求的主体为 multipart/form-data 表单。
类型
array
常量
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'
        ],
    ]
]);

on_headers

摘要

回调函数,当响应的HTTP头信息被接收且主体部分还未开始下载的时候调用。

类型
  • callable
常量

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 允许你获取请求传输数据统计以及处理器在底层传输的详情. on_stats 是个回调,当处理器完成传输一个请求的时候被调用。 该回调被调用请求传输数据统计、接收到响应,或遇到错误,包含发送请求数据时间的总量。

类型
  • callable
常量

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());
        }
    }
]);

proxy

摘要

传入字符串来指定HTTP代理,或者为不同代理指定不同协议的数组。

类型
  • string
  • array
默认值

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
    ]
]);

query

摘要

要添加到请求的查询字符串的关联数组或查询字符串。

类型
  • array
  • string
默认值

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']]);

sink

摘要

声明响应的主体部分将要保存的位置。

类型
  • string (path to file on disk)
  • fopen() resource
  • Psr\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_key

摘要

指定一个链接到私有SSL密钥的PEM格式的文件的路径的字符串。 如果需要密码,设置成一个数组,数组第一个元素为链接到私有SSL密钥的PEM格式的文件的路径,第二个元素为认证密码。

类型
  • string
  • array
默认值

None

常量

GuzzleHttp\RequestOptions::SSL_KEY

stream

摘要
设置成 true 流响应,而非下载响应。
类型
bool
默认值
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);
}

synchronous

摘要
设置成 true 来通知HTTP处理器你要等待响应,这有利于优化。
类型
bool
默认值
none
常量
GuzzleHttp\RequestOptions::SYNCHRONOUS

verify

摘要

请求时验证SSL证书行为。

  • 设置成 true 启用SSL证书验证,默认使用操作系统提供的CA包。
  • 设置成 false 禁用证书验证(这是不安全的!)。
  • 设置成字符串启用验证,并使用该字符串作为自定义证书CA包的路径。
类型
  • bool
  • string
默认值

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包:

  1. 检查php.ini文件中是否设置了 openssl.cafile
  2. 检查php.ini文件中是否设置了 curl.cainfo
  3. 检查 /etc/pki/tls/certs/ca-bundle.crt 是否存在 (Red Hat, CentOS, Fedora; 由ca-certificates包提供)
  4. 检查 /etc/ssl/certs/ca-certificates.crt 是否存在 (Ubuntu, Debian; 由ca-certificates包提供)
  5. 检查 /usr/local/share/certs/ca-root-nss.crt 是否存在 (FreeBSD; 由ca_root_nss包提供)
  6. 检查 /usr/local/etc/openssl/cert.pem 是否存在 (OS X; 由homebrew提供)
  7. 检查 C:\windows\system32\curl-ca-bundle.crt 是否存在 (Windows)
  8. 检查 C:\windows\curl-ca-bundle.crt 是否存在 (Windows)

查询的结果将缓存在内存中,以便同一进程后续快速调用。 然而在有些服务器如Apache中每个请求都在独立的进程中,你应该考虑设置 openssl.cafile 环境变量,指定到磁盘文件,以便整个过程都跳过。

如果你不需要特殊的证书包,可以使用Mozilla提供的通用CA包,你可以在 这里 下载(由cURL的维护者提供)。 一旦磁盘有了CA包,你可以设置PHP ini配置文件,指定该文件的路径到变量 openssl.cafile 中,这样就可以在请求中省略 "verify" 参数。 你可以在 cURL 网站 发现更多关于SSL证书的细节。

timeout

摘要
请求超时的秒数。使用 0 无限期的等待(默认行为)。
类型
float
默认值
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'

version

摘要
请求要使用到的协议版本。
类型
string, float
默认值
1.1
常量
GuzzleHttp\RequestOptions::VERSION
// Force HTTP/1.0
$request = $client->request('GET', '/get', ['version' => 1.0]);

Guzzle and PSR-7

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);

Headers

Both request and response messages contain HTTP headers.

Accessing 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";
}
Complex Headers

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.

Body

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

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.

Request Methods

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.

GET
$client->get('http://httpbin.org/get', [/** options **/])
POST
$client->post('http://httpbin.org/post', [/** options **/])
HEAD
$client->head('http://httpbin.org/get', [/** options **/])
PUT
$client->put('http://httpbin.org/put', [/** options **/])
DELETE
$client->delete('http://httpbin.org/delete', [/** options **/])
OPTIONS
$client->options('http://httpbin.org/get', [/** options **/])
PATCH
$client->patch('http://httpbin.org/put', [/** options **/])

For example:

$response = $client->patch('http://httpbin.org/patch', ['body' => 'content']);
Request URI

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');
Scheme

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
Host

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
Port

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
Path

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

Query string

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

Responses are the HTTP messages a client receives from a server after sending an HTTP request message.

Start-Line

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
Body

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

Streams

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.

Creating Streams

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); // ...
Metadata

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
Stream Decorators

Adding custom functionality to streams is very simple with stream decorators. Guzzle provides several built-in decorators that provide additional stream functionality.

Handlers and Middleware

Guzzle clients use a handler and middleware system to send HTTP requests.

Handlers

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:

  1. Sending request:
  1. 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.
  2. allow_redirects - No op when sending a request. Following redirects occurs when a response promise is being returned up the stack.
  3. cookies - Adds cookies to requests.
  4. prepare_body - The body of an HTTP request will be prepared (e.g., add default headers like Content-Length, Content-Type, etc.).
  5. <send request with handler>
  1. Processing response:
  1. prepare_body - no op on response processing.
  2. cookies - extracts response cookies into the cookie jar.
  3. allow_redirects - Follows redirects.
  4. 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

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]);

HandlerStack

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');

Creating a Handler

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".

Testing Guzzle Clients

Guzzle provides several tools that will enable you to easily mock the HTTP layer without needing to send requests over the internet.

  • Mock handler
  • History middleware
  • Node.js web server for integration testing

Mock Handler

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.

History Middleware

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.
}

Test Web Server

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.

  • Tests are more reliable
  • Tests do not require a network connection
  • Tests have no external dependencies
Using the test server

警告

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

FAQ

Does Guzzle require cURL?

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.

Can Guzzle send asynchronous requests?

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();

How can I add custom cURL options?

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'
    ]
]);

How can I add custom stream context options?

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'
        ]
    ]
]);

Why am I getting an SSL verification error?

You need to specify the path on disk to the CA bundle used by Guzzle for verifying the peer certificate. See verify.

What is this Maximum function nesting error?

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

Why am I getting a 417 error response?

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]);