<?php
|
namespace JVBase\integrations;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
trait Requests
|
{
|
protected bool $hasRequests = true;
|
/**
|
* Child classes must set this (e.g. 'https://api.example.com/v1/')
|
*/
|
protected ?string $baseUrl = null;
|
protected int $ttl = 300; //5 minutes default
|
|
/**
|
* Child classes define their auth headers (Bearer token, API key, etc.)
|
*/
|
abstract protected function getRequestHeaders(): array;
|
|
/**
|
* Child classes inspect the wp_remote_request response,
|
* decode it, and handle/log errors as needed
|
*/
|
abstract protected function handleResponse(array|\WP_Error $response, string $method, string $endpoint): array;
|
|
public function getRequest(string $endpoint, array $data = [], bool $force = false, ?int $ttl = null): array
|
{
|
$key = [$this->service_name, $this->userID, $endpoint, $data];
|
if (!$force) {
|
$cached = $this->cache->get($key);
|
if ($cached) {
|
return $cached;
|
}
|
}
|
$response = $this->makeRequest('GET', $endpoint, $data);
|
|
if ($response && !is_wp_error($response)) {
|
$ttl = is_int($ttl) ? $ttl : $this->ttl;
|
$this->cache->set($key, $response, $ttl);
|
}
|
|
return $response;
|
}
|
|
public function postRequest(string $endpoint, array $data = []): array
|
{
|
return $this->makeRequest('POST', $endpoint, $data);
|
}
|
|
public function putRequest(string $endpoint, array $data = []): array
|
{
|
return $this->makeRequest('PUT', $endpoint, $data);
|
}
|
|
public function deleteRequest(string $endpoint, array $data = []): array
|
{
|
return $this->makeRequest('DELETE', $endpoint, $data);
|
}
|
|
protected function makeRequest(string $method, string $endpoint, array $data = []): array
|
{
|
if (is_null($this->baseUrl)) {
|
error_log('Can not make request. No $baseUrl set for '.$this->service_name);
|
return [];
|
}
|
if ($this->requestLimiter && !$this->awaitRateLimit($endpoint)) {
|
$error = new \WP_Error('rate_limit_exceeded', "Rate limit exceeded for {$endpoint}");
|
return $this->handleResponse($error, $method, $endpoint);
|
}
|
|
$url = rtrim($this->baseUrl, '/') . '/' . ltrim($endpoint, '/');
|
|
$args = [
|
'method' => $method,
|
'headers' => $this->getRequestHeaders(),
|
'timeout' => 15,
|
];
|
|
if (!empty($data)) {
|
if (in_array($method, ['GET', 'DELETE'], true)) {
|
$url = add_query_arg($data, $url);
|
} else {
|
$args['body'] = wp_json_encode($data);
|
}
|
}
|
|
$response = wp_remote_request($url, $args);
|
|
$this->requestLimiter?->recordRequest($endpoint);
|
|
return $this->handleResponse($response, $method, $endpoint);
|
}
|
|
/**
|
* Waits (with jitter) for the rate limit to clear, up to a few attempts.
|
* Returns false if still limited after max attempts.
|
*/
|
protected function awaitRateLimit(string $endpoint, int $maxAttempts = 3): bool
|
{
|
$attempts = 0;
|
|
while (!$this->requestLimiter->checkRateLimit($endpoint)) {
|
$attempts++;
|
if ($attempts >= $maxAttempts) {
|
return false;
|
}
|
usleep(random_int(250_000, 750_000)); // 250-750ms jitter
|
}
|
|
return true;
|
}
|
}
|