Files
elixforms-web-pages/php/common/HttpClient.php
T

140 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace ElixForms\Common;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\RequestOptions;
/**
* Client HTTP robusto per effettuare chiamate REST esterne.
* Adapter applicativo basato su Guzzle per la gestione uniforme di
* header, autenticazione, timeout ed errori delle chiamate esterne.
*/
class HttpClient
{
private ClientInterface $client;
private array $defaultHeaders;
private ?string $username = null;
private ?string $password = null;
private int $timeout = 10;
/**
* @param array<string, string> $defaultHeaders Header predefiniti per ogni richiesta
*/
public function __construct(array $defaultHeaders = [], ?ClientInterface $client = null)
{
$this->defaultHeaders = $defaultHeaders;
$this->client = $client ?? new Client();
}
/**
* Imposta le credenziali per l'autenticazione Basic.
*/
public function setBasicAuth(string $username, string $password): self
{
$this->username = $username;
$this->password = $password;
return $this;
}
/**
* Imposta il timeout massimo per la connessione e l'esecuzione.
*/
public function setTimeout(int $seconds): self
{
if ($seconds <= 0) {
throw new \InvalidArgumentException('Il timeout deve essere maggiore di zero.');
}
$this->timeout = $seconds;
return $this;
}
/**
* Esegue una richiesta HTTP GET.
*
* @param string $url URL della richiesta
* @param array<string, mixed> $queryParams Parametri query aggiuntivi
* @param array<string, string> $headers Header specifici per questa richiesta
* @return string Risposta in formato testuale
* @throws \RuntimeException In caso di errore HTTP o di trasporto
*/
public function get(string $url, array $queryParams = [], array $headers = []): string
{
$options = $queryParams === [] ? [] : [RequestOptions::QUERY => $queryParams];
return $this->request($url, 'GET', $headers, $options);
}
/**
* Esegue una richiesta HTTP POST.
*
* @param string $url URL della richiesta
* @param mixed $data Dati da inviare nel body (array, stringa o JSON)
* @param array<string, string> $headers Header specifici per questa richiesta
* @return string Risposta in formato testuale
* @throws \RuntimeException In caso di errore HTTP o di trasporto
*/
public function post(string $url, $data, array $headers = []): string
{
$options = is_array($data)
? [RequestOptions::FORM_PARAMS => $data]
: [RequestOptions::BODY => $data];
return $this->request($url, 'POST', $headers, $options);
}
/**
* @param array<string, string> $headers
* @param array<string, mixed> $requestOptions
*/
private function request(string $url, string $method, array $headers, array $requestOptions = []): string
{
$options = [
RequestOptions::HEADERS => array_merge($this->defaultHeaders, $headers),
RequestOptions::TIMEOUT => $this->timeout,
RequestOptions::CONNECT_TIMEOUT => $this->timeout,
RequestOptions::ALLOW_REDIRECTS => ['max' => 3],
RequestOptions::VERIFY => true,
RequestOptions::HTTP_ERRORS => true,
];
if ($this->username !== null && $this->password !== null) {
$options[RequestOptions::AUTH] = [$this->username, $this->password];
}
try {
$response = $this->client->request(
strtoupper($method),
$url,
array_replace($options, $requestOptions)
);
} catch (RequestException $exception) {
$response = $exception->getResponse();
if ($response !== null) {
throw new \RuntimeException(
"Richiesta fallita con codice di stato HTTP {$response->getStatusCode()}.",
0,
$exception
);
}
throw new \RuntimeException('Errore di connessione al servizio esterno.', 0, $exception);
} catch (GuzzleException $exception) {
throw new \RuntimeException('Errore durante la chiamata al servizio esterno.', 0, $exception);
}
$statusCode = $response->getStatusCode();
if ($statusCode < 200 || $statusCode >= 300) {
throw new \RuntimeException("Richiesta fallita con codice di stato HTTP {$statusCode}.");
}
return (string)$response->getBody();
}
}