📦 Guzzle HTTP Client
Guzzle é um cliente HTTP para PHP que oferece uma interface limpa, consistente e poderosa para fazer pedidos HTTP. Segue os padrões PSR-7 (HTTP Message Interface).
Vantagens do Guzzle
✅ Interface Limpa
API intuitiva e bem documentada. Código mais legível e fácil de manter.
✅ PSR-7 Compliant
Segue os padrões PHP-FIG. Mensagens HTTP interoperáveis.
✅ Middleware
Sistema de middleware poderoso para logging, retry, autenticação, etc.
✅ Assíncrono
Suporte a pedidos assíncronos e paralelos com Promises.
✅ Testável
Fácil de testar com mock handlers. Ideal para TDD.
�️ Extensível
Plugins e extensões para várias necessidades.
📥 Instalação
Via Composer
composer require guzzlehttp/guzzle
Requisitos
- PHP 7.2.5 ou superior
- Composer
- Extensão cURL do PHP (recomendado)
Autoload
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client();
?>
📝 Uso Básico
Criar Cliente
<?php
use GuzzleHttp\Client;
// Cliente básico
$client = new Client();
// Cliente com configurações base
$client = new Client([
'base_uri' => 'https://jsonplaceholder.typicode.com',
'timeout' => 5.0,
'headers' => [
'Accept' => 'application/json',
'User-Agent' => 'MeuApp/1.0'
]
]);
?>
Pedido GET Simples
<?php
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://jsonplaceholder.typicode.com'
]);
$response = $client->get('/posts/1');
// Código de status
echo $response->getStatusCode(); // 200
// Headers da resposta
echo $response->getHeaderLine('content-type'); // application/json
// Corpo da resposta
$body = $response->getBody();
$data = json_decode($body, true);
echo $data['title'];
?>
Guzzle suporta chamadas mágicas: $client->get(), $client->post(), $client->put(), $client->delete(), $client->patch().
🔧 Métodos HTTP
GET Request
<?php
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com']);
// Método 1: Usando método mágico
$response = $client->get('/posts/1');
// Método 2: Usando request()
$response = $client->request('GET', '/posts/1');
// Método 3: Usando getAsync para assíncrono
$promise = $client->getAsync('/posts/1');
$response = $promise->wait();
// Processar resposta
$data = json_decode($response->getBody(), true);
?>
POST Request
<?php
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com']);
$response = $client->post('/posts', [
'json' => [
'title' => 'Meu Post',
'body' => 'Conteúdo do post',
'userId' => 1
]
]);
// O Guzzle converte automaticamente para JSON
// e define Content-Type: application/json
$data = json_decode($response->getBody(), true);
echo "Post criado com ID: " . $data['id'];
?>
POST com Form Data
<?php
$response = $client->post('/submit', [
'form_params' => [
'nome' => 'João Silva',
'email' => 'joao@exemplo.com',
'mensagem' => 'Olá, mundo!'
]
]);
?>
PUT Request
<?php
$response = $client->put('/posts/1', [
'json' => [
'id' => 1,
'title' => 'Título Atualizado',
'body' => 'Conteúdo atualizado',
'userId' => 1
]
]);
?>
PATCH Request
<?php
// Atualização parcial
$response = $client->patch('/posts/1', [
'json' => [
'title' => 'Novo Título'
]
]);
?>
DELETE Request
<?php
$response = $client->delete('/posts/1');
if ($response->getStatusCode() === 200) {
echo "Post eliminado com sucesso!";
}
?>
🔍 Query Parameters
<?php
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com']);
// Query parameters
$response = $client->get('/posts', [
'query' => [
'userId' => 1,
'_limit' => 5
]
]);
// Isso gera: /posts?userId=1&_limit=5
$posts = json_decode($response->getBody(), true);
foreach ($posts as $post) {
echo $post['title'] . "\n";
}
?>
Guzzle faz encoding automático dos parâmetros. Caracteres especiais são convertidos corretamente.
📋 Headers
Headers por Pedido
<?php
$response = $client->get('/posts', [
'headers' => [
'Authorization' => 'Bearer SEU_TOKEN',
'X-API-Key' => 'sua_api_key',
'Accept' => 'application/json',
'X-Custom-Header' => 'valor'
]
]);
?>
Headers Globais (no Cliente)
<?php
$client = new Client([
'base_uri' => 'https://api.exemplo.com',
'headers' => [
'Authorization' => 'Bearer ' . $token,
'Accept' => 'application/json',
'User-Agent' => 'MeuApp/1.0'
]
]);
// Todos os pedidos usarão estes headers
$response = $client->get('/users');
?>
Ler Headers da Resposta
<?php
$response = $client->get('/posts');
// Obter um header específico
$contentType = $response->getHeaderLine('content-type');
// Obter todos os valores de um header (pode haver múltiplos)
$allContentTypes = $response->getHeader('content-type');
// Todos os headers como array
$headers = $response->getHeaders();
print_r($headers);
?>
📦 Trabalhar com JSON
Enviar JSON
<?php
// A opção 'json' converte automaticamente para JSON
// e define Content-Type: application/json
$response = $client->post('/posts', [
'json' => [
'title' => 'Meu Post',
'body' => 'Conteúdo',
'tags' => ['php', 'api', 'guzzle']
]
]);
?>
Receber JSON
<?php
$response = $client->get('/posts/1');
//getBody() retorna um stream que pode ser lido como string
$body = (string) $response->getBody();
// Decodificar JSON
$data = json_decode($body, true);
// ou como objeto
$data = json_decode($body);
// Acessar dados
echo $data['title'];
?>
🚨 Tratamento de Erros
Exceções do Guzzle
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ConnectException;
$client = new Client(['base_uri' => 'https://api.exemplo.com']);
try {
$response = $client->get('/endpoint');
// Sucesso
$data = json_decode($response->getBody(), true);
} catch (ConnectException $e) {
// Erro de conexão (DNS, timeout, etc.)
echo "Erro de conexão: " . $e->getMessage();
} catch (RequestException $e) {
// Erro HTTP (4xx, 5xx)
echo "Status: " . $e->getCode();
echo "Mensagem: " . $e->getMessage();
// Obter resposta de erro se disponível
if ($e->hasResponse()) {
$errorBody = (string) $e->getResponse()->getBody();
$errorData = json_decode($errorBody, true);
print_r($errorData);
}
} catch (GuzzleException $e) {
// Outros erros do Guzzle
echo "Erro: " . $e->getMessage();
}
?>
Desativar Exceções HTTP
<?php
// Por padrão, Guzzle lança exceção para 4xx e 5xx
// Podes desativar este comportamento:
$response = $client->get('/endpoint', [
'http_errors' => false
]);
// Agora deves verificar manualmente
$statusCode = $response->getStatusCode();
if ($statusCode >= 200 && $statusCode < 300) {
$data = json_decode($response->getBody(), true);
} elseif ($statusCode >= 400) {
echo "Erro HTTP: $statusCode";
}
?>
Tipos de Exceção
| Exceção | Quando Ocorre |
|---|---|
ConnectException |
Falha de conexão (DNS, timeout, rede) |
RequestException |
Erro no pedido (4xx, 5xx) |
ClientException |
Erros 4xx (cliente) |
ServerException |
Erros 5xx (servidor) |
TooManyRedirectsException |
Excedeu limite de redirects |
🔧 Middleware
Middleware permite interceptar e modificar pedidos e respostas.
Logging Middleware
<?php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// Criar logger
$logger = new Logger('api');
$logger->pushHandler(new StreamHandler('api.log'));
// Criar stack de handlers com middleware
$stack = HandlerStack::create();
// Adicionar middleware de logging
$stack->push(
Middleware::log(
$logger,
new MessageFormatter('{method} {uri} - {code} - {req_body}')
)
);
// Criar cliente com o stack
$client = new Client([
'base_uri' => 'https://jsonplaceholder.typicode.com',
'handler' => $stack
]);
// Todos os pedidos serão logados automaticamente
$response = $client->get('/posts/1');
?>
Retry Middleware
<?php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
$stack = HandlerStack::create();
// Retry até 3 vezes em caso de erro de conexão ou 5xx
$stack->push(Middleware::retry(
function ($retries, Request $request, Response $response = null, $exception = null) {
// Parar após 3 tentativas
if ($retries >= 3) {
return false;
}
// Retry em erros de conexão
if ($exception instanceof \GuzzleHttp\Exception\ConnectException) {
return true;
}
// Retry em erros 5xx
if ($response && $response->getStatusCode() >= 500) {
return true;
}
return false;
},
function ($retries) {
// Delay exponencial: 1s, 2s, 4s
return 1000 * pow(2, $retries);
}
));
$client = new Client(['handler' => $stack]);
?>
⚡ Pedidos Assíncronos
Pedido Assíncrono Único
<?php
// Fazer pedido sem bloquear
$promise = $client->getAsync('/posts/1');
// Fazer outras coisas...
// Esperar pela resposta quando necessário
$response = $promise->wait();
$data = json_decode($response->getBody(), true);
?>
Múltiplos Pedidos Paralelos
<?php
use GuzzleHttp\Promise\Utils;
// Criar múltiplos pedidos assíncronos
$promises = [
'post1' => $client->getAsync('/posts/1'),
'post2' => $client->getAsync('/posts/2'),
'users' => $client->getAsync('/users'),
'comments' => $client->getAsync('/comments?postId=1')
];
// Executar todos em paralelo
$results = Utils::settle($promises)->wait();
// Processar resultados
foreach ($results as $key => $result) {
if ($result['state'] === 'fulfilled') {
$data = json_decode($result['value']->getBody(), true);
echo "$key: " . count($data) . " itens\n";
} else {
echo "$key: Erro - " . $result['reason']->getMessage() . "\n";
}
}
?>
Pedidos assíncronos em paralelo são muito mais rápidos quando precisas de fazer múltiplas chamadas a APIs diferentes!
📝 Exercícios
Exercício 1: Cliente com Headers
Desafio: Cria um cliente Guzzle com base_uri, timeout de 10 segundos e header de autorização.
<?php
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://jsonplaceholder.typicode.com',
'timeout' => 10.0,
'headers' => [
'Authorization' => 'Bearer meu-token',
'Accept' => 'application/json'
]
]);
$response = $client->get('/posts/1');
$data = json_decode($response->getBody(), true);
echo $data['title'];
?>
Exercício 2: Tratamento de Erros
Desafio: Faz um pedido com tratamento de erros de conexão e HTTP.
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ConnectException;
$client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com']);
try {
$response = $client->get('/posts/1');
$data = json_decode($response->getBody(), true);
echo $data['title'];
} catch (ConnectException $e) {
echo "Erro de conexão: " . $e->getMessage();
} catch (RequestException $e) {
echo "Erro HTTP: " . $e->getCode();
if ($e->hasResponse()) {
$error = json_decode($e->getResponse()->getBody(), true);
print_r($error);
}
}
?>
Exercício 3: Pedidos Paralelos
Desafio: Faz 3 pedidos GET em paralelo usando promises.
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Promise\Utils;
$client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com']);
$promises = [
'post' => $client->getAsync('/posts/1'),
'user' => $client->getAsync('/users/1'),
'comments' => $client->getAsync('/comments?postId=1')
];
$results = Utils::settle($promises)->wait();
$post = json_decode($results['post']['value']->getBody(), true);
$user = json_decode($results['user']['value']->getBody(), true);
$comments = json_decode($results['comments']['value']->getBody(), true);
echo "Post: {$post['title']}\n";
echo "User: {$user['name']}\n";
echo "Comments: " . count($comments) . "\n";
?>