Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Guzzle
https://github.com/guzzle/guzzle
Admin
Guzzle is a PHP HTTP client library that makes it easy to send HTTP requests and integrate with web
...
Tokens:
43,774
Snippets:
331
Trust Score:
9
Update:
1 month ago
Context
Skills
Chat
Benchmark
69.2
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Guzzle Guzzle is a PHP HTTP client library that makes it easy to send HTTP requests and integrate with web services. It provides a simple interface for building query strings, POST requests, streaming large uploads and downloads, using HTTP cookies, and uploading JSON data. Guzzle utilizes PSR-7 message interfaces, allowing seamless interoperability with any other PSR-7 compliant library. The library supports both synchronous and asynchronous request sending, connection pooling for concurrent requests, and a middleware system for customizing client behavior. Guzzle can use cURL for sending requests when available, but also works with PHP's stream wrappers as a fallback. With built-in support for cookies, redirects, authentication, and various body formats (JSON, form data, multipart), Guzzle handles most HTTP communication needs out of the box. ## Creating a Client The `GuzzleHttp\Client` class is the main interface for sending requests. Initialize it with a base URI and default request options that apply to all requests. ```php use GuzzleHttp\Client; // Create a client with base URI and default options $client = new Client([ 'base_uri' => 'https://api.example.com', 'timeout' => 2.0, 'headers' => [ 'User-Agent' => 'MyApp/1.0', 'Accept' => 'application/json', ] ]); // Requests use the base URI $response = $client->request('GET', '/users'); // https://api.example.com/users $response = $client->request('GET', '/users/123'); // https://api.example.com/users/123 $response = $client->request('GET', 'https://other.com/api'); // Absolute URLs override base_uri ``` ## Sending GET Requests Use the `get()` method or `request('GET', ...)` to fetch resources. Query parameters can be passed inline or via the `query` option. ```php use GuzzleHttp\Client; $client = new Client(['base_uri' => 'https://httpbin.org']); // Simple GET request $response = $client->get('/get'); echo $response->getStatusCode(); // 200 echo $response->getReasonPhrase(); // OK // GET with query parameters $response = $client->get('/get', [ 'query' => [ 'foo' => 'bar', 'baz' => ['hi', 'there'] ] ]); // Request URL: /get?foo=bar&baz%5B0%5D=hi&baz%5B1%5D=there // Reading response body $body = $response->getBody(); echo $body; // Full body as string $data = json_decode($body, true); // Parse JSON response // Accessing headers echo $response->getHeader('Content-Type')[0]; // application/json foreach ($response->getHeaders() as $name => $values) { echo $name . ': ' . implode(', ', $values) . "\n"; } ``` ## Sending POST Requests with Form Data Submit form data using the `form_params` option for URL-encoded data or `multipart` for file uploads. ```php use GuzzleHttp\Client; use GuzzleHttp\Psr7; $client = new Client(['base_uri' => 'https://httpbin.org']); // URL-encoded form POST (application/x-www-form-urlencoded) $response = $client->post('/post', [ 'form_params' => [ 'username' => 'john_doe', 'password' => 'secret123', 'remember' => true, 'tags' => ['php', 'guzzle'] // Nested arrays supported ] ]); // Multipart form POST with file upload (multipart/form-data) $response = $client->post('/post', [ 'multipart' => [ [ 'name' => 'field_name', 'contents' => 'field_value' ], [ 'name' => 'file_upload', 'contents' => Psr7\Utils::tryFopen('/path/to/file.pdf', 'r'), 'filename' => 'document.pdf', 'headers' => ['Content-Type' => 'application/pdf'] ], [ 'name' => 'avatar', 'contents' => Psr7\Utils::tryFopen('/path/to/image.jpg', 'r'), 'filename' => 'avatar.jpg' ] ] ]); echo $response->getStatusCode(); // 200 ``` ## Sending JSON Data Use the `json` option to automatically encode data as JSON and set the appropriate Content-Type header. ```php use GuzzleHttp\Client; $client = new Client([ 'base_uri' => 'https://api.example.com', 'headers' => ['Accept' => 'application/json'] ]); // POST JSON data $response = $client->post('/users', [ 'json' => [ 'name' => 'John Doe', 'email' => 'john@example.com', 'roles' => ['admin', 'user'], 'profile' => [ 'age' => 30, 'country' => 'US' ] ] ]); // Content-Type: application/json is automatically set // Body: {"name":"John Doe","email":"john@example.com",...} // PUT JSON data for updates $response = $client->put('/users/123', [ 'json' => [ 'name' => 'Jane Doe', 'email' => 'jane@example.com' ] ]); // PATCH for partial updates $response = $client->patch('/users/123', [ 'json' => ['status' => 'active'] ]); ``` ## Asynchronous Requests Send non-blocking requests using async methods. Responses are returned as promises that can be chained with callbacks. ```php use GuzzleHttp\Client; use GuzzleHttp\Promise; use Psr\Http\Message\ResponseInterface; use GuzzleHttp\Exception\RequestException; $client = new Client(['base_uri' => 'https://httpbin.org']); // Single async request with promise callbacks $promise = $client->getAsync('/get'); $promise->then( function (ResponseInterface $response) { echo 'Success: ' . $response->getStatusCode() . "\n"; echo $response->getBody(); }, function (RequestException $e) { echo 'Error: ' . $e->getMessage() . "\n"; if ($e->hasResponse()) { echo $e->getResponse()->getBody(); } } ); // Wait for the request to complete $promise->wait(); // Multiple concurrent requests $promises = [ 'users' => $client->getAsync('/get?resource=users'), 'posts' => $client->getAsync('/get?resource=posts'), 'comments' => $client->getAsync('/get?resource=comments'), ]; // Wait for all requests (throws on any failure) $responses = Promise\Utils::unwrap($promises); echo $responses['users']->getBody(); echo $responses['posts']->getBody(); // Wait for all, including failures (returns state and value/reason) $results = Promise\Utils::settle($promises)->wait(); foreach ($results as $key => $result) { if ($result['state'] === 'fulfilled') { echo "$key succeeded: " . $result['value']->getStatusCode() . "\n"; } else { echo "$key failed: " . $result['reason']->getMessage() . "\n"; } } ``` ## Connection Pooling with Pool Use `GuzzleHttp\Pool` to send a large number of requests concurrently with controlled concurrency. ```php use GuzzleHttp\Client; use GuzzleHttp\Pool; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Exception\RequestException; $client = new Client(); // Generator function yielding requests $requests = function ($total) { for ($i = 1; $i <= $total; $i++) { yield new Request('GET', "https://httpbin.org/get?page=$i"); } }; // Alternative: generator with closures for lazy request creation $requestsLazy = function ($total) use ($client) { for ($i = 1; $i <= $total; $i++) { yield function() use ($client, $i) { return $client->getAsync("https://httpbin.org/get?page=$i"); }; } }; $pool = new Pool($client, $requests(100), [ 'concurrency' => 5, // Max concurrent requests 'fulfilled' => function (Response $response, $index) { echo "Request $index completed: " . $response->getStatusCode() . "\n"; }, 'rejected' => function (RequestException $reason, $index) { echo "Request $index failed: " . $reason->getMessage() . "\n"; }, ]); // Initiate transfers and wait for completion $promise = $pool->promise(); $promise->wait(); ``` ## Error Handling and Exceptions Guzzle throws specific exceptions for different error types. Use `http_errors` option to control exception behavior. ```php use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; // 4xx errors use GuzzleHttp\Exception\ServerException; // 5xx errors use GuzzleHttp\Exception\ConnectException; // Network errors use GuzzleHttp\Exception\RequestException; // All request errors use GuzzleHttp\Exception\TransferException; // Base exception use GuzzleHttp\Psr7\Message; $client = new Client(['base_uri' => 'https://httpbin.org']); // Default: exceptions thrown on HTTP errors (4xx, 5xx) try { $response = $client->get('/status/404'); } catch (ClientException $e) { // 4xx client errors echo 'Client error: ' . $e->getResponse()->getStatusCode() . "\n"; echo 'Request: ' . Message::toString($e->getRequest()) . "\n"; echo 'Response: ' . Message::toString($e->getResponse()) . "\n"; } catch (ServerException $e) { // 5xx server errors echo 'Server error: ' . $e->getResponse()->getStatusCode() . "\n"; } catch (ConnectException $e) { // Network failures (DNS, timeout, connection refused) echo 'Connection error: ' . $e->getMessage() . "\n"; } catch (RequestException $e) { // Catch-all for request errors echo 'Request failed: ' . $e->getMessage() . "\n"; } // Disable automatic exceptions - check status manually $response = $client->get('/status/500', ['http_errors' => false]); if ($response->getStatusCode() >= 400) { echo 'Error response: ' . $response->getStatusCode() . "\n"; echo $response->getBody(); } ``` ## Authentication Configure HTTP authentication using the `auth` option. Supports Basic, Digest, and NTLM authentication. ```php use GuzzleHttp\Client; $client = new Client(['base_uri' => 'https://api.example.com']); // Basic authentication (default) $response = $client->get('/protected', [ 'auth' => ['username', 'password'] ]); // Explicit Basic auth $response = $client->get('/protected', [ 'auth' => ['username', 'password', 'basic'] ]); // Digest authentication $response = $client->get('/protected', [ 'auth' => ['username', 'password', 'digest'] ]); // NTLM authentication (Windows) $response = $client->get('/protected', [ 'auth' => ['username', 'password', 'ntlm'] ]); // Bearer token via headers $response = $client->get('/api/resource', [ 'headers' => [ 'Authorization' => 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' ] ]); // Client-wide default authentication $client = new Client([ 'base_uri' => 'https://api.example.com', 'auth' => ['api_user', 'api_secret'] ]); ``` ## Cookie Handling Manage cookies using `CookieJar` implementations for session persistence across requests. ```php use GuzzleHttp\Client; use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Cookie\FileCookieJar; use GuzzleHttp\Cookie\SessionCookieJar; // In-memory cookie jar $jar = new CookieJar(); $client = new Client(['cookies' => $jar]); // Make requests - cookies automatically stored and sent $client->get('https://httpbin.org/cookies/set/session_id/abc123'); $response = $client->get('https://httpbin.org/cookies'); // Response shows cookies being sent // Pre-populate cookies $jar = CookieJar::fromArray([ 'session_id' => 'abc123', 'user_token' => 'xyz789' ], 'httpbin.org'); $client = new Client(['cookies' => $jar]); // Access specific cookie $cookie = $jar->getCookieByName('session_id'); echo $cookie->getValue(); // abc123 echo $cookie->getDomain(); // httpbin.org echo $cookie->getExpires(); // Unix timestamp // File-based cookie jar (persists across script executions) $jar = new FileCookieJar('/path/to/cookies.json', true); $client = new Client(['cookies' => $jar]); // Session-based cookie jar (uses $_SESSION) $jar = new SessionCookieJar('session_key', true); $client = new Client(['cookies' => $jar]); // Enable shared cookie jar for all client requests $client = new Client(['cookies' => true]); ``` ## Redirect Handling Control redirect behavior with the `allow_redirects` option. Track redirect history and customize redirect handling. ```php use GuzzleHttp\Client; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\UriInterface; $client = new Client(); // Default: follow up to 5 redirects $response = $client->get('http://httpbin.org/redirect/3'); echo $response->getStatusCode(); // 200 (final response) // Disable redirects $response = $client->get('http://httpbin.org/redirect/3', [ 'allow_redirects' => false ]); echo $response->getStatusCode(); // 302 // Advanced redirect configuration $response = $client->get('http://httpbin.org/redirect/3', [ 'allow_redirects' => [ 'max' => 10, // Maximum redirects 'strict' => true, // Strict RFC redirects (POST stays POST) 'referer' => true, // Add Referer header 'protocols' => ['http', 'https'], // Allowed protocols 'track_redirects' => true, // Track redirect history 'on_redirect' => function ( RequestInterface $request, ResponseInterface $response, UriInterface $uri ) { echo "Redirecting from {$request->getUri()} to {$uri}\n"; } ] ]); // Access redirect history echo $response->getHeaderLine('X-Guzzle-Redirect-History'); // http://httpbin.org/relative-redirect/2, http://httpbin.org/relative-redirect/1 echo $response->getHeaderLine('X-Guzzle-Redirect-Status-History'); // 302, 302 ``` ## Timeout Configuration Set connection and request timeouts to prevent requests from hanging indefinitely. ```php use GuzzleHttp\Client; use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\RequestException; // Client-wide timeout defaults $client = new Client([ 'base_uri' => 'https://httpbin.org', 'timeout' => 30.0, // Total request timeout (seconds) 'connect_timeout' => 5.0, // Connection timeout (seconds) ]); // Per-request timeout override try { $response = $client->get('/delay/10', [ 'timeout' => 3.0 // Will timeout after 3 seconds ]); } catch (RequestException $e) { echo "Request timed out\n"; } // Connection timeout only try { $response = $client->get('/get', [ 'connect_timeout' => 2.0 // Timeout if can't connect in 2 seconds ]); } catch (ConnectException $e) { echo "Could not connect: " . $e->getMessage() . "\n"; } // Streaming response with read timeout $response = $client->get('/stream/50', [ 'stream' => true, 'read_timeout' => 10 // Timeout for reading streamed data ]); $body = $response->getBody(); while (!$body->eof()) { echo $body->read(1024); } ``` ## Proxy Configuration Route requests through HTTP proxies for different protocols or specific hosts. ```php use GuzzleHttp\Client; // Single proxy for all requests $client = new Client([ 'proxy' => 'http://proxy.example.com:8080' ]); // Protocol-specific proxies $client = new Client([ 'proxy' => [ 'http' => 'http://proxy.example.com:8080', 'https' => 'http://secure-proxy.example.com:8080', 'no' => ['.example.com', 'localhost', '192.168.*'] // Bypass proxy ] ]); // Authenticated proxy $client = new Client([ 'proxy' => 'http://username:password@proxy.example.com:8080' ]); // Per-request proxy override $response = $client->get('/get', [ 'proxy' => 'http://different-proxy.example.com:3128' ]); // Disable proxy for specific request $response = $client->get('/get', [ 'proxy' => '' ]); ``` ## SSL/TLS Configuration Configure SSL certificate verification and client certificates for secure connections. ```php use GuzzleHttp\Client; // Default: verify SSL using system CA bundle $client = new Client(); // Custom CA bundle $client = new Client([ 'verify' => '/path/to/ca-bundle.crt' ]); // Disable SSL verification (NOT recommended for production) $client = new Client([ 'verify' => false ]); // Client certificate authentication $client = new Client([ 'cert' => '/path/to/client-cert.pem' ]); // Client certificate with password $client = new Client([ 'cert' => ['/path/to/client-cert.pem', 'certificate_password'] ]); // Private SSL key $client = new Client([ 'ssl_key' => '/path/to/private-key.pem' ]); // Private key with password $client = new Client([ 'ssl_key' => ['/path/to/private-key.pem', 'key_password'] ]); // Minimum TLS version (PHP 7.4+ for TLS 1.3) $client = new Client([ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT ]); ``` ## Streaming Responses Handle large responses efficiently by streaming data instead of loading entirely into memory. ```php use GuzzleHttp\Client; use GuzzleHttp\Psr7; $client = new Client(); // Stream response body $response = $client->get('https://httpbin.org/stream/100', [ 'stream' => true ]); $body = $response->getBody(); while (!$body->eof()) { $chunk = $body->read(1024); echo $chunk; // Process chunk... } // Save response directly to file $client->get('https://httpbin.org/image/png', [ 'sink' => '/path/to/downloaded-image.png' ]); // Save to file using resource $resource = Psr7\Utils::tryFopen('/path/to/file.txt', 'w'); $client->get('https://httpbin.org/stream/50', [ 'sink' => $resource ]); // Save to PSR-7 stream $stream = Psr7\Utils::streamFor(Psr7\Utils::tryFopen('/tmp/output.txt', 'w')); $client->get('https://httpbin.org/stream/50', [ 'sink' => $stream ]); // Check headers before downloading body $client->get('https://httpbin.org/stream/1024', [ 'on_headers' => function ($response) { $contentLength = $response->getHeaderLine('Content-Length'); if ($contentLength > 1024 * 1024 * 100) { // 100MB limit throw new \Exception('File too large!'); } } ]); ``` ## Request and Response Debugging Enable debug output and collect transfer statistics for troubleshooting. ```php use GuzzleHttp\Client; use GuzzleHttp\TransferStats; $client = new Client(); // Enable verbose cURL output $response = $client->get('https://httpbin.org/get', [ 'debug' => true // Outputs to STDOUT ]); // Debug output to file $response = $client->get('https://httpbin.org/get', [ 'debug' => fopen('/tmp/guzzle-debug.log', 'w') ]); // Collect transfer statistics $response = $client->get('https://httpbin.org/get', [ 'on_stats' => function (TransferStats $stats) { echo "URL: " . $stats->getEffectiveUri() . "\n"; echo "Transfer time: " . $stats->getTransferTime() . " seconds\n"; // Handler-specific stats (cURL info) $handlerStats = $stats->getHandlerStats(); if (isset($handlerStats['total_time'])) { echo "Total time: " . $handlerStats['total_time'] . "\n"; echo "DNS lookup: " . $handlerStats['namelookup_time'] . "\n"; echo "Connect time: " . $handlerStats['connect_time'] . "\n"; } if ($stats->hasResponse()) { echo "Status: " . $stats->getResponse()->getStatusCode() . "\n"; } else { echo "Error: " . $stats->getHandlerErrorData() . "\n"; } } ]); // Track download/upload progress $response = $client->get('https://httpbin.org/bytes/10000', [ 'progress' => function ($downloadTotal, $downloaded, $uploadTotal, $uploaded) { if ($downloadTotal > 0) { $percent = round(($downloaded / $downloadTotal) * 100); echo "Downloaded: $percent%\n"; } } ]); ``` ## Middleware and Handlers Customize request/response processing with middleware. Create custom handlers for testing or specialized behavior. ```php use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use GuzzleHttp\Handler\CurlHandler; use GuzzleHttp\Middleware; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; // Create custom handler stack $stack = new HandlerStack(); $stack->setHandler(new CurlHandler()); // Add middleware to modify requests $stack->push(Middleware::mapRequest(function (RequestInterface $request) { return $request ->withHeader('X-Request-Id', uniqid()) ->withHeader('X-Timestamp', time()); })); // Add middleware to modify responses $stack->push(Middleware::mapResponse(function (ResponseInterface $response) { return $response->withHeader('X-Processed', 'true'); })); // Add logging middleware $stack->push(Middleware::tap( function (RequestInterface $request, array $options) { echo "Sending: " . $request->getMethod() . " " . $request->getUri() . "\n"; }, function (RequestInterface $request, array $options, $response) { echo "Received: " . $response->getStatusCode() . "\n"; } )); // Named middleware for ordering $stack->push(Middleware::mapRequest(function ($r) { return $r->withHeader('X-Foo', 'bar'); }), 'add_foo'); $stack->before('add_foo', Middleware::mapRequest(function ($r) { return $r->withHeader('X-Before', 'value'); }), 'before_foo'); $stack->after('add_foo', Middleware::mapRequest(function ($r) { return $r->withHeader('X-After', 'value'); })); // Remove middleware $stack->remove('add_foo'); $client = new Client(['handler' => $stack]); $response = $client->get('https://httpbin.org/get'); ``` ## Mock Handler for Testing Use `MockHandler` to simulate HTTP responses in unit tests without making real network requests. ```php use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Middleware; // Create mock handler with queued responses $mock = new MockHandler([ new Response(200, ['Content-Type' => 'application/json'], '{"id": 1, "name": "Test"}'), new Response(201, ['Location' => '/users/2']), new Response(400, [], '{"error": "Validation failed"}'), new RequestException('Connection timeout', new Request('GET', '/timeout')) ]); $handlerStack = HandlerStack::create($mock); // Add history middleware to capture requests $container = []; $history = Middleware::history($container); $handlerStack->push($history); $client = new Client(['handler' => $handlerStack]); // First request gets first queued response $response = $client->get('/users/1'); echo $response->getStatusCode(); // 200 echo $response->getBody(); // {"id": 1, "name": "Test"} // Second request gets second queued response $response = $client->post('/users', ['json' => ['name' => 'New User']]); echo $response->getStatusCode(); // 201 // Inspect captured requests echo count($container); // 2 echo $container[0]['request']->getMethod(); // GET echo $container[0]['request']->getUri(); // /users/1 echo $container[1]['request']->getMethod(); // POST // Reset mock queue $mock->reset(); $mock->append( new Response(200, [], 'Fresh response') ); // Add more responses to queue $mock->append(new Response(204)); ``` ## History Middleware for Request Inspection Track all requests and responses sent by a client for debugging or testing assertions. ```php use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Psr7\Response; $container = []; $history = Middleware::history($container); $mock = new MockHandler([ new Response(200, [], 'Response 1'), new Response(201, [], 'Response 2'), ]); $stack = HandlerStack::create($mock); $stack->push($history); $client = new Client(['handler' => $stack]); // Make some requests $client->get('/endpoint1', ['query' => ['foo' => 'bar']]); $client->post('/endpoint2', ['json' => ['key' => 'value']]); // Inspect all transactions echo "Total requests: " . count($container) . "\n"; foreach ($container as $index => $transaction) { $request = $transaction['request']; $response = $transaction['response']; $error = $transaction['error'] ?? null; $options = $transaction['options']; echo "Request $index:\n"; echo " Method: " . $request->getMethod() . "\n"; echo " URI: " . $request->getUri() . "\n"; echo " Headers: " . json_encode($request->getHeaders()) . "\n"; echo " Body: " . $request->getBody() . "\n"; if ($response) { echo " Response Status: " . $response->getStatusCode() . "\n"; echo " Response Body: " . $response->getBody() . "\n"; } if ($error) { echo " Error: " . $error->getMessage() . "\n"; } } ``` ## PSR-7 Request and Response Objects Create and manipulate PSR-7 compliant request and response objects directly. ```php use GuzzleHttp\Client; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Uri; use GuzzleHttp\Psr7; // Create a request manually $request = new Request( 'POST', 'https://api.example.com/users', [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer token123' ], json_encode(['name' => 'John']) ); // Modify immutable request (returns new instance) $request = $request ->withHeader('X-Custom', 'value') ->withAddedHeader('Accept', 'application/json'); // Access request properties echo $request->getMethod(); // POST echo $request->getUri(); // https://api.example.com/users echo $request->getUri()->getHost(); // api.example.com echo $request->getUri()->getPath(); // /users echo $request->getHeaderLine('Content-Type'); // application/json // Send the PSR-7 request $client = new Client(); $response = $client->send($request, ['timeout' => 10]); // Create a response manually (useful for mocking) $response = new Response( 200, // Status code ['Content-Type' => 'application/json'], // Headers '{"success": true}', // Body '1.1', // Protocol version 'OK' // Reason phrase ); // Work with URI objects $uri = new Uri('https://example.com/path?query=value#fragment'); echo $uri->getScheme(); // https echo $uri->getHost(); // example.com echo $uri->getPath(); // /path echo $uri->getQuery(); // query=value // Convert messages to strings for debugging echo Psr7\Message::toString($request); echo Psr7\Message::toString($response); ``` ## Creating and Using Streams Work with PSR-7 streams for efficient handling of request and response bodies. ```php use GuzzleHttp\Psr7; use GuzzleHttp\Client; // Create stream from string $stream = Psr7\Utils::streamFor('Hello, World!'); echo $stream; // Hello, World! echo $stream->getSize(); // 13 echo $stream->read(5); // Hello echo $stream->getContents(); // , World! $stream->rewind(); echo $stream->read(5); // Hello // Create stream from file $stream = Psr7\Utils::streamFor(Psr7\Utils::tryFopen('/path/to/file.txt', 'r')); echo $stream->getMetadata('uri'); // /path/to/file.txt echo $stream->isReadable(); // true echo $stream->isWritable(); // false (opened with 'r') // Create stream from generator (lazy evaluation) $generator = function ($bytes) { for ($i = 0; $i < $bytes; $i++) { yield str_repeat('.', 100); } }; $stream = Psr7\Utils::streamFor($generator(100)); // Generates data on-demand as it's read // Create stream from callback $stream = Psr7\Utils::streamFor(function ($size) { return str_repeat('x', min($size, 1024)); }); // Use stream as request body $client = new Client(); $stream = Psr7\Utils::streamFor(fopen('/path/to/large-file.bin', 'r')); $response = $client->post('https://httpbin.org/post', [ 'body' => $stream ]); // Stream capabilities $stream = Psr7\Utils::streamFor('test'); var_dump($stream->isReadable()); // bool(true) var_dump($stream->isWritable()); // bool(true) var_dump($stream->isSeekable()); // bool(true) $stream->seek(0, SEEK_END); echo $stream->tell(); // 4 ``` Guzzle is ideal for building API clients, web scrapers, webhook handlers, and any PHP application that needs to communicate over HTTP. It excels in scenarios requiring concurrent requests (API aggregation, batch processing), complex authentication flows, or detailed control over HTTP behavior. The PSR-7 and PSR-18 compliance ensures Guzzle integrates seamlessly with modern PHP frameworks like Laravel, Symfony, and Slim. Common integration patterns include creating service-specific client classes that wrap Guzzle with domain logic, using the middleware system for cross-cutting concerns like logging, caching, and rate limiting, and leveraging the mock handler for comprehensive test coverage. For production applications, configure appropriate timeouts, enable SSL verification, implement retry logic for transient failures, and use connection pooling for high-throughput scenarios.