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

alextartan/guzzle-psr18-adapter

PSR-18 HTTP client adapter for Guzzle. Wraps Guzzle to provide a PSR-18 ClientInterface, letting you send PSR-7 requests and receive PSR-7 responses with standard client/network/request exceptions. Requires PHP 7.2+.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require alextartan/guzzle-psr18-adapter
    

    Ensure your composer.json includes "php": "^7.2" and "guzzlehttp/guzzle": "^6.0".

  2. First Implementation: Replace a Guzzle Client instance with the PSR-18 adapter in a service or controller:

    use Alextartan\GuzzlePsr18Adapter\Psr18Adapter;
    use GuzzleHttp\Client;
    use Psr\Http\Message\RequestInterface;
    
    $guzzleClient = new Client();
    $psr18Client = new Psr18Adapter($guzzleClient);
    
    $request = new \GuzzleHttp\Psr7\Request('GET', 'https://api.example.com/data');
    $response = $psr18Client->sendRequest($request);
    
  3. Type-Hinting: Update method signatures to accept Psr\Http\Client\ClientInterface:

    public function fetchData(ClientInterface $client) {
        $request = new \GuzzleHttp\Psr7\Request('GET', 'https://api.example.com/data');
        $response = $client->sendRequest($request);
        // ...
    }
    
  4. Laravel Service Provider: Bind the adapter in AppServiceProvider:

    public function register() {
        $this->app->bind(\Psr\Http\Client\ClientInterface::class, function ($app) {
            return new Psr18Adapter(new Client([
                'base_uri' => config('services.api.base_uri'),
            ]));
        });
    }
    

Implementation Patterns

Core Workflows

  1. PSR-18 Compliance in Controllers: Inject ClientInterface and use the adapter transparently:

    public function getExternalData(ClientInterface $client) {
        $request = new \GuzzleHttp\Psr7\Request('GET', '/data');
        $response = $client->sendRequest($request);
        return json_decode($response->getBody(), true);
    }
    
  2. Middleware Integration: Use Guzzle middleware via the underlying client:

    $client = new Client([
        'handler' => HandlerStack::create(new Middleware()),
    ]);
    $psr18Client = new Psr18Adapter($client);
    
  3. Request Factories: Reuse Guzzle’s request factories if needed:

    $request = $psr18Client->getClient()->createRequest('POST', '/data', [
        'json' => ['key' => 'value']
    ]);
    
  4. Error Handling: Catch PSR-18 exceptions:

    try {
        $response = $client->sendRequest($request);
    } catch (\Psr\Http\Client\ClientExceptionInterface $e) {
        // Handle client errors (e.g., 4xx, 5xx)
    } catch (\Psr\Http\Client\NetworkExceptionInterface $e) {
        // Handle network issues
    }
    

Laravel-Specific Patterns

  1. API Resource Wrappers: Create a facade or helper to abstract PSR-18 calls:

    namespace App\Services;
    
    use Psr\Http\Client\ClientInterface;
    
    class ApiClient {
        public function __construct(private ClientInterface $client) {}
    
        public function getUserData(int $id) {
            $request = new \GuzzleHttp\Psr7\Request('GET', "/users/{$id}");
            return $this->client->sendRequest($request);
        }
    }
    
  2. Dynamic Configuration: Use Laravel’s config to manage Guzzle settings:

    $psr18Client = new Psr18Adapter(new Client(config('guzzle')));
    
  3. Testing: Mock ClientInterface in tests:

    $mockClient = $this->createMock(ClientInterface::class);
    $mockClient->method('sendRequest')->willReturn(new \GuzzleHttp\Psr7\Response(200));
    
    $this->app->instance(ClientInterface::class, $mockClient);
    

Gotchas and Tips

Common Pitfalls

  1. Method Signature Mismatches:

    • Issue: Calling request() or get() directly on the adapter (these are Guzzle-specific).
    • Fix: Use sendRequest() and construct requests manually:
      // ❌ Avoid
      $response = $psr18Client->request('GET', '/data');
      
      // ✅ Do
      $request = new \GuzzleHttp\Psr7\Request('GET', '/data');
      $response = $psr18Client->sendRequest($request);
      
  2. Response Handling:

    • Issue: PSR-18 ResponseInterface lacks Guzzle’s convenience methods (e.g., json()).
    • Fix: Use PSR-17 factories or Guzzle’s Response directly:
      $body = $response->getBody()->getContents();
      $data = json_decode($body, true);
      
  3. Middleware Conflicts:

    • Issue: Guzzle middleware may not play well with PSR-18’s strict interface.
    • Fix: Test middleware thoroughly, especially retries and timeouts.
  4. Async Limitations:

    • Issue: The adapter does not expose Guzzle’s async methods (e.g., sendAsync).
    • Fix: Use the underlying Client for async operations:
      $promise = $psr18Client->getClient()->sendAsync($request);
      
  5. Guzzle Version Lock:

    • Issue: The adapter may not support Guzzle 7+.
    • Fix: Check compatibility or fork the package if needed.

Debugging Tips

  1. Log Requests/Responses: Add Guzzle’s tap middleware to the underlying client:

    $client = new Client([
        'handler' => HandlerStack::create([
            new Middleware(),
            new \GuzzleHttp\Middleware::tap(function ($request, $options) {
                \Log::debug('Request:', [
                    'method' => $request->getMethod(),
                    'uri' => (string) $request->getUri(),
                    'headers' => $request->getHeaders(),
                ]);
            }),
        ]),
    ]);
    
  2. Verify PSR-18 Compliance: Use php-http/client-tests to validate compliance:

    composer require --dev php-http/client-tests
    vendor/bin/client-tests --client=Alextartan\GuzzlePsr18Adapter\Psr18Adapter
    
  3. Check Underlying Client: Access the Guzzle Client for advanced debugging:

    $guzzleClient = $psr18Client->getClient();
    $config = $guzzleClient->getConfig();
    

Extension Points

  1. Custom Request Factories: Extend the adapter to support custom request creation:

    class CustomPsr18Adapter extends Psr18Adapter {
        public function createRequest(string $method, string $uri, array $options = []) {
            return $this->getClient()->createRequest($method, $uri, $options);
        }
    }
    
  2. Response Decorators: Wrap responses to add Guzzle-like convenience methods:

    $response = $psr18Client->sendRequest($request);
    $data = json_decode($response->getBody(), true);
    
  3. Fork and Maintain: Fork the repository to add support for Guzzle 7/8 or PHP 8.2+ if needed. Key changes might include:

    • Updating composer.json dependencies.
    • Adjusting type hints for newer PHP versions.
    • Adding tests for new Guzzle features.
  4. Integration with Laravel HTTP Client: Use the adapter with Laravel’s HTTP client facade for consistency:

    use Illuminate\Support\Facades\Http;
    
    Http::macro('withPsr18Client', function () {
        return new Psr18Adapter(new Client());
    });
    

Configuration Quirks

  1. Base URI Handling: Configure the underlying Guzzle Client with a base URI:

    $psr18Client = new Psr18Adapter(new Client([
        'base_uri' => 'https://api.example.com/v1/',
    ]));
    
  2. Timeouts: Set timeouts via Guzzle’s config:

    $psr18Client = new Psr18Adapter(new Client([
        'timeout' => 10.0,
        'connect_timeout' => 5.0,
    ]));
    
  3. Authentication: Use Guzzle’s auth middleware:

    $client = new Client([
        'auth' => ['user', 'pass'],
    ]);
    $psr18Client = new Psr18Adapter($client);
    

Performance Considerations

  1. Reuse Clients:
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