Files
wb-api-test/app/Services/Wb/WbApiClient.php
T
2026-07-01 03:28:17 +03:00

111 lines
3.5 KiB
PHP

<?php
namespace App\Services\Wb;
use Illuminate\Support\Facades\Http;
use RuntimeException;
class WbApiClient
{
private string $baseUrl;
private string $apiKey;
private int $limit;
private int $timeout;
private int $requestSleepMs;
private int $tooManyAttemptsSleepSeconds;
private int $max429Retries;
public function __construct()
{
$this->baseUrl = rtrim((string) config('wb_api.base_url'), '/');
$this->apiKey = (string) config('wb_api.key');
$this->limit = max(1, min((int) config('wb_api.limit', 100), 500));
$this->timeout = (int) config('wb_api.timeout', 30);
$this->requestSleepMs = (int) config('wb_api.request_sleep_ms', 1000);
$this->tooManyAttemptsSleepSeconds = (int) config('wb_api.too_many_attempts_sleep_seconds', 30);
$this->max429Retries = (int) config('wb_api.max_429_retries', 20);
if ($this->baseUrl === '') {
throw new RuntimeException('WB_API_BASE_URL is empty. Check your .env file.');
}
if ($this->apiKey === '') {
throw new RuntimeException('WB_API_KEY is empty. Check your .env file.');
}
}
public function limit(): int
{
return $this->limit;
}
public function fetchPage(string $endpoint, array $params, int $page): array
{
$url = $this->baseUrl . '/api/' . ltrim($endpoint, '/');
$query = array_filter(array_merge($params, [
'key' => $this->apiKey,
'limit' => $this->limit,
'page' => $page,
]), static fn ($value) => $value !== null && $value !== '');
$attempt429 = 0;
while (true) {
if ($this->requestSleepMs > 0) {
usleep($this->requestSleepMs * 1000);
}
$response = Http::timeout($this->timeout)
->acceptJson()
->get($url, $query);
if ($response->status() === 429) {
$attempt429++;
if ($attempt429 > $this->max429Retries) {
throw new RuntimeException("Too many 429 responses for {$endpoint} page {$page}");
}
$retryAfter = $response->header('Retry-After');
$sleepSeconds = is_numeric($retryAfter)
? max(1, (int) $retryAfter)
: $this->tooManyAttemptsSleepSeconds;
echo "{$endpoint} page={$page} got 429, sleeping {$sleepSeconds}s, retry {$attempt429}/{$this->max429Retries}\n";
sleep($sleepSeconds);
continue;
}
if (!$response->successful()) {
throw new RuntimeException(sprintf(
'API request failed: endpoint=%s page=%d http=%d body=%s',
$endpoint,
$page,
$response->status(),
mb_substr($response->body(), 0, 1000)
));
}
$json = $response->json();
if (!is_array($json)) {
throw new RuntimeException("Unexpected non-array API response for {$endpoint}, page {$page}");
}
$items = $json['data'] ?? $json;
if (!is_array($items)) {
throw new RuntimeException("Unexpected API response shape for {$endpoint}, page {$page}");
}
if ($items !== [] && array_keys($items) !== range(0, count($items) - 1)) {
$items = [$items];
}
return array_values(array_filter($items, 'is_array'));
}
}
}