The Symfony HttpClient before there was a Symfony HttpClient

Symfony 4 has no HTTP client of its own this year — the component arrives in 4.3 — so a 4.0 project reaches for Guzzle, and the thing worth setting up is not the library but the boundary around it.

final class PaymentGateway
{
    public function __construct(ClientInterface $http, string $baseUri)
    {
        $this->http    = $http;
        $this->baseUri = $baseUri;
    }

    public function authorise(Card $card, int $cents): Authorisation
    {
        // the only place in the codebase that knows this API's shape
    }
}

Injecting the client interface rather than constructing one is what makes the class testable without a network, and it is the same reason the base URI is a constructor argument rather than a constant. Wrapping the vendor exception in one of your own at this boundary means a change of HTTP library later touches one file. This is unremarkable advice that gets skipped, and the cost of skipping it is a Guzzle exception type appearing in a controller three layers up.