@@ -30,6 +30,11 @@ class Client
3030 private int $ maxRedirects = 5 ;
3131 private bool $ allowRedirects = true ;
3232 private string $ userAgent = '' ;
33+ private int $ maxRetries = 0 ;
34+ private int $ retryDelay = 1000 ; // milliseconds
35+
36+ /** @var array<int> $retryStatusCodes */
37+ private array $ retryStatusCodes = [500 , 503 ];
3338
3439 /**
3540 * @param string $key
@@ -102,6 +107,45 @@ public function setUserAgent(string $userAgent): self
102107 return $ this ;
103108 }
104109
110+ /**
111+ * Set the maximum number of retries.
112+ *
113+ * The client will automatically retry the request if the response status code is 500 or 503, indicating a temporary error.
114+ * If the request fails after the maximum number of retries, the normal response will be returned.
115+ *
116+ * @param int $maxRetries
117+ * @return self
118+ */
119+ public function setMaxRetries (int $ maxRetries ): self
120+ {
121+ $ this ->maxRetries = $ maxRetries ;
122+ return $ this ;
123+ }
124+
125+ /**
126+ * Set the retry delay in milliseconds.
127+ *
128+ * @param int $retryDelay
129+ * @return self
130+ */
131+ public function setRetryDelay (int $ retryDelay ): self
132+ {
133+ $ this ->retryDelay = $ retryDelay ;
134+ return $ this ;
135+ }
136+
137+ /**
138+ * Set the retry status codes.
139+ *
140+ * @param array<int> $retryStatusCodes
141+ * @return self
142+ */
143+ public function setRetryStatusCodes (array $ retryStatusCodes ): self
144+ {
145+ $ this ->retryStatusCodes = $ retryStatusCodes ;
146+ return $ this ;
147+ }
148+
105149 /**
106150 * Flatten request body array to PHP multiple format
107151 *
@@ -125,6 +169,29 @@ private static function flatten(array $data, string $prefix = ''): array
125169 return $ output ;
126170 }
127171
172+ /**
173+ * Retry a callback with exponential backoff
174+ *
175+ * @param callable $callback
176+ * @return mixed
177+ * @throws \Exception
178+ */
179+ private function withRetries (callable $ callback ): mixed
180+ {
181+ $ attempts = 1 ;
182+
183+ while (true ) {
184+ $ res = $ callback ();
185+
186+ if (!in_array ($ res ->getStatusCode (), $ this ->retryStatusCodes ) || $ attempts >= $ this ->maxRetries ) {
187+ return $ res ;
188+ }
189+
190+ usleep ($ this ->retryDelay * 1000 ); // Convert milliseconds to microseconds
191+ $ attempts ++;
192+ }
193+ }
194+
128195 /**
129196 * This method is used to make a request to the server.
130197 *
@@ -190,23 +257,33 @@ public function fetch(
190257 curl_setopt ($ ch , $ option , $ value );
191258 }
192259
193- $ responseBody = curl_exec ($ ch );
194- $ responseStatusCode = curl_getinfo ($ ch , CURLINFO_HTTP_CODE );
195- if (curl_errno ($ ch )) {
196- $ errorMsg = curl_error ($ ch );
197- }
260+ $ sendRequest = function () use ($ ch , &$ responseHeaders ) {
261+ $ responseHeaders = [];
198262
199- curl_close ($ ch );
263+ $ responseBody = curl_exec ($ ch );
264+ $ responseStatusCode = curl_getinfo ($ ch , CURLINFO_HTTP_CODE );
265+ if (curl_errno ($ ch )) {
266+ $ errorMsg = curl_error ($ ch );
267+ }
200268
201- if (isset ($ errorMsg )) {
202- throw new FetchException ($ errorMsg );
203- }
269+ curl_close ($ ch );
204270
205- $ response = new Response (
206- statusCode: $ responseStatusCode ,
207- headers: $ responseHeaders ,
208- body: $ responseBody
209- );
271+ if (isset ($ errorMsg )) {
272+ throw new FetchException ($ errorMsg );
273+ }
274+
275+ return new Response (
276+ statusCode: $ responseStatusCode ,
277+ headers: $ responseHeaders ,
278+ body: $ responseBody
279+ );
280+ };
281+
282+ if ($ this ->maxRetries > 0 ) {
283+ $ response = $ this ->withRetries ($ sendRequest );
284+ } else {
285+ $ response = $ sendRequest ();
286+ }
210287
211288 return $ response ;
212289 }
@@ -260,4 +337,34 @@ public function getUserAgent(): string
260337 {
261338 return $ this ->userAgent ;
262339 }
340+
341+ /**
342+ * Get the maximum number of retries.
343+ *
344+ * @return int
345+ */
346+ public function getMaxRetries (): int
347+ {
348+ return $ this ->maxRetries ;
349+ }
350+
351+ /**
352+ * Get the retry delay.
353+ *
354+ * @return int
355+ */
356+ public function getRetryDelay (): int
357+ {
358+ return $ this ->retryDelay ;
359+ }
360+
361+ /**
362+ * Get the retry status codes.
363+ *
364+ * @return array<int>
365+ */
366+ public function getRetryStatusCodes (): array
367+ {
368+ return $ this ->retryStatusCodes ;
369+ }
263370}
0 commit comments