VOOZH about

URL: https://deepwiki.com/friendsofhyperf/components/4.1-request-building-and-execution

⇱ Request Building and Execution | friendsofhyperf/components | DeepWiki


Loading...
Last indexed: 14 February 2026 (15d5ca)
Menu

HTTP Client - Request Building and Execution

The http-client component provides a Laravel-inspired fluent interface for building and executing HTTP requests in Hyperf applications. Built on Guzzle, it offers chainable configuration methods, automatic coroutine handler detection, retry mechanisms, and middleware support.

Related Documentation:

Purpose and Scope

This page documents:

  • Request Configuration - Headers, authentication, body formats, URLs, timeouts, SSL options
  • Request Execution - Sending requests, retry logic, error handling
  • Middleware Pipeline - Request/response transformation hooks
  • Async Execution - Coroutine handler detection and promise-based requests
  • Basic Response Access - Essential methods for accessing response data

For testing-specific features (fakes, stubs, assertions) see page 4.2. For concurrent request pools see page 4.3.

Sources: src/http-client/src/PendingRequest.php1-1388 src/http-client/src/Factory.php1-489

Core Architecture

Component Relationships


Component Interactions:

The HTTP client follows a factory pattern where Factory creates PendingRequest instances. Each pending request builds a Guzzle HandlerStack with middleware layers. When executed, the request flows through the middleware pipeline, optionally hitting stub callbacks in test mode, and returns a Response wrapper with convenient methods for inspection.

Sources: src/http-client/src/Factory.php74-489 src/http-client/src/PendingRequest.php48-263 src/http-client/src/Http.php106-217

Request Lifecycle


Lifecycle Phases:

  1. Facade Resolution - Http::get() uses __callStatic() to resolve Factory from container via getFacadeRoot()
  2. PendingRequest Creation - Factory::__call() creates new PendingRequest via newPendingRequest(), passing global middleware
  3. Stub Registration - Factory's stubCallbacks collection attached via stub(), and preventStrayRequests flag set
  4. Fluent Configuration - Chained methods like withHeaders(), timeout() accumulate in $options array
  5. HTTP Verb Call - Methods like get(), post() call send() which triggers execution
  6. Handler Stack Assembly - buildHandlerStack() creates HandlerStack and layers handlers via pushHandlers()
  7. Client Creation - buildClient() instantiates GuzzleHttp\Client with handler stack
  8. Request Execution - sendRequest() calls $client->request() or $client->requestAsync()
  9. Middleware Pipeline - Handlers execute in LIFO order (last pushed, first executed)
  10. Response Wrapping - PSR-7 response wrapped in Response via newResponse(), populated with cookies and transfer stats
  11. Recording - If $recording is true, recordRequestResponsePair() stores to $recorded array
  12. Event Dispatch - ResponseReceived or ConnectionFailed event dispatched for observability integrations

Sources: src/http-client/src/PendingRequest.php759-834 src/http-client/src/PendingRequest.php856-925 src/http-client/src/PendingRequest.php952-991 src/http-client/src/Factory.php140-151 src/http-client/src/Factory.php480-487 src/http-client/src/Http.php115-128

Making Requests

HTTP Verb Methods

The Http facade provides static access to all HTTP methods through __callStatic() which proxies to Factory::__call(), which creates a PendingRequest and invokes the method:


HTTP Verb Implementation:

Each verb method in PendingRequest delegates to send() at src/http-client/src/PendingRequest.php759-834:

  • get(string $url, $query = null) - Sends GET with query in options array
  • head(string $url, $query = null) - Sends HEAD with query in options array
  • post(string $url, array $data = []) - Sends POST with $data keyed by $this->bodyFormat (default: 'json')
  • patch(string $url, array $data = []) - Sends PATCH with data in body format
  • put(string $url, array $data = []) - Sends PUT with data in body format
  • delete(string $url, array $data = []) - Sends DELETE, only includes body if $data is not empty

Body Format Handling:

The $bodyFormat property (default: 'json' set in constructor) determines how $data is sent:

  • 'json'['json' => $data] → Guzzle sends as application/json
  • 'form_params'['form_params' => $data] → Guzzle sends as application/x-www-form-urlencoded
  • 'multipart'['multipart' => $data] → Guzzle sends as multipart/form-data
  • 'body'['body' => $pendingBody] → Raw string body

Sources: src/http-client/src/PendingRequest.php759-834 src/http-client/src/PendingRequest.php856-925 src/http-client/src/PendingRequest.php243-255 src/http-client/src/Http.php115-128 tests/HttpClient/HttpClientTest.php79-86

Fluent Configuration Chain

The HTTP client uses a fluent interface allowing method chaining:


Each configuration method returns $this, enabling chainable calls before the final HTTP verb method.

Sources: src/http-client/src/PendingRequest.php266-637 tests/HttpClient/HttpClientTest.php417-435

Request Configuration

Headers and Authentication

Headers:


Authentication:


Sources: src/http-client/src/PendingRequest.php414-503 tests/HttpClient/HttpClientTest.php698-738

Request Body Formats

Body Format Methods:

Body format is controlled via bodyFormat() method and convenience wrappers at src/http-client/src/PendingRequest.php296-365:

JSON (Default):


Form Data:


Multipart (File Uploads):


Raw Body:


Body Format Processing:

The parseHttpOptions() method at src/http-client/src/PendingRequest.php1211-1241 processes body formats:

  1. If $options contains key matching $this->bodyFormat:
    • For 'multipart': Calls parseMultipartBodyFormat() to normalize array structure
    • For 'body': Uses $this->pendingBody directly
    • For arrays: Merges with $this->pendingFiles (from attach() calls)
  2. If no matching key: Sets $options[$this->bodyFormat] = $this->pendingBody
  3. For 'json' with JsonSerializable: Preserves object (Guzzle will serialize)
  4. For Arrayable objects: Converts via toArray()

After send, $this->pendingBody and $this->pendingFiles are reset to [null, []].

Sources: src/http-client/src/PendingRequest.php296-365 src/http-client/src/PendingRequest.php1211-1253 src/http-client/src/PendingRequest.php243-255 tests/HttpClient/HttpClientTest.php363-392 tests/HttpClient/HttpClientTest.php437-467 tests/HttpClient/HttpClientTest.php624-672

URL Parameters and Base URL

Query Parameters:


Base URL:


URL Template Parameters:


Sources: src/http-client/src/PendingRequest.php368-379 src/http-client/src/PendingRequest.php266-275 src/http-client/src/PendingRequest.php506-515 src/http-client/src/PendingRequest.php1205-1208 tests/HttpClient/HttpClientTest.php830-899

Timeouts and Redirects

Timeouts:


Redirects:


SSL Verification:


Sources: src/http-client/src/PendingRequest.php581-602 src/http-client/src/PendingRequest.php532-565

Cookies and Additional Options

Cookies:


Response Sink:


Guzzle Options:


Sources: src/http-client/src/PendingRequest.php518-529 src/http-client/src/PendingRequest.php567-578 src/http-client/src/PendingRequest.php625-637 tests/HttpClient/HttpClientTest.php811-828

Response Handling

Basic Response Access

The Response object provides methods for accessing response data after request execution:


Error Handling:


Sources: src/http-client/src/Response.php76-251 src/http-client/src/Response.php322-340 src/http-client/src/RequestException.php16-47

Retry Mechanisms

Retry Logic Flow


Retry Configuration:

The retry() method at src/http-client/src/PendingRequest.php604-622 configures retry behavior:


Retry Properties Set:


Execution in send():

At src/http-client/src/PendingRequest.php876-924 retry logic wraps request execution:

  1. Calls support library's retry() helper with $this->backoff ?? $this->tries
  2. On each attempt:
    • Executes sendRequest()
    • Wraps response in newResponse(), calls populateResponse()
    • Dispatches ResponseReceived event
    • If !successful(), calls retryWhenCallback to get $shouldRetry
    • If throwCallback set and conditions met, calls response->throw($throwCallback)
    • If attempt < tries and shouldRetry, throws to trigger retry
    • If final attempt and retryThrow enabled, throws
  3. Retry helper applies delay from retryDelay or backoff array

Sources: src/http-client/src/PendingRequest.php604-622 src/http-client/src/PendingRequest.php876-924 src/support/src/retry.php1-50

Integration with Backoff Strategies

The retry mechanism integrates with the Support library's backoff strategies (see Backoff Strategies):


How Backoff Arrays Work:

When retry() receives an array:

  1. Sets $this->backoff = $times (the array) at line 613
  2. Calculates $times = count($times) + 1 at line 614 for total attempts
  3. In send(), passes $this->backoff ?? $this->tries to retry helper at line 878
  4. Retry helper extracts delay for each attempt from array at src/support/src/retry.php1-50

Example Execution:


Total of 4 attempts (array length + 1), with delays between each failure.

Sources: src/http-client/src/PendingRequest.php610-615 src/http-client/src/PendingRequest.php876-924 src/support/src/retry.php1-50

Middleware System

Middleware Types and Execution Order

The HTTP client supports three levels of middleware:


Handler Stack Construction:

The handler stack is assembled in pushHandlers() method at src/http-client/src/PendingRequest.php973-991:

  1. Base Handler - HandlerStack::create($this->handler) creates stack with base transport (CoroutineHandler or default)

  2. Push Order (pushes happen top-to-bottom):

    • buildBeforeSendingHandler() - Executes beforeSendingCallbacks collection
    • buildRecorderHandler() - Records request/response pair to Factory::$recorded
    • Custom middleware - Each from $this->middleware collection (global + per-request)
    • buildStubHandler() - Intercepts requests in test mode via stubCallbacks
  3. Execution Order (LIFO - last pushed executes first):

    • Stub handler checks if URL matches test stubs, returns fake response if found
    • Custom middleware transforms request/response (user-defined + global)
    • Recorder stores pair in $recorded array for assertions
    • Before sending callbacks execute with Request/options/PendingRequest
    • Transport handler performs actual HTTP call (CoroutineHandler in coroutines, CurlHandler otherwise)

Why LIFO? Middleware stacks execute in reverse order so the last-pushed handler (stub) gets first chance to intercept, allowing test fakes to prevent real HTTP calls.

Sources: src/http-client/src/PendingRequest.php973-991 src/http-client/src/PendingRequest.php993-1064 src/http-client/src/Factory.php169-197

Global Middleware

Setting Global Middleware:

Global middleware applies to all requests created by the Factory:


Sources: src/http-client/src/Factory.php164-197

Per-Request Middleware

Adding Middleware to Individual Requests:


Sources: src/http-client/src/PendingRequest.php640-671

Before Sending Callbacks

Before Sending Hooks:

The beforeSending() method allows inspection or modification of the request right before it's sent:


Multiple beforeSending() callbacks can be registered and will execute in order.

Sources: src/http-client/src/PendingRequest.php673-684 src/http-client/src/PendingRequest.php1067-1090

Async Support and Coroutines

Coroutine Handler Detection

Automatic Handler Selection:

The buildHandlerStack() method automatically detects Swoole coroutine context and selects the appropriate handler at src/http-client/src/PendingRequest.php952-971:


Detection Logic:

  1. Check $this->handler - Skip detection if custom handler already set via setHandler()
  2. Check extension_loaded('swoole') - Verify Swoole extension is available
  3. Check Coroutine::inCoroutine() - Verify currently executing in a Swoole coroutine context
  4. Check native curl hooks - Ensure native curl hooks are NOT enabled:
    • For Swoole < 4.6.0: SWOOLE_HOOK_NATIVE_CURL constant doesn't exist
    • For Swoole >= 4.6.0: Check if flag is disabled in \Swoole\Runtime::getHookFlags()
  5. Result - If all conditions pass, setHandler(new CoroutineHandler()) configures non-blocking I/O

Why Check Native Curl Hooks?

Native curl hooks (SWOOLE_HOOK_NATIVE_CURL) make PHP's native curl_*() functions coroutine-safe. When enabled, Guzzle's default CurlHandler already provides non-blocking behavior, so CoroutineHandler is unnecessary. When disabled or unavailable, CoroutineHandler provides explicit coroutine support via Swoole's Coroutine\Http\Client.

Manual Override:


Sources: src/http-client/src/PendingRequest.php952-971 src/http-client/src/PendingRequest.php1167-1177

Async Requests

Making Async Requests:


Async with Retry:

Async requests support retry logic with promises:


The retry mechanism works with async requests, automatically retrying failed promises according to the configured strategy.

Sources: src/http-client/src/PendingRequest.php1133-1152 src/http-client/src/PendingRequest.php1255-1334

Promise Handling

Promise Interface:

Async requests return Guzzle promises (GuzzleHttp\Promise\PromiseInterface) which support standard promise operations:


Sources: src/http-client/src/PendingRequest.php1255-1281

Debugging and Inspection

Debugging Methods

Dump Request:


Dump and Die:


Both methods dump the Request object and Guzzle options array using Symfony's VarDumper.

Sources: src/http-client/src/PendingRequest.php724-756

Transfer Statistics

Tracking Request Stats:


Sources: src/http-client/src/Response.php277-285 src/http-client/src/PendingRequest.php1348-1387

Advanced Configuration

Custom Guzzle Client

Setting Custom Client:


Custom Handler:


Sources: src/http-client/src/PendingRequest.php1154-1177 src/http-client/src/PendingRequest.php927-950

Request and Response Objects

Request Wrapper:

The Request class wraps PSR-7 requests with convenient inspection methods:

  • method() - Get HTTP method
  • url() - Get request URL
  • header($key) - Get header value
  • headers() - Get all headers
  • hasHeader($key, $value) - Check header existence
  • body() - Get request body
  • data() - Get decoded data (form/JSON)
  • isJson() - Check if JSON request
  • isForm() - Check if form request
  • isMultipart() - Check if multipart request

Sources: src/http-client/src/Request.php21-311

Response Extensions:

Response implements ArrayAccess and Stringable for convenient data access:


Sources: src/http-client/src/Response.php26-458

Macros and Extensions

Adding Custom Methods:


Both PendingRequest and Response use the Macroable trait, allowing dynamic method addition.

Sources: src/http-client/src/PendingRequest.php50-51 src/http-client/src/Response.php28-31

Integration with Other Components

Event Dispatching

The HTTP client dispatches events through Hyperf's event system during request execution:

  • RequestSending - Fired before request is sent
  • ResponseReceived - Fired after successful response
  • ConnectionFailed - Fired on connection failure

These events enable observability integrations:


Sources: src/http-client/src/Events/RequestSending.php1-30 src/http-client/src/Events/ResponseReceived.php1-30 src/http-client/src/Events/ConnectionFailed.php1-30 src/http-client/src/PendingRequest.php1374-1387

Sentry Tracing Integration

When the Sentry component is enabled, the GuzzleHttpClientAspect automatically creates tracing spans for HTTP client requests. Each request generates a span with metadata including URL, method, and status code. See AOP-Based Instrumentation for details.

Sources: High-level architecture diagram

Telescope Recording

When Telescope is enabled, HTTP client requests are automatically recorded for debugging via the RequestWatcher. Request/response pairs are stored and viewable in the Telescope dashboard. See Telescope Debug Assistant for details.

Sources: High-level architecture diagram

Configuration Summary

Default Options

The PendingRequest constructor sets these defaults:


These can be overridden with withOptions() or specific methods like timeout() and connectTimeout().

Sources: src/http-client/src/PendingRequest.php250-255

Option Merging

Options are merged using array_merge_recursive for these keys:

  • cookies
  • form_params
  • headers
  • json
  • multipart
  • query

Other options use array_replace_recursive to prevent nested merging.

Sources: src/http-client/src/PendingRequest.php227-238 src/http-client/src/PendingRequest.php1094-1104

Refresh this wiki

On this page