Исходники на GitHub

Документация здесь

Guzzle

Guzzle - это PHP-HTTP-клиент, который упрощает отправку HTTP-запросов и интеграцию с веб-сервисами. Guzzle может отправлять как синхронные, так и асинхронные запросы, используя один и тот же интерфейс. Среди его плюсов: простой интерфейс работы со строкой запросов, использование HTTP-кук (cookies), загрузка данных. Если вы не хотите сами настраивать POST или GET запросы через cURL, то библиотека Guzzle как раз для вас.

Будем использовать Guzzle. Именно в этой версии есть поддержка стандарта PSR-7 — HTTP Message Interface

Установка Guzzle HTTP-клиент

Рекомендуемый способ установки Guzzle - с помощью Composer. В файле composer.json

{
  "require": {
    "guzzlehttp/guzzle": "^7.8"
  }
}

Или из консоли (терминала):

php composer.phar require guzzlehttp/guzzle

Если нужна последняя нестабильная версия пакета:

{
  "require": {
    "guzzlehttp/guzzle": "^7.8@dev"
  }
}

После установки вам понадобится автозагрузчик Composer:

require 'vendor/autoload.php';

Создание клиента

Можно делать запросы в Guzzle, используя объект GuzzleHttp\ClientInterface:

$client = new Client([
  // Базовый URI используется с относительными запросами
  'base_uri' => 'http://httpbin.org',
  // Вы можете установить любое количество параметров запроса по умолчанию.
  'timeout'  => 2.0,
]);

Клиент является неизменными в Guzzle 6, что означает, что вы не можете изменить значения по умолчанию, используемые клиентом после его создания.

Конструктор клиента принимает ассоциативный массив опций:

base_uri

(string|UriInterface) Базовый URI клиента, который объединен в относительные URI. Может быть строкой или экземпляром UriInterface. Когда клиенту предоставляется относительный URI, клиент объединяет базовый URI с относительным URI, используя правила, описанные в RFC 3986, section 2.

// Создать клиента с базовым URI
$client = new GuzzleHttp\Client(['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');

Не хочется читать RFC 3986? Вот несколько быстрых примеров того, как base_uri разрешается с другим URI:

base_uri URI Результат
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

(callable) Функция, которая передает HTTP-запросы по сети. Функция вызывается с Psr7\Http\Message\RequestInterface и массив опций передачи, и должен возвращать GuzzleHttp\Promise\PromiseInterface который выполняется с Psr7\Http\Message\ResponseInterface в случае успеха. handler это опция только для конструктора, которая не может быть переопределена в параметрах per/request.

(mixed) Все остальные параметры, передаваемые конструктору, используются в качестве параметров запроса по умолчанию для каждого запроса, создаваемого клиентом.

Отправка запросов

Магические методы на клиенте позволяют легко отправлять синхронные запросы:

$client = new GuzzleHttp\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');

Вы можете создать запрос и затем отправить запрос клиенту, когда будете готовы:

use GuzzleHttp\Psr7\Request;

$request = new Request('PUT', 'http://httpbin.org/put');
$response = $client->send($request, ['timeout' => 2]);

Клиентские объекты обеспечивают большую гибкость в том, как передаются запросы, включая параметры запроса по умолчанию, промежуточное программное обеспечение стека обработчиков по умолчанию, которое используется каждым запросом, и базовый URI, который позволяет отправлять запросы с относительными URI.

Асинхронные запросы

Вы можете отправлять асинхронные запросы, используя магические методы, предоставляемые клиентом:

$client = new GuzzleHttp\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');

Вы также можете использовать методы sendAsync() и requestAsync() клиента:

// Создать объект запроса PSR-7 для отправки
$headers = ['X-Foo' => 'Bar'];
$body = 'Hello!';
$request = new Request('HEAD', 'http://httpbin.org/head', $headers, $body);
$promise = $client->sendAsync($request);

// Или, если вам не нужно передавать экземпляр запроса:
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');

$promise, возвращаемые этими методами, реализуют Promises/A+ spec, предоставленные библиотекой Guzzle promises. Это означает, что вы сможете связать цепочкой then() вызовы promise. Эти then затем реализуют либо успешный результат с помощью Psr\Http\Message\ResponseInterface, либо запрос отклоняется с выбросом исключения exception:

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

// Инициируем каждый запрос, но не блокируем
$promises = [
  'image' => $client->getAsync('/image'),
  'png'   => $client->getAsync('/image/png'),
  'jpeg'  => $client->getAsync('/image/jpeg'),
  'webp'  => $client->getAsync('/image/webp')
];

// Дождаться завершения всех запросов. Выдает исключение ConnectException
// если какой-либо из запросов не выполнен
$results = Promise\unwrap($promises);

// Дождемся завершения запросов, даже если некоторые из них завершатся неудачно
$results = Promise\settle($promises)->wait();

// Можно получить доступ к каждому результату, используя key, предоставленный для функции развертки
echo $results['image']['value']->getHeader('Content-Length')[0]
echo $results['png']['value']->getHeader('Content-Length')[0]

Можно использовать 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) {
  // Здесь обработка успешного запроса
  },
  'rejected' => function ($reason, $index) {
  // Здесь обработка запроса, который завершился неудачей
  },
]);

// Инициализируем трансферы и создаём promise
$promise = $pool->promise();

// Ожидаем ответ promise // Принудительно завершаем пул запросов
$promise->wait();

Или используя замыкание, которое вернет promise, как только пул вызовет замыкание:

$client = new Client();

$requests = function ($total) use ($client) {
  $uri = 'http://127.0.0.1:8126/guzzle-server/perf';
  for ($i = 0; $i < $total; $i++) {
    yield function() use ($client, $uri) {
      return $client->getAsync($uri);
    };
  }
};

$pool = new Pool($client, $requests(100));

Использование ответов

В предыдущих примерах мы получили $response или мы получили ответ от promise. Объект ответа реализует ответ PSR-7, Psr\Http\Message\ResponseInterface, и содержит много полезной информации.

Вы можете получить код состояния и фразу ответа:

$code = $response->getStatusCode(); // 200
$reason = $response->getReasonPhrase(); // OK

Вы можете извлечь заголовки из ответа:

// Проверка, есть ли заголовок
if ($response->hasHeader('Content-Length')) {
echo "It exists";
}

// Получаем заголовок из ответа
echo $response->getHeader('Content-Length')[0];

// Получаем все заголовки ответа
foreach ($response->getHeaders() as $name => $values) {
echo $name . ': ' . implode(', ', $values) . "\r\n";
}

Тело ответа может быть получено с помощью метода getBody(). Тело ответа может быть использовано как строка, приведено к строке или как объект, подобный потоку:

$body = $response->getBody();

// Неявно приводим тело ответа к строке и отображаем его
echo $body;

// Явное приведение тела к строке
$stringBody = (string) $body;

// Читаем первые 10 байт тела ответа
$tenBytes = $body->read(10);

// Прочитать оставшееся содержимое тела ответа как строку
$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 предоставляет несколько способов загрузки данных.

Можно отправлять запросы, содержащие поток данных, передавая строку, ресурс возвращается из fopen, или как экземпляр Psr\Http\Message\StreamInterface в опцию запроса 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
$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/Form запросы

В дополнение к указанию необработанных данных запроса с использованием опции запроса body, Guzzle предоставляет полезные абстракции при отправке данных POST.

Отправка полей формы

Для отправки POST-запросов application/x-www-form-urlencoded необходимо указать поля POST в виде массива в параметрах запроса form_params:

$response = $client->request('POST', 'http://httpbin.org/post', [
  'form_params' => [
  'field_name' => 'abc',
  'other_field' => '123',
  'nested_field' => [
    'nested' => 'hello'
   ]
  ]
]);

Отправка файлов форм

Можно отправлять файлы вместе с формой (POST-запросы multipart/form-data), используя опцию запроса multipart. Она принимает массив ассоциативных массивов, где каждый ассоциативный массив содержит следующие ключи:

  • name: (required, string) имя ключа поля формы.
  • contents: (required, mixed) Укажите тут строку для отправки содержимого файла в виде строки, предоставьте ресурс fopen для потоковой передачи содержимого из потока PHP или укажите Psr\Http\Message\StreamInterface для потоковой передачи содержимого из потока PSR-7.
$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 может поддерживать для вас сеанс файлов cookie, если это указано с помощью параметра запроса файлов cookie. При отправке запроса параметр cookie должен быть установлен на экземпляр GuzzleHttp\Cookie\CookieJarInterface:

// Использование конкретного cookie jar
$jar = new \GuzzleHttp\Cookie\CookieJar;
$r = $client->request('GET', 'http://httpbin.org/cookies', [
  'cookies' => $jar
]);

Можно установить для cookie значение true в конструкторе клиента, если хотите использовать общий файл cookie для всех запросов:

// Используем общий клиентский файл cookie
$client = new \GuzzleHttp\Client(['cookies' => true]);
$r = $client->request('GET', 'http://httpbin.org/cookies');

Редиректы (перенаправления)

Guzzle будет автоматически следовать за редиректами, только если чётко не указать этого не делать. Вы можете настроить поведение перенаправления, используя опцию запроса allow_redirects:

  • Установите значение true, чтобы включить нормальные перенаправления с максимальным количеством 5 перенаправлений. Это значение по умолчанию.
  • Установите в false, чтобы отключить перенаправления.
  • Передайте ассоциативный массив, содержащий ключ max, чтобы указать максимальное количество перенаправлений, и при необходимости укажите значение ключа strict, чтобы указать, следует ли использовать строгие перенаправления, совместимые с RFC (что означает запросы перенаправления POST со следующими запросами тоже типа POST, тогда как в обычном режиме большинство браузеров по умолчанию перенаправляют запросы POST со следующими запросами GET)./li>
$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\Psr7;
use GuzzleHttp\Exception\RequestException;

try {
  $client->request('GET', 'https://github.com/_abc_123_404');
} catch (RequestException $e) {
  echo Psr7\str($e->getRequest());
  if ($e->hasResponse()) {
    echo Psr7\str($e->getResponse());
  }
}

Исключение GuzzleHttp\Exception\ConnectException выдается в случае сетевой ошибки. Это исключение происходит от GuzzleHttp\Exception\RequestException. GuzzleHttp\Exception\ClientException выбрасывается для ошибок уровня 400, если для параметра запроса http_errors установлено значение true. Это исключение распространяется на 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 Psr7\str($e->getRequest());
  echo Psr7\str($e->getResponse());
}

GuzzleHttp\Exception\ServerException генерируется для ошибок уровня 500, если для параметра запроса http_errors установлено значение true. Это исключение происходит от GuzzleHttp\Exception\BadResponseException.

GuzzleHttp\Exception\TooManyRedirectsException генерируется, когда выполняется слишком много перенаправлений. Это исключение происходит от GuzzleHttp\Exception\RequestException.

Все вышеперечисленные исключения происходят из GuzzleHttp\Exception\TransferException.

Переменные окружения

Guzzle предоставляет несколько переменных среды, которые можно использовать для настройки поведения библиотеки:

GUZZLE_CURL_SELECT_TIMEOUT

Управляет продолжительностью в секундах, которую обработчик curl_multi_* будет использовать при выборе обработчика curl, используя curl_multi_select(). Некоторые системы имеют проблемы с реализацией PHP curl_multi_select(), где вызов этой функции всегда приводит к ожиданию максимальной продолжительности времени ожидания.

HTTP_PROXY

Определяет прокси для использования при отправке запросов по протоколу http.

Поскольку переменная HTTP_PROXY может содержать произвольный пользовательский ввод в некоторых (CGI) средах, эта переменная используется только в CLI SAPI. Смотрите https://httpoxy.org для получения дополнительной информации.

HTTPS_PROXY

Определяет прокси для использования при отправке запросов по протоколу https.

Соответствующие настройки php.ini

Guzzle может использовать настройки php.ini при настройке клиентов:

openssl.cafile

Указывает путь на диске к файлу CA (сертификат SSL/TLS безопасности) в формате PEM, который будет использоваться при отправке запросов через «https». Подробнее: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults

Опции запросов