amphp/http-client
Asynchronous HTTP client for PHP built on Revolt and fibers. Supports HTTP/1 & HTTP/2, concurrent requests, connection pooling, redirects, gzip/deflate, streaming bodies, TLS by default, forms, cookies, and proxies—no ext/curl dependency.
Installation:
composer require amphp/http-client
For HTTP/2 optimizations, also install:
composer require ext-nghttp2
First Request:
use Amp\Http\Client\HttpClientBuilder;
$client = HttpClientBuilder::buildDefault();
$response = await $client->request('https://httpbin.org/get');
echo $response->getBody()->buffer();
Key Classes:
HttpClientBuilder: Configures and builds clients.Request: Defines HTTP requests (mutable).Response: Handles responses (mutable).use Amp\Http\Client\HttpClientBuilder;
use Amp\Loop;
$client = HttpClientBuilder::buildDefault();
$urls = ['https://httpbin.org/get', 'https://httpbin.org/ip'];
$tasks = [];
foreach ($urls as $url) {
$tasks[] = $client->request($url);
}
$responses = await Amp\Promise\all($tasks);
foreach ($responses as $response) {
echo $response->getBody()->buffer() . "\n";
}
Pattern: Use Request for structured requests.
$request = new Request('https://api.example.com/data', 'POST');
$request->setHeader('Authorization', 'Bearer token123');
$request->setHeader('Content-Type', 'application/json');
$request->setBody(json_encode(['key' => 'value']));
Workflow:
Request objects for retries (deep clone with clone).HttpContent for streaming bodies (e.g., file uploads).Pattern: Stream responses for large payloads.
$response = await $client->request('https://large-file.example.com/data');
$body = $response->getBody();
// Stream chunks (memory-efficient)
while (!$body->isEmpty()) {
$chunk = await $body->read();
// Process chunk (e.g., save to disk)
}
Integration Tip:
getHeaders() for metadata (e.g., Content-Length).getStatus() for HTTP status codes (e.g., 200, 404).Pattern: Leverage default concurrency.
$client = HttpClientBuilder::buildDefault();
$promises = [];
for ($i = 0; $i < 100; $i++) {
$promises[] = $client->request("https://httpbin.org/get?i=$i");
}
$responses = await Amp\Promise\all($promises);
Optimization:
HttpClientBuilder::build() with custom connection pools for long-lived clients.$client = (new HttpClientBuilder)
->withHttp2Support()
->build();
Pattern: Compose interceptors for reusable logic.
use Amp\Http\Client\Interceptor\SetRequestHeader;
$client = (new HttpClientBuilder)
->intercept(new SetRequestHeader('X-API-Key', 'secret123'))
->intercept(new SetRequestHeader('User-Agent', 'MyApp/1.0'))
->build();
Common Use Cases:
Authorization headers.LogHttpArchive for HAR files.RetryRequests interceptor.PrivateCache or amphp/http-client-cache.Example: Retry Failed Requests
use Amp\Http\Client\Interceptor\RetryRequests;
$client = (new HttpClientBuilder)
->intercept(new RetryRequests(3)) // Retry 3 times
->build();
Pattern: Use try/catch with RequestException.
try {
$response = await $client->request('https://invalid.url');
} catch (Amp\Http\Client\RequestException $e) {
echo "Request failed: " . $e->getMessage();
}
Common Exceptions:
RequestException: Network/HTTP errors.ConnectionException: Connection issues (e.g., DNS failure).Pattern: Use amphp/http-client-cookies.
use Amp\Http\Client\Cookie\CookieJar;
use Amp\Http\Client\Interceptor\CookieHandler;
$cookieJar = new CookieJar();
$client = (new HttpClientBuilder)
->intercept(new CookieHandler($cookieJar))
->build();
// First request (sets cookies)
$response = await $client->request('https://example.com/login');
// Subsequent requests (uses cookies)
$response = await $client->request('https://example.com/dashboard');
Pattern: Route through proxies using amphp/http-tunnel.
use Amp\Http\Client\Proxy\ProxyResolver;
use Amp\Http\Client\Proxy\Socks5Proxy;
$proxy = new Socks5Proxy('proxy.example.com', 1080);
$resolver = new ProxyResolver($proxy);
$client = (new HttpClientBuilder)
->withProxyResolver($resolver)
->build();
Request and Response are mutable. Avoid modifying them after passing to request().$original = new Request('https://api.example.com');
$cloned = clone $original;
$cloned->setMethod('POST');
FollowRedirects interceptor:
GET redirects (preserves method for 307/308).$client = (new HttpClientBuilder)
->followRedirects(0) // Disable auto-redirects
->build();
nghttp2 extension for optimal performance.$client = (new HttpClientBuilder)
->withHttp2Support()
->build();
$client = (new HttpClientBuilder)
->withConnectionLimit(50) // Default is 10
->build();
getHeader() carefully.$request->setHeader('Content-Type', 'application/json');
// Later: $request->getHeader('content-type') returns 'application/json'.
$client = (new HttpClientBuilder)
->withTlsContext(\Amp\Socket\TlsContext::create([
'verify_peer' => false, // Disable for testing only!
'cafile' => '/path/to/cert.pem',
]))
->build();
LogHttpArchive for HAR files:
$client = (new HttpClientBuilder)
->listen(new LogHttpArchive('/tmp/debug.har'))
->build();
SetRequestTimeout interceptor.$response->close().ApplicationInterceptor or NetworkInterceptor.Transport implementation for custom protocols.use Amp\Http\Client\Interceptor\ApplicationInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
class CustomHeaderInterceptor implements ApplicationInterceptor {
public function intercept(Request $request, callable $next): \Amp\Promise {
$request->addHeader('X-Custom', 'value');
return $next($request);
}
}
HttpClient once and reuse it (How can I help you explore Laravel packages today?