| Current Path : /home/seto/indexator.pm/src/Search/ |
| Current File : /home/seto/indexator.pm/src/Search/ConcurrentChecker.php |
<?php
namespace App\Search;
class ConcurrentChecker
{
/**
* Run up to $concurrency queries in parallel via curl_multi.
*
* @param array<int|string, string> $queries map of key => query string
* @return array<int|string, SearchResult> same keys, SearchResult values
*/
public function runBatch(
SearchProviderInterface $provider,
array $queries,
int $concurrency = 10,
): array {
if (empty($queries)) {
return [];
}
$results = [];
$keys = array_keys($queries);
$chunks = array_chunk($keys, $concurrency);
foreach ($chunks as $chunk) {
$mh = curl_multi_init();
$handles = [];
foreach ($chunk as $key) {
$ch = curl_init();
$opts = $provider->buildCurlOptions($queries[$key]);
$opts[CURLOPT_RETURNTRANSFER] = true;
curl_setopt_array($ch, $opts);
curl_multi_add_handle($mh, $ch);
$handles[$key] = $ch;
}
// Execute all handles concurrently
$running = null;
do {
$status = curl_multi_exec($mh, $running);
if ($running > 0) {
curl_multi_select($mh, 1.0);
}
} while ($running > 0 && $status === CURLM_OK);
// Collect results
foreach ($handles as $key => $ch) {
$body = (string) (curl_multi_getcontent($ch) ?? '');
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
if ($error !== '') {
$results[$key] = new SearchResult('error', 0, [], '', 'curl error: ' . $error);
} else {
$results[$key] = $provider->parseResponse($body, $httpCode);
}
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
}
return $results;
}
}