guzzle/guzzle
Guzzle 3.x is a PHP HTTP client and web service framework with a cURL-like API, persistent connections, pooling, parallel requests, service descriptions, and a Symfony2 event/plugin system. End-of-life; use Guzzle 5+ for maintenance.
Installation
composer require guzzlehttp/guzzle
First Request
use GuzzleHttp\Client;
$client = new Client();
$response = $client->get('https://api.example.com/data');
$body = $response->getBody()->getContents();
Laravel Integration
config/app.php (if not auto-discovered):
'providers' => [
GuzzleHttp\GuzzleServiceProvider::class,
],
public function __construct(private Client $client) {}
// In a Laravel controller/service
$response = $this->client->request('GET', 'https://api.github.com/users/octocat');
$status = $response->getStatusCode();
$json = json_decode($response->getBody(), true);
config/services.php):
'guzzle' => [
'timeout' => 10.0,
'base_uri' => env('API_BASE_URL', 'https://api.example.com'),
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . env('API_TOKEN'),
],
],
AppServiceProvider:
$this->app->singleton(Client::class, function ($app) {
return new Client($app['config']['services.guzzle']);
});
use GuzzleHttp\Promise\Utils;
$promises = [
$this->client->getAsync('https://api1.example.com/data'),
$this->client->postAsync('https://api2.example.com/data', ['json' => [...]]),
];
Utils::settle($promises)->wait();
$stack = HandlerStack::create();
$stack->push(Middleware::tap(function ($request) {
Log::debug('Request:', ['url' => $request->getUri()]);
}));
$client = new Client(['handler' => $stack]);
GuzzleHttp\Middleware for retries:
use GuzzleHttp\Middleware;
$retryMiddleware = Middleware::retry(
function ($retries, $request, $exception) {
return $retries < 3 && in_array($exception->getCode(), [429, 500, 502, 503, 504]);
},
function ($retries) {
return 100 * $retries; // Exponential backoff
}
);
$response = $this->client->get('https://example.com/large-file.zip');
file_put_contents('download.zip', $response->getBody());
Http facade for consistency:
// app/Extensions/HttpClient.php
namespace App\Extensions;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Http;
class HttpClient extends Client {
public function laravelRequest(string $method, string $url, array $options = []) {
return Http::withOptions($options)->{$method}($url);
}
}
StripeClient, GitHubClient) with typed methods:
class GitHubClient {
public function __construct(private Client $client) {}
public function getUser(string $username): array {
$response = $this->client->get("https://api.github.com/users/{$username}");
return json_decode($response->getBody(), true);
}
}
$stack = HandlerStack::create();
$stack->push(Middleware::tap(function ($request) {
event(new RequestSent($request));
}));
$stack->push(Middleware::tap(function ($response) {
event(new ResponseReceived($response));
}));
$cacheKey = "api_data_{$url}";
$data = cache()->remember($cacheKey, now()->addHours(1), function () use ($client, $url) {
return $client->get($url)->json();
});
$mock = $this->getMockBuilder(Client::class)
->disableOriginalConstructor()
->onlyMethods(['get'])
->getMock();
$mock->method('get')->willReturn((new MockResponse('{"data": "test"}'));
$this->app->instance(Client::class, $mock);
Client instance is singleton to avoid connection leaks:
// Bad: Creates a new client per request (leaks connections)
$client = new Client();
// Good: Singleton in container
$this->app->singleton(Client::class, function () {
return new Client(['timeout' => 10]);
});
// Wrong: Assumes JSON (throws exception if not JSON)
$data = $response->getBody();
// Right:
$data = json_decode($response->getBody(), true);
$client = new Client(['timeout' => 5.0]); // 5 seconds
$client = new Client(['verify' => false]); // UNSAFE!
$client = new Client(['verify' => '/path/to/cert.pem']);
getAsync) return PromiseInterface. Always handle with wait() or then():
// Bad: Unhandled promise
$this->client->getAsync('...');
// Good:
$promise = $this->client->getAsync('...');
$promise->then(function ($response) { /* ... */ });
CacheMiddleware) conflicts with Laravel’s cache. Avoid mixing them.$stack->push(Middleware::tap(function ($request, $response) {
if ($response->getStatusCode() >= 400) {
Log::error('API Error', [
'url' => $request->getUri(),
'status' => $response->getStatusCode(),
'body' => (string) $response->getBody(),
]);
}
}));
GuzzleHttp\HandlerStack to log:
$stack->push(Middleware::tap(function ($request) {
Log::debug('Request', [
'url' => (string) $request->getUri(),
'headers' => $request->getHeaders(),
'body' => $request->getBody() ? $request->getBody()->getContents() : null,
]);
}));
$client = new Client(['allow_redirects' => false]);
response->getHeader('Location') for redirect URLs..env:
GUZZLE_PROXY=http://proxy.example.com:8080
$client = new Client(['proxy' => env('GUZZLE_PROXY')]);
GuzzleHttp\Promise\PromiseInterface for custom logic:
$handler = new CustomHandler();
$client = new Client
How can I help you explore Laravel packages today?