Fetch PHP

repository·main·Indexed 19 days ago

https://github.com/thavarshan/fetch-php

A modern HTTP client library for PHP built on Guzzle that provides a JavaScript-like fetch API experience. It supports synchronous and asynchronous requests using a Promise-based or Async/Await pattern via the Matrix\Support library, featuring a fluent API, automatic retries with exponential backoff, and concurrent request execution using all() and batch().

Tokens
119.2K
Snippets
331
Records
372
Agent score
62%

What's inside fetch-php

  1. Immutability and PSR-7 compatibility

    main

    The Response class uses ResponseImmutabilityTrait to provide PSR-7 compatible methods that return a new instance rather than modifying the existing one. This ensures the response remains immutable.

    Supported methods:

    • withStatus($code, $reasonPhrase = ''): Returns a new instance with the specified status.
    • withAddedHeader($name, $value): Returns a new instance with the header appended.
    • withoutHeader($name): Returns a new instance without the specified header.
    • withHeader($name, $value): Returns a new instance with the header replaced.
    • withProtocolVersion($version): Returns a new instance with the new protocol version.
    • withBody(StreamInterface $body): Returns a new instance with a new body stream.
    // The original $response remains unchanged
    $newResponse = $response->withStatus(201)->withHeader('X-Custom', 'Value');
  2. Use Fetch PHP with PSR-7 and PSR-18

    main

    Fetch PHP implements PSR-7 (HTTP Message interfaces) and PSR-18 (HTTP Client interfaces). This allows you to use standard PSR-7 request objects with the Fetch PHP Client class, or use the Client class as a drop-in replacement in any system expecting a PSR-18 compatible client.

    To use a PSR-7 request with Fetch PHP, instantiate a etch\http\Client and pass the request to sendRequest().

    // Create a PSR-7 request
    use GuzzleHttp!Psr7!Request;
    $request = new Request('GET', 'https://api.example.com/users', [
        'Accept' => 'application/json'
    ]);
    
    // Our Client class is PSR-18 compatible
    $client = new \Fetch\Http\Client();
    $response = $client->sendRequest($request);
  3. Use the ContentType enum for type-safe MIME types

    main

    The Fetch\Enum\ContentType enum provides type-safe constants for common HTTP MIME types. It is used to specify the format of request bodies or to inspect response headers.

    Available constants include:

    • ContentType::JSON (application/json)
    • ContentType::FORM_URLENCODED (application/x-www-form-urlencoded)
    • ContentType::MULTIPART (multipart/form-data)
    • ContentType::TEXT (text/plain)
    • ContentType::HTML (text/html)
    • ContentType::XML (application/xml)
    • ContentType::XML_TEXT (text/xml)
    • ContentType::BINARY (application/octet-stream)
    • ContentType::PDF (application/pdf)
    • ContentType::CSV (text/csv)
    • ContentType::ZIP (application/zip)
    • ContentType::JAVASCRIPT (application/javascript)
    • ContentType::CSS (text/css)
    use Fetch\Enum\ContentType;
    
    // Example: Using a constant in a request
    $client->post($url, $data, ContentType::JSON);
  4. Use Correlation IDs for request tracing

    main

    Every event produced by a single logical request shares a unique correlation ID. You can use $event->getCorrelationId() to stitch together the entire lifecycle (request, response, retries, and errors) in your logs or tracing systems.

    fetch_client()
        ->onRequest(fn ($e) => $tracer->startSpan('http', $e->getCorrelationId()))
        ->onResponse(fn ($e) => $tracer->endSpan($e->getCorrelationId(), $e->getLatency()));
  5. Events vs. Middleware

    main

    Choose between events and middleware based on your goal:

    • Events: Use for observation. Listeners receive event objects but cannot modify the request or response. They are ideal for logging, metrics, and tracing.
    • Middleware: Use for participation. Middleware can actively modify the request/response, short-circuit the flow, or handle errors.
  6. Fetch PHP Architectural Overview

    main

    The package uses a layered architecture to separate user concerns from low-level HTTP implementation:

    1. User-facing API: High-level functions (fetch(), fetch_client()) and helpers (get(), post()).
    2. Client Layer: The Client class providing a fluent, chainable interface.
    3. Handler Layer: The ClientHandler class managing the core HTTP logic.
    4. HTTP Message Layer: The Response class representing the result of a request.
    5. Utilities and Constants: Standardized Enums (Method, ContentType, Status) and helper functions.
  7. Reuse and clone ClientHandler configurations

    main

    The ClientHandler allows you to create specialized clients from a base configuration.

    • Reusing a Client: Once a ClientHandler is configured (e.g., with a base URI and auth token), you can use it for multiple requests.
    • Cloning with withClonedOptions(): Create a new instance that inherits all settings from the current handler but applies specific overrides (like different headers or timeouts) without modifying the original instance.
    $baseClient = ClientHandler::createWithBaseUri('https://api.example.com')
        ->withHeaders(['Accept' => 'application/json']);
    
    // Create a clone for authenticated requests
    $authClient = $baseClient->withClonedOptions([
        'headers' => ['Authorization' => 'Bearer ' . $token]
    ]);
    
    // Create a clone for long-running requests
    $longTimeoutClient = $baseClient->withClonedOptions([
        'timeout' => 60
    ]);
  8. How async/await works in Fetch PHP

    main

    Fetch PHP implements JavaScript-like async/await patterns using the jerome/matrix library. While PHP does not have native async/await support, the Matrix\​Support namespace provides the necessary functions to wrap operations in Promises and manage their lifecycle.

    Core Async Functions (from Matrix library):

    • async(): Wraps a function to run asynchronously, returning a Promise.
    • await(): Waits for a Promise to resolve and returns its value.
    • all(): Runs multiple Promises concurrently and waits for all to complete.
    • race(): Runs multiple Promises concurrently and returns the first to complete.
    • any(): Returns the first Promise to successfully resolve.
    • map(): Processes an array of items with controlled concurrency.
    • batch(): Processes items in batches with controlled concurrency.
    • retry(): Retries an async operation with exponential backoff.
    • timeout(): Adds a timeout to a promise.
    use function Matrix\Support\async;
    use function Matrix\Support\await;
    
    $promise = async(function() {
        return fetch('https://api.example.com/users');
    });
    
    $response = await($promise);
  9. How to choose between Client, ClientHandler, and Helpers

    main

    Depending on your requirements, you should choose one of the following interfaces:

    ComponentWhen to Use
    ClientWhen you need PSR-18 compatibility, a simple JS-like API, or are working within a framework that expects a standard PSR-18 client.
    ClientHandlerWhen you need advanced features like asynchronous requests, promises, fine-grained retry logic, or complex configuration.
    Global Helpers (fetch(), etc.)When making simple, one-off requests where conciseness is the priority.
  10. Use the Async/Await pattern

    main

    Fetch PHP supports an async/await pattern via the Matrix\Support library (included as a dependency). This allows you to write asynchronous code that looks synchronous.

    Key functions:

    • async(callable): Wraps a task in an async context.
    • await(promise): Waits for the promise to resolve and returns the result.
    • all(array_of_promises): Waits for all promises in an array to resolve, returning an associative array of results.
    • race(array_of_promises): Returns the result of the first promise to settle.
    use function Matrix\Support\async;
    use function Matrix\Support\await;
    
    $response = await(async(fn() => fetch('https://api.example.com/users')));
    $users = $response->json();
    echo "Fetched " . count($users) . " users";
  11. Set listener priority

    main

    Listeners are executed in order of priority (highest first). If multiple listeners have the same priority, they are executed in the order they were registered.

    fetch_client()
        ->onResponse($auditLogger, priority: 100) // runs first
        ->onResponse($metrics);                    // priority 0, runs later
  12. Add Middleware to the FetchPHP pipeline

    main

    FetchPHP uses an onion-style middleware pipeline. Middleware can be used to inject headers, log requests, or short-circuit the execution.

    Middleware Interface

    A middleware is a callable with the signature: handle(RequestInterface $request, callable $next): ResponseInterface|PromiseInterface

    Integration

    • Adding Middleware: Use addMiddleware() on a Client or ClientHandler.
    • Ordering: Middleware is stored with a priority. They are resolved highest-priority-first.
    • Execution: When a request is sent, it runs through the MiddlewarePipeline. Middleware sit outside of caching and retries, meaning they can intercept or modify the request before it reaches the cache or the network.
    • Built-in Middleware:
      • AddHeadersMiddleware: Injects or overrides headers.
      • LoggingMiddleware: Provides PSR-3 logging for requests and responses, including a correlation ID.

    Methods

    • addMiddleware()
    • middleware()
    • withoutMiddleware()
    • when() / unless() (conditional middleware application)