📦 Guzzle HTTP Client

💡 O que é Guzzle?

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

Terminal
composer require guzzlehttp/guzzle

Requisitos

  • PHP 7.2.5 ou superior
  • Composer
  • Extensão cURL do PHP (recomendado)

Autoload

PHP - Incluir Autoload
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client();
?>

📝 Uso Básico

Criar Cliente

Instanciar 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

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'];
?>
💡 Método Mágico

Guzzle suporta chamadas mágicas: $client->get(), $client->post(), $client->put(), $client->delete(), $client->patch().

🔧 Métodos HTTP

GET Request

GET com Guzzle
<?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

POST com JSON
<?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

Form Data
<?php
$response = $client->post('/submit', [
    'form_params' => [
        'nome' => 'João Silva',
        'email' => 'joao@exemplo.com',
        'mensagem' => 'Olá, mundo!'
    ]
]);
?>

PUT Request

PUT
<?php
$response = $client->put('/posts/1', [
    'json' => [
        'id' => 1,
        'title' => 'Título Atualizado',
        'body' => 'Conteúdo atualizado',
        'userId' => 1
    ]
]);
?>

PATCH Request

PATCH
<?php
// Atualização parcial
$response = $client->patch('/posts/1', [
    'json' => [
        'title' => 'Novo Título'
    ]
]);
?>

DELETE Request

DELETE
<?php
$response = $client->delete('/posts/1');

if ($response->getStatusCode() === 200) {
    echo "Post eliminado com sucesso!";
}
?>

🔍 Query Parameters

Parâmetros na URL
<?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";
}
?>
💡 Encoding Automático

Guzzle faz encoding automático dos parâmetros. Caracteres especiais são convertidos corretamente.

📋 Headers

Headers por Pedido

Headers Personalizados
<?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)

Headers Padrão
<?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

Headers de 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

Opção '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

Processar Resposta
<?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

Try/Catch
<?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

http_errors => false
<?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

Middleware de Log
<?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

Retry Automático
<?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

getAsync
<?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

Pedidos em Paralelo
<?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";
    }
}
?>
💡 Performance

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";
?>