HTTP client
Concurrent API calls
Call several APIs at once and wait for all of them.
phasync/http-client is a PSR-18 client built on cURL's multi interface: requests started in different coroutines run together, and the slowest one decides how long the batch takes.
use phasync\HttpClient\HttpClient;
use phasync\Psr\Request;
phasync::run(function () {
$client = new HttpClient();
$urls = [
'users' => 'https://api.example.com/users',
'orders' => 'https://api.example.com/orders',
'stock' => 'https://api.example.com/stock',
];
$jobs = [];
foreach ($urls as $name => $url) {
$jobs[$name] = phasync::go(function () use ($client, $url) {
$response = $client->sendRequest(Request::create('GET', $url));
return \json_decode((string) $response->getBody(), true);
});
}
$results = [];
foreach ($jobs as $name => $job) {
$results[$name] = phasync::await($job);
}
});Three requests this way take as long as the slowest of them, not the sum of all three.
Timeouts and failures#
A request that takes too long, or fails to connect, throws once you ask the response for anything: getStatusCode(), a header, or the body. timeoutMs bounds how long a single request may take, counted from when it starts fetching, not from when sendRequest() is called.
$client = new HttpClient(['timeoutMs' => 2000]); // 2 seconds per request
foreach ($jobs as $name => $job) {
try {
$response = phasync::await($job);
$results[$name] = $response->getStatusCode();
} catch (\Throwable $e) {
$results[$name] = null; // this one failed; the others are unaffected
}
}One request failing, timing out, or being cancelled with phasync::cancel() does not affect the others in the same batch: catch around each await(), not around the whole loop, so one slow or broken endpoint does not lose the results you already have.
POST and other methods#
Build the request with phasync\Psr\Request and send it the same way:
use phasync\Psr\Request;
use phasync\Psr\StringStream;
$request = Request::create('POST', 'https://api.example.com/orders')
->withHeader('Content-Type', 'application/json')
->withBody(new StringStream(\json_encode(['item' => 'widget'])));
$response = $client->sendRequest($request);Requests built and sent this way go through the same client and run concurrently with phasync::go(), the same as the GETs above.
See also#
HttpClientA PSR-18 HTTP client that runs concurrent requests oncurl_multi.phasync::goStarts$fnas a coroutine in the current frame and scope, and returns it, without waiting for it.