Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Guzzle Psr18 Adapter Laravel Package

ricardofiorani/guzzle-psr18-adapter

Tiny adapter that lets you use Guzzle as a PSR-18 HTTP client. Bridges PSR-7 requests/responses and promises to the PSR-18 ClientInterface, easing migration to standards-based HTTP without rewriting existing Guzzle integrations.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require ricardofiorani/guzzle-psr18-adapter
    

    Add to composer.json under require if not using Composer globally.

  2. Basic Usage: Create a PSR-18 HTTP client instance:

    use Ricardofiorani\GuzzlePsr18Adapter\GuzzleHttpClient;
    use GuzzleHttp\Client as GuzzleClient;
    
    $guzzleClient = new GuzzleClient();
    $psr18Client = new GuzzleHttpClient($guzzleClient);
    
  3. First Use Case: Send a simple GET request:

    $response = $psr18Client->sendRequest(
        new \Psr\Http\Message\Request('GET', 'https://api.example.com/data')
    );
    $body = $response->getBody()->getContents();
    

Where to Look First

  • Documentation: Check the PSR-18 spec for expected behavior.
  • Source Code: The adapter is minimal (~50 lines). Review GuzzleHttpClient for implementation details.
  • Tests: Study the test suite for edge cases (e.g., error handling, middleware).

Implementation Patterns

Core Workflow

  1. Adapter as a Wrapper: Use the adapter to wrap an existing GuzzleHttp\Client instance, enabling PSR-18 compliance in legacy or mixed-codebase projects.

    $guzzleClient = new GuzzleClient(['base_uri' => 'https://api.example.com']);
    $psr18Client = new GuzzleHttpClient($guzzleClient);
    
  2. Middleware Integration: Leverage Guzzle’s middleware stack while adhering to PSR-18:

    $stack = \GuzzleHttp\HandlerStack::create();
    $stack->push(\GuzzleHttp\Middleware::mapResponse(function ($response) {
        return $response->withHeader('X-Custom', 'Value');
    }));
    $guzzleClient = new GuzzleClient(['handler' => $stack]);
    $psr18Client = new GuzzleHttpClient($guzzleClient);
    
  3. Dependency Injection: Register the adapter in your DI container (e.g., Laravel’s AppServiceProvider):

    $this->app->singleton(\Psr\Http\Client\ClientInterface::class, function ($app) {
        return new GuzzleHttpClient(
            new GuzzleClient($app['config']['http.client.options'])
        );
    });
    
  4. Async Requests: Use Guzzle’s async capabilities via PSR-18 (though PSR-18 itself is synchronous):

    $promise = $psr18Client->sendAsyncRequest(
        new \Psr\Http\Message\Request('GET', 'https://api.example.com/data')
    );
    $response = $promise->wait(); // Blocking; consider using a library like `react/promise` for async.
    

Integration Tips

  • Laravel HTTP Client: Combine with Laravel’s Http facade for hybrid usage:
    use Illuminate\Support\Facades\Http;
    
    $response = Http::withOptions(['handler' => $guzzleClient->getHandler()])
        ->get('https://api.example.com/data');
    
  • Testing: Mock the adapter in tests:
    $mockClient = $this->createMock(\Psr\Http\Client\ClientInterface::class);
    $mockClient->method('sendRequest')->willReturn($mockResponse);
    
  • Error Handling: Wrap calls in try-catch for RequestException (Guzzle) or TransferException (PSR-18):
    try {
        $response = $psr18Client->sendRequest($request);
    } catch (\Psr\Http\Client\ClientExceptionInterface $e) {
        // Handle PSR-18 errors.
    }
    

Gotchas and Tips

Pitfalls

  1. Async Limitations:

    • PSR-18 is synchronous. The adapter’s sendAsyncRequest is a thin wrapper around Guzzle’s async; avoid mixing with reactive libraries (e.g., ReactPHP) without explicit handling.
    • Fix: Use wait() or then() sparingly; prefer synchronous calls where possible.
  2. Middleware Conflicts:

    • Guzzle middleware may alter responses in ways incompatible with PSR-18 expectations (e.g., modifying Request objects).
    • Fix: Test middleware thoroughly. Use mapRequest/mapResponse cautiously.
  3. Deprecated Guzzle Features:

    • The adapter was last updated in 2021. Guzzle v7+ may introduce breaking changes.
    • Fix: Pin Guzzle version (^6.5 or ^7.0) in composer.json and monitor for updates.
  4. Response Validation:

    • PSR-18 requires responses to implement \Psr\Http\Message\ResponseInterface, but Guzzle’s responses may lack expected methods (e.g., getStatusCode()).
    • Fix: Verify responses with:
      if (!$response instanceof \Psr\Http\Message\ResponseInterface) {
          throw new \RuntimeException('Invalid PSR-18 response');
      }
      

Debugging Tips

  1. Log Requests/Responses: Add Guzzle’s logging middleware to debug:

    $stack->push(\GuzzleHttp\Middleware::tap(function ($request) {
        \Log::debug('Request:', ['url' => (string) $request->getUri()]);
    }));
    
  2. Check Headers: Ensure headers are properly formatted (e.g., Content-Type):

    $request = new \GuzzleHttp\Psr7\Request('GET', 'https://example.com');
    $request = $request->withHeader('Content-Type', 'application/json');
    
  3. Handler Stack Inspection: Dump the handler stack to debug middleware order:

    \Log::debug('Handler stack:', [$guzzleClient->getConfig('handler')->getHandlers()]);
    

Extension Points

  1. Custom Response Factories: Override the adapter’s response factory to transform Guzzle responses:

    class CustomGuzzleHttpClient extends GuzzleHttpClient {
        public function sendRequest(\Psr\Http\Message\RequestInterface $request) {
            $response = parent::sendRequest($request);
            return $this->customizeResponse($response);
        }
    
        protected function customizeResponse($response) {
            return new \Psr\Http\Message\Response(
                $response->getStatusCode(),
                $response->getHeaders(),
                $response->getBody()
            );
        }
    }
    
  2. Event Dispatching: Integrate with Laravel events or other frameworks:

    $stack->push(\GuzzleHttp\Middleware::tap(function ($request) use ($dispatcher) {
        $dispatcher->dispatch('http.request', [$request]);
    }));
    
  3. Retry Logic: Use Guzzle’s retry middleware via PSR-18:

    $stack->push(\GuzzleHttp\Middleware::retry(
        function ($retries, \GuzzleHttp\Exception\RequestException $exception) {
            return $retries < 3 && $exception->hasResponse();
        },
        function (\GuzzleHttp\Exception\RequestException $exception) {
            return 500;
        }
    ));
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor