Skip to content

Changing Client

One of the core design goals of this package is driver independence. You write your request code once, and the underlying HTTP library can be swapped at any time -- through configuration, the builder, or direct injection. This is especially valuable when moving between environments (e.g., a Symfony project that uses the Symfony driver vs. a CLI tool that uses raw cURL).

Available Drivers

The package ships with three production drivers and one test driver:

Driver name Library Package
curl PHP cURL extension Built-in (no dependency)
guzzle Guzzle HTTP guzzlehttp/guzzle
symfony Symfony HttpClient symfony/http-client
(mock) Built-in test double Built-in

The default driver is curl, which requires no additional dependencies since the cURL extension is included with most PHP installations.

CurlDriver

The cURL driver provides zero-dependency HTTP support. It uses PHP's native cURL extension with automatic HTTP/1.1 and HTTP/2 negotiation, full header parsing, streaming support, and built-in SSL verification. This is the best choice for quick starts and lightweight applications.

GuzzleDriver

Guzzle is a mature, feature-rich HTTP client with its own middleware ecosystem, PSR-7 message support, and excellent streaming capabilities. Use it when you need Guzzle-specific features or when your project already depends on it.

SymfonyDriver

The Symfony HttpClient offers native HTTP/2 support, automatic content-type detection, and multiple transports (native PHP, cURL, amphp). It is the natural choice for Symfony applications.

Switching Drivers

Via Configuration

The simplest way to choose a driver is through HttpClientConfig:

use Cognesy\Http\Config\HttpClientConfig;
use Cognesy\Http\HttpClient;

// Use Guzzle
$client = HttpClient::fromConfig(new HttpClientConfig(driver: 'guzzle'));

// Use Symfony
$client = HttpClient::fromConfig(new HttpClientConfig(driver: 'symfony'));

// Use cURL (default)
$client = HttpClient::fromConfig(new HttpClientConfig(driver: 'curl'));

Via the Builder

The builder gives you the same choice in a more explicit, composable form:

use Cognesy\Http\Config\HttpClientConfig;
use Cognesy\Http\Creation\HttpClientBuilder;

$client = (new HttpClientBuilder())
    ->withConfig(new HttpClientConfig(driver: 'guzzle'))
    ->create();

Injecting a Driver Directly

If you have a pre-configured driver instance, bypass the registry entirely:

use Cognesy\Http\Creation\HttpClientBuilder;

$client = (new HttpClientBuilder())
    ->withDriver($myCustomDriver)
    ->create();

Or use the static shorthand:

use Cognesy\Http\HttpClient;

$client = HttpClient::fromDriver($myCustomDriver);

Reusing a Vendor Client Instance

Sometimes you already have a configured vendor client (e.g., a GuzzleHttp\Client with custom options or a Symfony client with specific transport settings). You can pass that instance directly:

use Cognesy\Http\Creation\HttpClientBuilder;
use GuzzleHttp\Client;

$guzzle = new Client([
    'timeout' => 10,
    'verify' => '/path/to/cacert.pem',
]);

$client = (new HttpClientBuilder())
    ->withClientInstance('guzzle', $guzzle)
    ->create();

The withClientInstance() method selects the driver by name and passes your vendor client to it, so the driver uses your instance instead of creating its own.

This works with any supported driver:

use Symfony\Component\HttpClient\HttpClient as SymfonyHttpClient;

$client = (new HttpClientBuilder())
    ->withClientInstance('symfony', SymfonyHttpClient::create(['timeout' => 30]))
    ->create();

Using the Mock Driver

For testing, the mock driver lets you define responses without making network calls:

use Cognesy\Http\Creation\HttpClientBuilder;
use Cognesy\Http\Data\HttpResponse;

$client = (new HttpClientBuilder())
    ->withMock(function ($mock) {
        $mock->addResponse(
            HttpResponse::sync(200, ['Content-Type' => 'application/json'], '{"users":[]}'),
            url: 'https://api.example.com/users',
            method: 'GET',
        );
    })
    ->create();

Request Code Stays the Same

The key insight is that your request code never changes when you switch drivers. The same HttpRequest, the same send() call, the same response handling:

$response = $client->send(new HttpRequest(
    url: 'https://api.example.com/data',
    method: 'GET',
    headers: ['Accept' => 'application/json'],
    body: '',
    options: [],
))->get();

echo $response->body();

This code works identically with cURL, Guzzle, Symfony, or the mock driver.

See Also