HTTP क्लाइंट
डिफ़ॉल्ट रूप से cURL, जब आप अपना इस्तेमाल करना चाहें तो कोई भी PSR-18 क्लाइंट, और टेस्ट के लिए एक नकली क्लाइंट।
डिफ़ॉल्ट
httpClient: न होने पर रिक्वेस्ट OpenEmail\Http\CurlHttpClient से होकर जाती हैं, जिसे curl एक्सटेंशन के सिवा कुछ नहीं चाहिए। यह हर क्लाइंट के लिए एक cURL handle रखता है, इसलिए दूसरी रिक्वेस्ट वही कनेक्शन दोबारा इस्तेमाल करती है जो पहली ने खोला था, और कॉन्फ़िगरेशन पेज प्रॉक्सी, certificate authorities और अतिरिक्त cURL सेटिंग्स के लिए इसके options की सूची देता है।
बाइट्स चाहे जो भी पहुँचाए, बाकी सब कुछ क्लाइंट ख़ुद करता है: क्रेडेंशियल, idempotency कुंजियाँ, retry और उनका backoff, हर प्रयास का टाइमआउट और हर विफलता का exception। HTTP क्लाइंट सिर्फ़ एक रिक्वेस्ट भेजता है और जवाब लौटा देता है।
कोई भी PSR-18 क्लाइंट
OpenEmail\Http\Psr18HttpClient PSR-18 को implement करने वाले किसी भी क्लाइंट को लपेटता है, जैसे Guzzle या Symfony HttpClient, इसलिए रिक्वेस्ट उसी क्लाइंट से होकर जाती हैं जिसे आपका ऐप्लिकेशन पहले से कॉन्फ़िगर करता है, उसके middleware, logging और प्रॉक्सी सेटिंग्स के साथ।
use GuzzleHttp\Client;use OpenEmail\Http\Psr18HttpClient;use OpenEmail\OpenEmail; $client = new OpenEmail(httpClient: new Psr18HttpClient(new Client())); $client->me->ping();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 अपनी रिक्वेस्ट PSR-17 factories से बनाता है। adapter पहले ख़ुद क्लाइंट को इस्तेमाल करता है जब वह factory भी हो, जैसे Symfony का Psr18Client, फिर इंस्टॉल होने पर php-http/discovery को, फिर nyholm/psr7 या Guzzle की अपनी factory को, और कोई न मिलने पर क्या इंस्टॉल करना है यह बताते हुए InvalidArgumentException throw करता है। इन्हें ख़ुद चुनने के लिए requestFactory: और streamFactory: पास करें। Symfony के Psr18Client को PSR-18 interfaces और एक PSR-17 implementation की भी ज़रूरत होती है, इसलिए इसे composer require symfony/http-client psr/http-client nyholm/psr7 से इंस्टॉल करें।
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();| विषय | क्या होता है |
|---|---|
| टाइमआउट | Guzzle को हर रिक्वेस्ट पर क्लाइंट का timeout: मिलता है। PSR-18 के पास किसी दूसरे क्लाइंट को टाइमआउट देने का कोई तरीक़ा नहीं है, इसलिए जिस क्लाइंट को आप लपेटते हैं उस पर टाइमआउट सेट करें, जैसा ऊपर HttpClient::create(['timeout' => 30, 'max_redirects' => 0]) करता है, वरना कभी जवाब न देने वाली रिक्वेस्ट हमेशा इंतज़ार कर सकती है। |
| Error status | PSR-18 क्लाइंट 4xx और 5xx जवाबों को throw करने के बजाय लौटाता है, और adapter Guzzle को भी ऐसा ही करने को कहता है, इसलिए हर अस्वीकार फिर भी सही ApiException बनता है। |
| कोई जवाब नहीं | लपेटे गए क्लाइंट से आया ClientExceptionInterface एक NetworkException बन जाता है जो उसका संदेश रखता है, पर ख़ुद exception नहीं, क्योंकि उसमें रिक्वेस्ट और उसका Authorization हेडर होता है। जब संदेश बताता है कि रिक्वेस्ट का समय समाप्त हो गया, तो isTimeout() true होता है, और Guzzle को छोड़कर किसी भी क्लाइंट के लिए संदेश बताता है कि टाइमआउट लपेटे गए क्लाइंट का अपना था। |
| Redirect | Guzzle को इनका पालन न करने को कहा जाता है, और Symfony उदाहरण max_redirects को 0 पर सेट करता है, इसलिए redirect एक ApiException बन जाता है, जैसा डिफ़ॉल्ट cURL क्लाइंट के साथ होता है। |
आपका अपना क्लाइंट
httpClient: ऐसी कोई भी चीज़ लेता है जो OpenEmail\Http\HttpClient को implement करे, एक interface जिसमें एक ही मेथड है: send(HttpRequest $request): HttpResponse। हर status के लिए जवाब लौटाएँ, 4xx और 5xx समेत, और throw सिर्फ़ तब करें जब कोई जवाब न आया हो।
| क्लास | इसमें क्या होता है |
|---|---|
| HttpRequest | method, url, नाम और मान के array के रूप में headers, स्ट्रिंग या null के रूप में body, और सेकंड में timeout, या कोई न हो तो null। header($name) किसी भी case में एक हेडर पढ़ता है। var_dump(), print_r() और json_encode() इसका Authorization हेडर [redacted] के रूप में दिखाते हैं, लेकिन var_export() और Symfony का dump() headers को जैसा है वैसा ही छापते हैं, इसलिए इन दोनों से कभी रिक्वेस्ट log न करें। |
| HttpResponse | new HttpResponse($status, $headers, $body) के रूप में बनता है। यह हेडर के नामों को lowercase कर देता है, और header($name) किसी भी case में एक हेडर पढ़ता है। |
| HttpClientException | जब कोई जवाब न आया हो तो इसे send से throw करें। new HttpClientException($message, timeout: true) टाइमआउट को चिह्नित करता है। |
बिना नेटवर्क के टेस्टिंग
नकली क्लाइंट वह रिकॉर्ड करता है जो बाहर जाता और वही जवाब देता है जो टेस्ट को चाहिए, इसलिए मशीन से बाहर कुछ नहीं जाता और टेस्ट ठीक-ठीक जाँच सकता है कि क्या भेजा गया।
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);- मेल खाने वाली
ApiExceptionsubclass पाने के लिए 2xx के बाहर का कोई status लौटाएँ, जिसकी बॉडी API का error envelope हो, जैसे{"error": {"type": "validation_error", "code": "invalid_parameter", "message": "..."}}। - ऐसा
NetworkExceptionपाने के लिए जिसकाisTimeout()true हो,sendसेnew HttpClientException('timed out', timeout: true)throw करें। कोई दूसराException, जैसेRuntimeException, ऐसाNetworkExceptionबन जाता है जिसकाisTimeout()false होता है। LogicExceptionऔर कोई भीError, जैसेTypeError, नकली क्लाइंट में bug माने जाते हैं। वे बिना बदले throw होते हैं और कभी retry नहीं होते।
जब आप विफलताओं को script करें तो टेस्ट क्लाइंट maxRetries: 0 के साथ बनाएँ। वरना दोहराने में सुरक्षित कॉल पर retry योग्य status या नेटवर्क विफलता को तीन बार आज़माया जाता है, बीच में असली इंतज़ार के साथ।