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

Common Http Laravel Package

geocoder-php/common-http

Common HTTP layer for Geocoder PHP providers. Includes shared HTTP client abstractions, request/response handling, and helpers to integrate PSR-18 clients and PSR-7 messages, keeping geocoding providers lightweight and consistent across transports.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require geocoder-php/common-http
    

    This package is designed to be a dependency for other HTTP-based geocoders (e.g., geocoder-php/google-map, geocoder-php/openstreetmap). Install the appropriate provider package based on your needs.

  2. First Use Case:

    • If you're integrating with a geocoder provider (e.g., Google Maps, OpenStreetMap), this package handles the low-level HTTP logic. Focus on configuring the provider (e.g., API keys, HTTP clients) rather than raw HTTP requests.
  3. Where to Look First:

    • AbstractHttpProvider: The core class that defines HTTP request/response handling. Extend this for custom providers.
    • PSR-18 Client: The package now uses PSR-18 (HttpClientInterface) for HTTP requests. Laravel's built-in GuzzleHttp\Client or Symfony\Contracts\HttpClient\HttpClient will work out of the box.

Implementation Patterns

1. Dependency Injection

  • PSR-18 HTTP Client: Pass your HTTP client (e.g., Guzzle, Symfony HTTP Client) to the provider constructor. Example:
    use Geocoder\Provider\AbstractHttpProvider;
    use Psr\Http\Client\ClientInterface;
    
    $httpClient = new \GuzzleHttp\Client();
    $provider = new CustomProvider($httpClient, $apiKey);
    
  • PSR-17 Factories: Use Psr\Http\Message\RequestFactoryInterface and Psr\Http\Message\StreamFactoryInterface for request/response creation. Laravel's Symfony\Component\HttpFoundation\Request or GuzzleHttp\Psr7 factories work well.

2. Extending AbstractHttpProvider

  • Override methods like:
    • getUrl(): Define the API endpoint.
    • getRequest(): Customize request headers/body (e.g., add authentication).
    • getParsedResponse(): Parse provider-specific responses (e.g., JSON/XML).
  • Example:
    class CustomProvider extends AbstractHttpProvider {
        protected function getUrl(): string {
            return 'https://api.example.com/geocode?q=' . urlencode($this->query);
        }
    
        protected function getParsedResponse(ResponseInterface $response): array {
            $data = json_decode($response->getBody(), true);
            return $data['results'] ?? [];
        }
    }
    

3. Integration with Laravel

  • Service Provider: Bind the HTTP client and provider to the container:
    public function register() {
        $this->app->singleton(ClientInterface::class, function ($app) {
            return new \GuzzleHttp\Client();
        });
    
        $this->app->bind(CustomProvider::class, function ($app) {
            return new CustomProvider(
                $app->make(ClientInterface::class),
                config('services.custom_geocoder.api_key')
            );
        });
    }
    
  • Configuration: Store API keys/endpoints in config/services.php:
    'custom_geocoder' => [
        'api_key' => env('CUSTOM_GEOCODE_API_KEY'),
        'endpoint' => 'https://api.example.com',
    ],
    

4. Handling Responses

  • Use getParsedResponse() to transform raw responses into geocoder-compatible formats (e.g., arrays with latitude, longitude).
  • Example for a custom provider:
    $provider = new CustomProvider($httpClient, $apiKey);
    $results = $provider->geocode('1600 Amphitheatre Parkway, Mountain View');
    foreach ($results as $result) {
        echo $result->getCoordinates(); // Returns a \Geocoder\Coordinate object
    }
    

5. Error Handling

  • Extend handleResponse() to manage provider-specific errors (e.g., rate limits, invalid queries):
    protected function handleResponse(ResponseInterface $response): void {
        if ($response->getStatusCode() === 429) {
            throw new \RuntimeException('Rate limit exceeded');
        }
        parent::handleResponse($response);
    }
    

Gotchas and Tips

1. PSR-18/PSR-17 Compatibility

  • Gotcha: Older code using HttpClient or MessageFactory directly may break. Use dependency injection for ClientInterface, RequestFactoryInterface, and StreamFactoryInterface.
  • Tip: Laravel's Http facade (Guzzle) or Symfony\Component\HttpClient\HttpClient are drop-in replacements. Example:
    use Symfony\Contracts\HttpClient\HttpClientInterface;
    
    $httpClient = \Symfony\Contracts\HttpClient\HttpClient::create();
    

2. Debugging HTTP Requests

  • Tip: Use middleware to log requests/responses. For Guzzle:
    $client = new \GuzzleHttp\Client([
        'handler' => \GuzzleHttp\HandlerStack::create([
            new \GuzzleHttp\Middleware::tap(function ($request, $options) {
                \Log::debug('Geocoder Request:', [
                    'url' => (string) $request->getUri(),
                    'headers' => $request->getHeaders(),
                ]);
            }),
        ]),
    ]);
    

3. Rate Limiting and Retries

  • Gotcha: Providers like Google Maps throttle requests. Implement retries with exponential backoff:
    use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
    use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
    
    try {
        $response = $httpClient->request('GET', $url);
    } catch (ClientExceptionInterface | ServerExceptionInterface $e) {
        if ($e->getCode() === 429) {
            sleep(2); // Retry after delay
            return $this->geocode($query);
        }
        throw $e;
    }
    

4. Caching Responses

  • Tip: Cache geocode results to reduce API calls. Use Laravel's cache:
    $cacheKey = 'geocode_' . md5($query);
    $results = \Cache::remember($cacheKey, now()->addHours(1), function () use ($provider, $query) {
        return $provider->geocode($query);
    });
    

5. Testing

  • Tip: Mock the HTTP client in tests. Example with PHPUnit:
    use Psr\Http\Message\ResponseInterface;
    
    $mockResponse = $this->createMock(ResponseInterface::class);
    $mockResponse->method('getBody')->willReturn(json_encode(['results' => []]));
    
    $mockClient = $this->createMock(ClientInterface::class);
    $mockClient->method('sendRequest')->willReturn($mockResponse);
    
    $provider = new CustomProvider($mockClient, 'api_key');
    $results = $provider->geocode('test');
    $this->assertCount(0, $results);
    

6. Provider-Specific Quirks

  • Gotcha: Some providers (e.g., OpenStreetMap) require query parameters in a specific format. Override getUrl() to ensure correct URL construction:
    protected function getUrl(): string {
        return sprintf(
            '%s/reverse?lat=%s&lon=%s',
            $this->endpoint,
            $this->query->getLatitude(),
            $this->query->getLongitude()
        );
    }
    

7. Performance

  • Tip: Reuse HTTP clients and providers. Laravel's service container handles this automatically if bound as singletons.
  • Gotcha: Avoid instantiating new clients/providers per request. Example of anti-pattern:
    // Bad: Creates a new client every time
    $provider = new CustomProvider(new \GuzzleHttp\Client(), $apiKey);
    

8. Extension Points

  • Custom Headers: Add headers via getRequest():
    protected function getRequest(): RequestInterface {
        $request = $this->requestFactory->createRequest('GET', $this->getUrl());
        $request = $request->withHeader('X-Custom-Header', 'value');
        return $request;
    }
    
  • Timeouts: Configure timeouts in the HTTP client:
    $client = new \GuzzleHttp\Client([
        'timeout' => 10.0, // 10 seconds
    ]);
    
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.
babenkoivan/elastic-client
innmind/static-analysis
innmind/coding-standard
datacore/hub-sdk
alengo/sulu-http-cache-bundle
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity