Рано или поздно любой проект на Yii2 начинает общаться с внешним миром: отправлять данные в CRM, получать курсы валют, дергать API платежных систем. И тут на сцену выходит Guzzle — мощный HTTP-клиент, который уже встроен в Yii2 по умолчанию. В этой статье разберем, как подружить ваш проект с чужими сервисами без боли и лишних костылей.
Почему Guzzle, а не file_get_contents?
Многие начинающие разработчики используют file_get_contents() для запросов к API. Работает? Да. Но только пока запрос простой и сервер отвечает мгновенно. Стоит добавить заголовки, авторизацию или обработать ошибку — и начинается боль. Guzzle решает эти проблемы из коробки.
Вот что вы получаете с Guzzle:
- Удобная работа с заголовками, куками и авторизацией.
- Автоматическая обработка редиректов.
- Поддержка асинхронных запросов.
- Гибкая настройка таймаутов и повторных попыток.
- Понятные исключения при ошибках.
Yii2 уже включает Guzzle в зависимости, так что отдельно устанавливать его не нужно. Достаточно убедиться, что пакет guzzlehttp/guzzle есть в composer.json.
Базовый запрос: получаем данные из API
Начнем с простого. Допустим, нам нужно получить список пользователей с внешнего сервиса. Создадим компонент, который будет отвечать за интеграцию.
client = new Client([
'base_uri' => $this->baseUrl,
'timeout' => $this->timeout,
'headers' => [
'Accept' => 'application/json',
],
]);
}
public function getUsers()
{
try {
$response = $this->client->get('/users');
$data = json_decode($response->getBody()->getContents(), true);
return $data;
} catch (GuzzleException $e) {
Yii::error('API error: ' . $e->getMessage());
return null;
}
}
}
?>
Теперь достаточно зарегистрировать компонент в конфиге приложения и вызывать его методы из любого места.
// config/web.php
'components' => [
'externalApi' => [
'class' => 'app\components\ExternalApi',
'baseUrl' => 'https://api.example.com',
],
],
Использование в контроллере:
$users = Yii::$app->externalApi->getUsers();
if ($users === null) {
return $this->render('error', ['message' => 'Сервис недоступен']);
}
return $this->render('users', ['users' => $users]);
Отправляем данные: POST-запросы и авторизация
Часто API требует не только получать, но и отправлять данные. Например, создать заказ или обновить профиль. Guzzle позволяет легко отправлять JSON и добавлять токены авторизации.
public function createOrder(array $orderData)
{
try {
$response = $this->client->post('/orders', [
'json' => $orderData,
'headers' => [
'Authorization' => 'Bearer ' . $this->getToken(),
'Content-Type' => 'application/json',
],
]);
return json_decode($response->getBody()->getContents(), true);
} catch (GuzzleException $e) {
Yii::error('Order creation failed: ' . $e->getMessage());
throw new \Exception('Не удалось создать заказ');
}
}
private function getToken()
{
// Здесь можно кешировать токен, чтобы не запрашивать каждый раз
return Yii::$app->cache->getOrSet('api_token', function () {
$response = $this->client->post('/auth', [
'json' => [
'login' => Yii::$app->params['apiLogin'],
'password' => Yii::$app->params['apiPassword'],
],
]);
$data = json_decode($response->getBody()->getContents(), true);
return $data['token'];
}, 3600);
}
Обратите внимание на кеширование токена. Если API выдает токен на час, нет смысла запрашивать его перед каждым запросом. Yii::cache с методом getOrSet отлично с этим справляется.
Обработка ошибок и таймауты
Внешний сервис может упасть, зависнуть или вернуть ошибку 500. Ваша задача — не дать приложению упасть вместе с ним. Guzzle выбрасывает исключения, которые нужно правильно обрабатывать.
Вот основные типы исключений:
| Исключение | Когда возникает | Что делать |
|---|---|---|
| ConnectException | Сервер недоступен | Повторить запрос позже, залогировать |
| RequestException | Ошибка 4xx или 5xx | Разобрать тело ответа, показать пользователю |
| TooManyRedirectsException | Слишком много редиректов | Проверить настройки, ограничить количество |
| ServerException | Ошибка 5xx | Повторить запрос, уведомить администратора |
Чтобы не дублировать код обработки, можно создать middleware или обертку. Но самый простой способ — использовать метод request() с параметром http_errors => false, чтобы Guzzle не выбрасывал исключения на 4xx и 5xx, а возвращал ответ как есть.
$response = $this->client->request('GET', '/users', [
'http_errors' => false,
]);
if ($response->getStatusCode() >= 400) {
Yii::error('API returned error: ' . $response->getBody());
return null;
}
return json_decode($response->getBody()->getContents(), true);
Такой подход дает больше контроля. Вы сами решаете, как реагировать на ошибки.
Повторные запросы и асинхронность
Иногда сервис отвечает с ошибкой из-за временных проблем. В таких случаях помогает повторный запрос. Guzzle поддерживает middleware для retry.
use GuzzleHttp\Middleware;
use GuzzleHttp\HandlerStack;
$handlerStack = HandlerStack::create();
$handlerStack->push(Middleware::retry(
function ($retries, $request, $response, $exception) {
// Повторяем, если ошибка 500 или таймаут, но не больше 3 раз
if ($retries >= 3) {
return false;
}
if ($exception instanceof ConnectException) {
return true;
}
if ($response && $response->getStatusCode() >= 500) {
return true;
}
return false;
},
function ($retries) {
// Задержка перед повтором: 100ms, 200ms, 400ms
return 100 * (2 ** $retries);
}
));
$client = new Client([
'handler' => $handlerStack,
'base_uri' => 'https://api.example.com',
]);
Асинхронные запросы позволяют отправлять несколько запросов одновременно и не ждать каждого по отдельности. Это особенно полезно, когда нужно собрать данные из нескольких источников.
use GuzzleHttp\Promise;
$promises = [
'users' => $client->getAsync('/users'),
'orders' => $client->getAsync('/orders'),
];
$results = Promise\Utils::settle($promises)->wait();
foreach ($results as $key => $result) {
if ($result['state'] === 'fulfilled') {
$data[$key] = json_decode($result['value']->getBody(), true);
} else {
Yii::error("Failed to fetch $key: " . $result['reason']);
}
}
Такой подход экономит время: вместо последовательных запросов вы делаете их параллельно.
Что в итоге
Guzzle — это не просто HTTP-клиент, а полноценный инструмент для интеграции с внешними сервисами. В Yii2 он уже есть, так что не нужно изобретать велосипед. Выносите логику в компоненты, кешируйте токены, обрабатывайте ошибки и не забывайте про таймауты. Тогда ваш проект будет стабильно работать даже с самыми капризными API.
Главное правило: не доверяйте внешнему сервису. Он может упасть, ответить медленно или вернуть неожиданные данные. Ваша задача — сделать так, чтобы пользователь этого не заметил.

