11<?php
22
33/**
4- * HTTP Client library
5- *
6- * @author Matt Bernier <[email protected] > 7- * @author Elmer Thomas <[email protected] > 8- * @copyright 2018 SendGrid
9- * @license https://opensource.org/licenses/MIT The MIT License
10- * @version GIT: <git_id>
11- * @link http://packagist.org/packages/sendgrid/php-http-client
12- */
4+ * HTTP Client library
5+ *
6+ * @author Matt Bernier <[email protected] > 7+ * @author Elmer Thomas <[email protected] > 8+ * @copyright 2018 SendGrid
9+ * @license https://opensource.org/licenses/MIT The MIT License
10+ * @version GIT: <git_id>
11+ * @link http://packagist.org/packages/sendgrid/php-http-client
12+ */
1313
1414namespace SendGrid ;
1515
2727 */
2828class Client
2929{
30- /** @var string */
30+ const TOO_MANY_REQUESTS_HTTP_CODE = 429 ;
31+
32+ /**
33+ * @var string
34+ */
3135 protected $ host ;
32- /** @var array */
36+
37+ /**
38+ * @var array
39+ */
3340 protected $ headers ;
34- /** @var string */
41+
42+ /**
43+ * @var string
44+ */
3545 protected $ version ;
36- /** @var array */
46+
47+ /**
48+ * @var array
49+ */
3750 protected $ path ;
38- /** @var array */
51+
52+ /**
53+ * @var array
54+ */
3955 protected $ curlOptions ;
40- /** @var bool $isConcurrentRequest */
56+
57+ /**
58+ * @var bool
59+ */
4160 protected $ isConcurrentRequest ;
42- /** @var array $savedRequests */
61+
62+ /**
63+ * @var array
64+ */
4365 protected $ savedRequests ;
44- /** @var bool */
66+
67+ /**
68+ * @var bool
69+ */
4570 protected $ retryOnLimit ;
4671
4772 /**
4873 * These are the supported HTTP verbs
4974 *
5075 * @var array
5176 */
52- private $ methods = ['get ' , 'post ' , 'patch ' , 'put ' , 'delete ' ];
77+ private $ methods = ['get ' , 'post ' , 'patch ' , 'put ' , 'delete ' ];
5378
5479 /**
5580 * Initialize the client
@@ -105,6 +130,14 @@ public function getPath()
105130 return $ this ->path ;
106131 }
107132
133+ /**
134+ * @return array
135+ */
136+ public function getCurlOptions ()
137+ {
138+ return $ this ->curlOptions ;
139+ }
140+
108141 /**
109142 * Set extra options to set during curl initialization
110143 *
@@ -134,7 +167,7 @@ public function setRetryOnLimit($retry)
134167 }
135168
136169 /**
137- * set concurrent request flag
170+ * Set concurrent request flag
138171 *
139172 * @param bool $isConcurrent
140173 *
@@ -148,20 +181,12 @@ public function setIsConcurrentRequest($isConcurrent)
148181 }
149182
150183 /**
151- * @return array
184+ * Build the final URL to be passed
185+ *
186+ * @param array $queryParams an array of all the query parameters
187+ *
188+ * @return string
152189 */
153- public function getCurlOptions ()
154- {
155- return $ this ->curlOptions ;
156- }
157-
158- /**
159- * Build the final URL to be passed
160- *
161- * @param array $queryParams an array of all the query parameters
162- *
163- * @return string
164- */
165190 private function buildUrl ($ queryParams = null )
166191 {
167192 $ path = '/ ' . implode ('/ ' , $ this ->path );
@@ -178,13 +203,14 @@ private function buildUrl($queryParams = null)
178203 * @param string $method
179204 * @param array $body
180205 * @param array $headers
206+ *
181207 * @return array
182208 */
183209 private function createCurlOptions ($ method , $ body = null , $ headers = null )
184210 {
185211 $ options = [
186212 CURLOPT_RETURNTRANSFER => true ,
187- CURLOPT_HEADER => 1 ,
213+ CURLOPT_HEADER => true ,
188214 CURLOPT_CUSTOMREQUEST => strtoupper ($ method ),
189215 CURLOPT_SSL_VERIFYPEER => true ,
190216 CURLOPT_FAILONERROR => false
@@ -213,7 +239,7 @@ private function createCurlOptions($method, $body = null, $headers = null)
213239 *
214240 * @return array
215241 */
216- private function createSavedRequest ($ requestData , $ retryOnLimit = false )
242+ private function createSavedRequest (array $ requestData , $ retryOnLimit = false )
217243 {
218244 return array_merge ($ requestData , ['retryOnLimit ' => $ retryOnLimit ]);
219245 }
@@ -223,7 +249,7 @@ private function createSavedRequest($requestData, $retryOnLimit = false)
223249 *
224250 * @return array
225251 */
226- private function createCurlMultiHandle ($ requests )
252+ private function createCurlMultiHandle (array $ requests )
227253 {
228254 $ channels = [];
229255 $ multiHandle = curl_multi_init ();
@@ -241,68 +267,72 @@ private function createCurlMultiHandle($requests)
241267 /**
242268 * Prepare response object
243269 *
244- * @param resource $curl the curl resource
270+ * @param resource $channel the curl resource
271+ * @param string $content
245272 *
246273 * @return Response object
247274 */
248- private function prepareResponse ( $ curl )
275+ private function parseResponse ( $ channel , $ content )
249276 {
250- $ response = curl_exec ($ curl );
251- $ headerSize = curl_getinfo ($ curl , CURLINFO_HEADER_SIZE );
252- $ statusCode = curl_getinfo ($ curl , CURLINFO_HTTP_CODE );
253- $ responseBody = substr ($ response , $ headerSize );
254- $ responseHeaders = substr ($ response , 0 , $ headerSize );
277+ $ headerSize = curl_getinfo ($ channel , CURLINFO_HEADER_SIZE );
278+ $ statusCode = curl_getinfo ($ channel , CURLINFO_HTTP_CODE );
279+
280+ $ responseBody = substr ($ content , $ headerSize );
281+
282+ $ responseHeaders = substr ($ content , 0 , $ headerSize );
255283 $ responseHeaders = explode ("\n" , $ responseHeaders );
256284 $ responseHeaders = array_map ('trim ' , $ responseHeaders );
257- $ response = new Response ( $ statusCode , $ responseBody , $ responseHeaders );
258- return $ response ;
285+
286+ return new Response ( $ statusCode , $ responseBody , $ responseHeaders ) ;
259287 }
260-
288+
261289 /**
262290 * Retry request
263291 *
264- * @param array $responseHeaders headers from rate limited response
265- * @param string $method the HTTP verb
266- * @param string $url the final url to call
267- * @param array $body request body
268- * @param array $headers original headers
292+ * @param array $responseHeaders headers from rate limited response
293+ * @param string $method the HTTP verb
294+ * @param string $url the final url to call
295+ * @param array $body request body
296+ * @param array $headers original headers
269297 *
270298 * @return Response response object
271299 */
272- private function retryRequest ($ responseHeaders , $ method , $ url , $ body , $ headers )
300+ private function retryRequest (array $ responseHeaders , $ method , $ url , $ body , $ headers )
273301 {
274302 $ sleepDurations = $ responseHeaders ['X-Ratelimit-Reset ' ] - time ();
275303 sleep ($ sleepDurations > 0 ? $ sleepDurations : 0 );
276304 return $ this ->makeRequest ($ method , $ url , $ body , $ headers , false );
277305 }
278306
279307 /**
280- * Make the API call and return the response. This is separated into
281- * it's own function, so we can mock it easily for testing.
282- *
283- * @param string $method the HTTP verb
284- * @param string $url the final url to call
285- * @param array|\JsonSerializable $body request body
286- * @param array $headers any additional request headers
287- * @param bool $retryOnLimit should retry if rate limit is reach?
288- *
289- * @return Response object
290- */
308+ * Make the API call and return the response.
309+ * This is separated into it's own function, so we can mock it easily for testing.
310+ *
311+ * @param string $method the HTTP verb
312+ * @param string $url the final url to call
313+ * @param array $body request body
314+ * @param array $headers any additional request headers
315+ * @param bool $retryOnLimit should retry if rate limit is reach?
316+ *
317+ * @return Response object
318+ */
291319 public function makeRequest ($ method , $ url , $ body = null , $ headers = null , $ retryOnLimit = false )
292320 {
293- $ curl = curl_init ($ url );
321+ $ channel = curl_init ($ url );
294322
295- $ curlOpts = $ this ->createCurlOptions ($ method , $ body , $ headers );
323+ $ options = $ this ->createCurlOptions ($ method , $ body , $ headers );
296324
297- curl_setopt_array ($ curl , $ curlOpts );
325+ curl_setopt_array ($ channel , $ options );
326+ $ content = curl_exec ($ channel );
298327
299- $ response = $ this ->prepareResponse ( $ curl );
328+ $ response = $ this ->parseResponse ( $ channel , $ content );
300329
301- if ($ response ->statusCode () == 429 && $ retryOnLimit ) {
302- return $ this ->retryRequest ($ response ->headers (true ), $ method , $ url , $ body , $ headers );
330+ if ($ response ->statusCode () === self ::TOO_MANY_REQUESTS_HTTP_CODE && $ retryOnLimit ) {
331+ $ responseHeaders = $ response ->headers (true );
332+ return $ this ->retryRequest ($ responseHeaders , $ method , $ url , $ body , $ headers );
303333 }
304334
305- curl_close ($ curl );
335+ curl_close ($ channel );
306336
307337 return $ response ;
308338 }
@@ -311,9 +341,10 @@ public function makeRequest($method, $url, $body = null, $headers = null, $retry
311341 * Send all saved requests at once
312342 *
313343 * @param array $requests
344+ *
314345 * @return Response[]
315346 */
316- public function makeAllRequests ($ requests = [])
347+ public function makeAllRequests (array $ requests = [])
317348 {
318349 if (empty ($ requests )) {
319350 $ requests = $ this ->savedRequests ;
@@ -330,32 +361,26 @@ public function makeAllRequests($requests = [])
330361 $ retryRequests = [];
331362 $ responses = [];
332363 $ sleepDurations = 0 ;
333- foreach ($ channels as $ id => $ ch ) {
334- $ response = curl_multi_getcontent ($ ch );
335- $ headerSize = curl_getinfo ($ ch , CURLINFO_HEADER_SIZE );
336- $ statusCode = curl_getinfo ($ ch , CURLINFO_HTTP_CODE );
337- $ responseBody = substr ($ response , $ headerSize );
338-
339- $ responseHeaders = substr ($ response , 0 , $ headerSize );
340- $ responseHeaders = explode ("\n" , $ responseHeaders );
341- $ responseHeaders = array_map ('trim ' , $ responseHeaders );
342-
343- $ response = new Response ($ statusCode , $ responseBody , $ responseHeaders );
344- if (($ statusCode === 429 ) && $ requests [$ id ]['retryOnLimit ' ]) {
364+ foreach ($ channels as $ id => $ channel ) {
365+
366+ $ content = curl_multi_getcontent ($ channel );
367+ $ response = $ this ->parseResponse ($ channel , $ content );
368+
369+ if ($ response ->statusCode () === self ::TOO_MANY_REQUESTS_HTTP_CODE && $ requests [$ id ]['retryOnLimit ' ]) {
345370 $ headers = $ response ->headers (true );
346371 $ sleepDurations = max ($ sleepDurations , $ headers ['X-Ratelimit-Reset ' ] - time ());
347372 $ requestData = [
348373 'method ' => $ requests [$ id ]['method ' ],
349374 'url ' => $ requests [$ id ]['url ' ],
350375 'body ' => $ requests [$ id ]['body ' ],
351- 'headers ' =>$ headers ,
376+ 'headers ' => $ headers ,
352377 ];
353378 $ retryRequests [] = $ this ->createSavedRequest ($ requestData , false );
354379 } else {
355380 $ responses [] = $ response ;
356381 }
357382
358- curl_multi_remove_handle ($ multiHandle , $ ch );
383+ curl_multi_remove_handle ($ multiHandle , $ channel );
359384 }
360385 curl_multi_close ($ multiHandle );
361386
@@ -368,15 +393,13 @@ public function makeAllRequests($requests = [])
368393 }
369394
370395 /**
371- * Add variable values to the url.
372- * (e.g. /your/api/{variable_value}/call)
373- * Another example: if you have a PHP reserved word, such as and,
374- * in your url, you must use this method.
375- *
376- * @param string $name name of the url segment
377- *
378- * @return Client object
379- */
396+ * Add variable values to the url. (e.g. /your/api/{variable_value}/call)
397+ * Another example: if you have a PHP reserved word, such as and, in your url, you must use this method.
398+ *
399+ * @param string $name name of the url segment
400+ *
401+ * @return Client object
402+ */
380403 public function _ ($ name = null )
381404 {
382405 if (isset ($ name )) {
@@ -391,14 +414,14 @@ public function _($name = null)
391414 }
392415
393416 /**
394- * Dynamically add method calls to the url, then call a method.
395- * (e.g. client.name.name.method())
396- *
397- * @param string $name name of the dynamic method call or HTTP verb
398- * @param array $args parameters passed with the method call
399- *
400- * @return Client|Response|Response[]|null object
401- */
417+ * Dynamically add method calls to the url, then call a method.
418+ * (e.g. client.name.name.method())
419+ *
420+ * @param string $name name of the dynamic method call or HTTP verb
421+ * @param array $args parameters passed with the method call
422+ *
423+ * @return Client|Response|Response[]|null object
424+ */
402425 public function __call ($ name , $ args )
403426 {
404427 $ name = strtolower ($ name );
@@ -422,10 +445,8 @@ public function __call($name, $args)
422445
423446 if ($ this ->isConcurrentRequest ) {
424447 // save request to be sent later
425- $ this ->savedRequests [] = $ this ->createSavedRequest (
426- ['method ' => $ name , 'url ' => $ url , 'body ' => $ body , 'headers ' => $ headers ],
427- $ retryOnLimit
428- );
448+ $ requestData = ['method ' => $ name , 'url ' => $ url , 'body ' => $ body , 'headers ' => $ headers ];
449+ $ this ->savedRequests [] = $ this ->createSavedRequest ($ requestData , $ retryOnLimit );
429450 return null ;
430451 }
431452
0 commit comments