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.