Guzzle 23,452 (https://github.com/guzzle/guzzle 23,452 ) wraps cURL. composer require guzzlehttp/guzzle installed 8.2.0 (6 September 2026). The 7.x line is still patched (7.15.5).
<?php
require 'vendor/autoload.php';
use GuzzleHttp\{Client, Exception\ClientException, Psr7\HttpFactory};
$api = new Client(['base_uri' => 'http://127.0.0.1:8218', 'timeout' => 2]);
echo json_decode($api->get('/products/2')->getBody())->title, "\n";
try {
$api->post('/orders', ['json' => ['product_id' => 2, 'qty' => 3]]); // no token
} catch (ClientException $e) {
echo $e::class, ' ', $e->getResponse()->getStatusCode(), "\n";
}
$request = (new HttpFactory())->createRequest('GET', '/products/9'); // PSR-17 factory
echo $api->sendRequest($request)->getStatusCode(), "\n"; // PSR-18: no throwOutput
SQL Queries That Scale GuzzleHttp\Exception\ClientException 401 404
The json option encodes the body and sets the header. Guzzle's methods throw on 4xx and 5xx, while PSR-18's sendRequest() throws only on network errors. getAsync() returns a promise, and Utils::unwrap() ran two /slow calls in 0.80 s. PSR-7 defines messages and PSR-18 the client. Code that depends on Psr\Http\Client\ClientInterface can switch to Symfony 6,093 HttpClient or a mock.