guzzlehttp/guzzle
Guzzle is a PHP HTTP client for sending sync or async requests with an easy API. Built on PSR-7 and PSR-18, supports middleware, cookies, streaming uploads/downloads, and JSON. Transport-agnostic for flexible integrations.
Install via Composer: composer require guzzlehttp/guzzle. Start with the GuzzleHttp\Client for basic HTTP interactions—synchronous or asynchronous. The simplest first use case remains fetching JSON from an API endpoint:
use GuzzleHttp\Client;
$client = new Client();
$response = $client->get('https://api.example.com/users/123');
$data = json_decode($response->getBody(), true);
Key things to explore first:
getResponse(), getBody(), etc.).sendAsync() or requestAsync() for non-blocking operations (requires handling promises).Middleware for cross-cutting concerns: Add logging, retry logic, or authentication via middleware stacking. Example:
$client = new Client([
'handler' => HandlerStack::create()->push(Middleware::retry($retryDecider))
]);
$handlerStack->remove('middleware_name') (fixed in 7.10.3 to handle callable strings correctly).Per-request overrides: Pass options like ['json' => $payload], ['headers' => ['Authorization' => 'Bearer ...']], or ['sink' => $stream] to request(), get(), post(), etc.
Batching requests: Use promises with Promise\Utils::all() or GuzzleHttp\Pool for concurrent requests:
$pool = new Pool($client, $generators, [
'concurrency' => 5,
'fulfilled' => function ($response, $id) { /* handle */ },
'rejected' => function ($response, $id) { /* handle */ },
]);
$pool->promise()->wait();
Integration with frameworks: Laravel uses Guzzle via Http::client() (Symfony HttpKernel-based). In tests, mock responses with MockHandler and middleware.
Middleware order matters: Push middleware in the order you want them applied during request creation (outer to inner). Middleware added later via push() wraps earlier ones.
$handlerStack->remove('middleware_name') even if the name is a callable string.Invalid HTTP headers: New in 7.10.3, invalid header lines now throw clearer exceptions (e.g., malformed Content-Length). Validate headers before sending requests.
Stream rewinding: Body streams must be rewindable for retries. Use Stream::factory($content) for StringBody to ensure rewindability.
Timeout pitfalls: connect_timeout (DNS + TCP handshake) and timeout (total request duration) are distinct—configure both explicitly for reliability.
Async ≠ concurrent by default: Promises execute serially unless you use Pool or utils::unwrap() with a concurrency-supporting handler stack (e.g., cURL multi).
Debugging:
curl.debug or debug handler option to see raw HTTP.dd($response->getStatusCode(), $response->getHeaders()).Configuration via environment: Use GuzzleHttp\Client with base_uri and inject config via dependency injection.
Protocol version handling: Empty request protocol versions are now treated as HTTP/1.1 (fixed in 7.10.3). Explicitly set HTTP_VERSION => '1.1' if needed.
PSR-18 interoperability: ClientInterface from psr/http-client is fully supported—ideal for decoupling from Guzzle-specific APIs.
How can I help you explore Laravel packages today?