Kalo te dokumentacioni
PHP

Klientët HTTP

cURL si parazgjedhje, çdo klient PSR-18 kur preferoni të përdorni tuajin, dhe një imitim për testet.

Parazgjedhja

Pa httpClient:, kërkesat kalojnë përmes OpenEmail\Http\CurlHttpClient, që nuk ka nevojë për asgjë tjetër përveç zgjerimit curl. Mban një handle cURL për çdo klient, ndaj një kërkesë e dytë ripërdor lidhjen që hapi e para, dhe faqja Konfigurimi rendit opsionet e tij për proxy-t, autoritetet e certifikimit dhe cilësimet shtesë të cURL.

Çfarëdo që i lëviz bajtet, klienti vetë bën gjithçka tjetër: kredencialin, çelësat e idempotencës, riprovat dhe backoff-in e tyre, afatin e skadimit për çdo përpjekje dhe përjashtimin për çdo dështim. Një klient HTTP vetëm dërgon një kërkesë dhe kthen përgjigjen.

Çdo klient PSR-18

OpenEmail\Http\Psr18HttpClient mbështjell çdo klient që implementon PSR-18, si Guzzle ose Symfony HttpClient, ndaj kërkesat kalojnë përmes klientit që aplikacioni juaj e konfiguron tashmë, me middleware-in, regjistrimin në log dhe cilësimet e proxy-t të tij.

guzzle.php
use GuzzleHttp\Client;use OpenEmail\Http\Psr18HttpClient;use OpenEmail\OpenEmail; $client = new OpenEmail(httpClient: new Psr18HttpClient(new Client())); $client->me->ping();
symfony_http_client.php
use OpenEmail\Http\Psr18HttpClient;use OpenEmail\OpenEmail;use Symfony\Component\HttpClient\HttpClient;use Symfony\Component\HttpClient\Psr18Client; $http = new Psr18Client(HttpClient::create(['timeout' => 30, 'max_redirects' => 0])); $client = new OpenEmail(httpClient: new Psr18HttpClient($http)); $client->me->ping();

PSR-18 i ndërton kërkesat e tij me fabrika PSR-17. Adapteri përdor vetë klientin kur ai është edhe fabrikë, siç është Psr18Client i Symfony-t, pastaj php-http/discovery kur është i instaluar, pastaj nyholm/psr7 ose fabrikat e vetë Guzzle-it, dhe hedh InvalidArgumentException duke përmendur çfarë duhet instaluar kur nuk gjen asnjë. Jepni requestFactory: dhe streamFactory: për t’i zgjedhur vetë. Psr18Client i Symfony-t ka nevojë edhe për ndërfaqet PSR-18 dhe për një implementim të PSR-17, ndaj instalojeni me composer require symfony/http-client psr/http-client nyholm/psr7.

factories.php
use GuzzleHttp\Client;use Nyholm\Psr7\Factory\Psr17Factory;use OpenEmail\Http\Psr18HttpClient;use OpenEmail\OpenEmail; $factory = new Psr17Factory(); $client = new OpenEmail(httpClient: new Psr18HttpClient(new Client(), requestFactory: $factory, streamFactory: $factory)); $client->me->ping();
ÇështjaÇfarë ndodh
Afatet e skadimitGuzzle e merr timeout: të klientit në çdo kërkesë. PSR-18 nuk ka mënyrë për t’i kaluar një afat skadimi ndonjë klienti tjetër, ndaj vendosni një te klienti që mbështillni, siç bën HttpClient::create(['timeout' => 30, 'max_redirects' => 0]) më sipër, përndryshe një kërkesë që nuk përgjigjet kurrë mund të presë përgjithmonë.
Statuset e gabimitNjë klient PSR-18 i kthen përgjigjet 4xx dhe 5xx në vend që të hedhë përjashtim, dhe adapteri i thotë Guzzle-it të bëjë të njëjtën gjë, ndaj çdo refuzim bëhet prapë ApiException i duhur.
Pa përgjigjeNjë ClientExceptionInterface nga klienti i mbështjellë bëhet një NetworkException që mban mesazhin e tij, por jo vetë përjashtimin, sepse ai mban kërkesën dhe header-in e saj Authorization. isTimeout() është true kur mesazhi thotë se kërkesës i skadoi afati, dhe për çdo klient përveç Guzzle-it mesazhi thotë se ishte afati i vetë klientit të mbështjellë.
RidrejtimetGuzzle-it i thuhet të mos i ndjekë, dhe shembulli i Symfony-t e vendos max_redirects në 0, ndaj një ridrejtim bëhet ApiException, ashtu si me klientin cURL të parazgjedhur.

Klienti juaj

httpClient: pranon çdo gjë që implementon OpenEmail\Http\HttpClient, një ndërfaqe me një metodë të vetme: send(HttpRequest $request): HttpResponse. Ktheni përgjigjen për çdo status, përfshirë 4xx dhe 5xx, dhe hidhni përjashtim vetëm kur nuk mbërriti asnjë përgjigje.

KlasaÇfarë mbart
HttpRequestmethod, url, headers si një array emrash dhe vlerash, body si string ose null, dhe timeout në sekonda, ose null kur nuk ka. header($name) lexon një header pa marrë parasysh shkronjat e mëdha a të vogla. var_dump(), print_r() dhe json_encode() e shfaqin header-in e saj Authorization si [redacted], por var_export() dhe dump() i Symfony-t i shtypin headers ashtu siç janë, ndaj mos e regjistroni kurrë një kërkesë me këto të dyja.
HttpResponseNdërtohet si new HttpResponse($status, $headers, $body). I kthen emrat e header-ave në shkronja të vogla, dhe header($name) lexon një të tillë pa marrë parasysh shkronjat e mëdha a të vogla.
HttpClientExceptionHidheni nga send kur nuk mbërriti asnjë përgjigje. new HttpClientException($message, timeout: true) shënon një skadim afati.

Testimi pa rrjet

Një klient imitim regjistron atë që do të kishte dalë dhe përgjigjet me çfarëdo që i duhet testit, ndaj asgjë nuk largohet nga makina dhe testi mund të kontrollojë saktësisht çfarë u dërgua.

fake_http_client.php
use OpenEmail\Http\HttpClient;use OpenEmail\Http\HttpRequest;use OpenEmail\Http\HttpResponse;use OpenEmail\OpenEmail; $fake = new class implements HttpClient {    public array $requests = [];     public function send(HttpRequest $request): HttpResponse    {        $this->requests[] = $request;         return new HttpResponse(            200,            ['content-type' => 'application/json'],            json_encode(['id' => 'msg_test', 'status' => 'sent', 'replayed' => false], JSON_THROW_ON_ERROR),        );    }}; $testClient = new OpenEmail(apiKey: 'oe_test_fake', httpClient: $fake, maxRetries: 0, disableUpdateNotice: true); $sent = $testClient->emails->send([    'from' => '[email protected]',    'to' => '[email protected]',    'subject' => 'Hi',    'text' => 'Hello',]); $request = $fake->requests[0]; echo $sent['status'], ' ', $request->method, ' ', $request->url, ' ', $request->header('Idempotency-Key'), PHP_EOL;var_dump($request);
  • Ktheni një status jashtë 2xx me zarfin e gabimit të API-së si trup, si p.sh. {"error": {"type": "validation_error", "code": "invalid_parameter", "message": "..."}}, për të marrë nënklasën përkatëse të ApiException.
  • Hidhni new HttpClientException('timed out', timeout: true) nga send për të marrë një NetworkException me isTimeout() true. Çdo Exception tjetër, si një RuntimeException, bëhet një NetworkException me isTimeout() false.
  • Një LogicException dhe çdo Error, si një TypeError, llogariten si defekte të imitimit. Hidhen të pandryshuara dhe nuk riprovohen kurrë.

Ndërtojeni klientin e testimit me maxRetries: 0 kur programoni dështime. Përndryshe, një status i riprovueshëm ose një dështim rrjeti te një thirrje që mund të përsëritet pa rrezik provohet tri herë, me pritje të vërteta ndërmjet.