11<?php
22
33/**
4- * HTTP Client library
5- *
6- * @author Matt Bernier <dx@sendgrid.com>
7- * @author Elmer Thomas <dx@sendgrid.com>
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 <dx@sendgrid.com>
7+ * @author Elmer Thomas <dx@sendgrid.com>
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 /**
55- * Initialize the client
56- *
57- * @param string $host the base url (e.g. https://api.sendgrid.com)
58- * @param array $headers global request headers
59- * @param string $version api version (configurable)
60- * @param array $path holds the segments of the url path
61- */
62- public function __construct ($ host , $ headers = [], $ version = '/v3 ' , $ path = [])
80+ * Initialize the client
81+ *
82+ * @param string $host the base url (e.g. https://api.sendgrid.com)
83+ * @param array $headers global request headers
84+ * @param string $version api version (configurable)
85+ * @param array $path holds the segments of the url path
86+ */
87+ public function __construct ($ host , array $ headers = [], $ version = '/v3 ' , array $ path = [])
6388 {
6489 $ this ->host = $ host ;
6590 $ this ->headers = $ headers ;
@@ -104,6 +129,14 @@ public function getPath()
104129 return $ this ->path ;
105130 }
106131
132+ /**
133+ * @return array
134+ */
135+ public function getCurlOptions ()
136+ {
137+ return $ this ->curlOptions ;
138+ }
139+
107140 /**
108141 * Set extra options to set during curl initialization
109142 *
@@ -133,7 +166,7 @@ public function setRetryOnLimit($retry)
133166 }
134167
135168 /**
136- * set concurrent request flag
169+ * Set concurrent request flag
137170 *
138171 * @param bool $isConcurrent
139172 *
@@ -147,20 +180,12 @@ public function setIsConcurrentRequest($isConcurrent)
147180 }
148181
149182 /**
150- * @return array
183+ * Build the final URL to be passed
184+ *
185+ * @param array $queryParams an array of all the query parameters
186+ *
187+ * @return string
151188 */
152- public function getCurlOptions ()
153- {
154- return $ this ->curlOptions ;
155- }
156-
157- /**
158- * Build the final URL to be passed
159- *
160- * @param array $queryParams an array of all the query parameters
161- *
162- * @return string
163- */
164189 private function buildUrl ($ queryParams = null )
165190 {
166191 $ path = '/ ' . implode ('/ ' , $ this ->path );
@@ -183,7 +208,7 @@ private function createCurlOptions($method, $body = null, $headers = null)
183208 {
184209 $ options = [
185210 CURLOPT_RETURNTRANSFER => true ,
186- CURLOPT_HEADER => 1 ,
211+ CURLOPT_HEADER => true ,
187212 CURLOPT_CUSTOMREQUEST => strtoupper ($ method ),
188213 CURLOPT_SSL_VERIFYPEER => true ,
189214 CURLOPT_FAILONERROR => false
@@ -212,7 +237,7 @@ private function createCurlOptions($method, $body = null, $headers = null)
212237 *
213238 * @return array
214239 */
215- private function createSavedRequest ($ requestData , $ retryOnLimit = false )
240+ private function createSavedRequest (array $ requestData , $ retryOnLimit = false )
216241 {
217242 return array_merge ($ requestData , ['retryOnLimit ' => $ retryOnLimit ]);
218243 }
@@ -222,7 +247,7 @@ private function createSavedRequest($requestData, $retryOnLimit = false)
222247 *
223248 * @return array
224249 */
225- private function createCurlMultiHandle ($ requests )
250+ private function createCurlMultiHandle (array $ requests )
226251 {
227252 $ channels = [];
228253 $ multiHandle = curl_multi_init ();
@@ -240,68 +265,72 @@ private function createCurlMultiHandle($requests)
240265 /**
241266 * Prepare response object
242267 *
243- * @param resource $curl the curl resource
268+ * @param resource $channel the curl resource
269+ * @param string $content
244270 *
245271 * @return Response object
246272 */
247- private function prepareResponse ( $ curl )
273+ private function parseResponse ( $ channel , $ content )
248274 {
249- $ response = curl_exec ($ curl );
250- $ headerSize = curl_getinfo ($ curl , CURLINFO_HEADER_SIZE );
251- $ statusCode = curl_getinfo ($ curl , CURLINFO_HTTP_CODE );
252- $ responseBody = substr ($ response , $ headerSize );
253- $ responseHeaders = substr ($ response , 0 , $ headerSize );
275+ $ headerSize = curl_getinfo ($ channel , CURLINFO_HEADER_SIZE );
276+ $ statusCode = curl_getinfo ($ channel , CURLINFO_HTTP_CODE );
277+
278+ $ responseBody = substr ($ content , $ headerSize );
279+
280+ $ responseHeaders = substr ($ content , 0 , $ headerSize );
254281 $ responseHeaders = explode ("\n" , $ responseHeaders );
255282 $ responseHeaders = array_map ('trim ' , $ responseHeaders );
256- $ response = new Response ( $ statusCode , $ responseBody , $ responseHeaders );
257- return $ response ;
283+
284+ return new Response ( $ statusCode , $ responseBody , $ responseHeaders ) ;
258285 }
259-
286+
260287 /**
261288 * Retry request
262289 *
263- * @param array $responseHeaders headers from rate limited response
264- * @param string $method the HTTP verb
265- * @param string $url the final url to call
266- * @param array $body request body
267- * @param array $headers original headers
290+ * @param array $responseHeaders headers from rate limited response
291+ * @param string $method the HTTP verb
292+ * @param string $url the final url to call
293+ * @param array $body request body
294+ * @param array $headers original headers
268295 *
269296 * @return Response response object
270297 */
271- private function retryRequest ($ responseHeaders , $ method , $ url , $ body , $ headers )
298+ private function retryRequest (array $ responseHeaders , $ method , $ url , $ body , $ headers )
272299 {
273300 $ sleepDurations = $ responseHeaders ['X-Ratelimit-Reset ' ] - time ();
274301 sleep ($ sleepDurations > 0 ? $ sleepDurations : 0 );
275302 return $ this ->makeRequest ($ method , $ url , $ body , $ headers , false );
276303 }
277304
278305 /**
279- * Make the API call and return the response. This is separated into
280- * it's own function, so we can mock it easily for testing.
281- *
282- * @param string $method the HTTP verb
283- * @param string $url the final url to call
284- * @param array|\JsonSerializable $body request body
285- * @param array $headers any additional request headers
286- * @param bool $retryOnLimit should retry if rate limit is reach?
287- *
288- * @return Response object
289- */
306+ * Make the API call and return the response.
307+ * This is separated into it's own function, so we can mock it easily for testing.
308+ *
309+ * @param string $method the HTTP verb
310+ * @param string $url the final url to call
311+ * @param array $body request body
312+ * @param array $headers any additional request headers
313+ * @param bool $retryOnLimit should retry if rate limit is reach?
314+ *
315+ * @return Response object
316+ */
290317 public function makeRequest ($ method , $ url , $ body = null , $ headers = null , $ retryOnLimit = false )
291318 {
292- $ curl = curl_init ($ url );
319+ $ channel = curl_init ($ url );
293320
294- $ curlOpts = $ this ->createCurlOptions ($ method , $ body , $ headers );
321+ $ options = $ this ->createCurlOptions ($ method , $ body , $ headers );
295322
296- curl_setopt_array ($ curl , $ curlOpts );
323+ curl_setopt_array ($ channel , $ options );
324+ $ content = curl_exec ($ channel );
297325
298- $ response = $ this ->prepareResponse ( $ curl );
326+ $ response = $ this ->parseResponse ( $ channel , $ content );
299327
300- if ($ response ->statusCode () == 429 && $ retryOnLimit ) {
301- return $ this ->retryRequest ($ response ->headers (true ), $ method , $ url , $ body , $ headers );
328+ if ($ response ->statusCode () === self ::TOO_MANY_REQUESTS_HTTP_CODE && $ retryOnLimit ) {
329+ $ responseHeaders = $ response ->headers (true );
330+ return $ this ->retryRequest ($ responseHeaders , $ method , $ url , $ body , $ headers );
302331 }
303332
304- curl_close ($ curl );
333+ curl_close ($ channel );
305334
306335 return $ response ;
307336 }
@@ -312,7 +341,7 @@ public function makeRequest($method, $url, $body = null, $headers = null, $retry
312341 * @param array $requests
313342 * @return Response[]
314343 */
315- public function makeAllRequests ($ requests = [])
344+ public function makeAllRequests (array $ requests = [])
316345 {
317346 if (empty ($ requests )) {
318347 $ requests = $ this ->savedRequests ;
@@ -329,32 +358,26 @@ public function makeAllRequests($requests = [])
329358 $ retryRequests = [];
330359 $ responses = [];
331360 $ sleepDurations = 0 ;
332- foreach ($ channels as $ id => $ ch ) {
333- $ response = curl_multi_getcontent ($ ch );
334- $ headerSize = curl_getinfo ($ ch , CURLINFO_HEADER_SIZE );
335- $ statusCode = curl_getinfo ($ ch , CURLINFO_HTTP_CODE );
336- $ responseBody = substr ($ response , $ headerSize );
337-
338- $ responseHeaders = substr ($ response , 0 , $ headerSize );
339- $ responseHeaders = explode ("\n" , $ responseHeaders );
340- $ responseHeaders = array_map ('trim ' , $ responseHeaders );
341-
342- $ response = new Response ($ statusCode , $ responseBody , $ responseHeaders );
343- if (($ statusCode === 429 ) && $ requests [$ id ]['retryOnLimit ' ]) {
361+ foreach ($ channels as $ id => $ channel ) {
362+
363+ $ content = curl_multi_getcontent ($ channel );
364+ $ response = $ this ->parseResponse ($ channel , $ content );
365+
366+ if ($ response ->statusCode () === self ::TOO_MANY_REQUESTS_HTTP_CODE && $ requests [$ id ]['retryOnLimit ' ]) {
344367 $ headers = $ response ->headers (true );
345368 $ sleepDurations = max ($ sleepDurations , $ headers ['X-Ratelimit-Reset ' ] - time ());
346369 $ requestData = [
347370 'method ' => $ requests [$ id ]['method ' ],
348371 'url ' => $ requests [$ id ]['url ' ],
349372 'body ' => $ requests [$ id ]['body ' ],
350- 'headers ' =>$ headers ,
373+ 'headers ' => $ headers ,
351374 ];
352375 $ retryRequests [] = $ this ->createSavedRequest ($ requestData , false );
353376 } else {
354377 $ responses [] = $ response ;
355378 }
356379
357- curl_multi_remove_handle ($ multiHandle , $ ch );
380+ curl_multi_remove_handle ($ multiHandle , $ channel );
358381 }
359382 curl_multi_close ($ multiHandle );
360383
@@ -367,15 +390,13 @@ public function makeAllRequests($requests = [])
367390 }
368391
369392 /**
370- * Add variable values to the url.
371- * (e.g. /your/api/{variable_value}/call)
372- * Another example: if you have a PHP reserved word, such as and,
373- * in your url, you must use this method.
374- *
375- * @param string $name name of the url segment
376- *
377- * @return Client object
378- */
393+ * Add variable values to the url. (e.g. /your/api/{variable_value}/call)
394+ * Another example: if you have a PHP reserved word, such as and, in your url, you must use this method.
395+ *
396+ * @param string $name name of the url segment
397+ *
398+ * @return Client object
399+ */
379400 public function _ ($ name = null )
380401 {
381402 if (isset ($ name )) {
@@ -390,14 +411,14 @@ public function _($name = null)
390411 }
391412
392413 /**
393- * Dynamically add method calls to the url, then call a method.
394- * (e.g. client.name.name.method())
395- *
396- * @param string $name name of the dynamic method call or HTTP verb
397- * @param array $args parameters passed with the method call
398- *
399- * @return Client|Response|Response[]|null object
400- */
414+ * Dynamically add method calls to the url, then call a method.
415+ * (e.g. client.name.name.method())
416+ *
417+ * @param string $name name of the dynamic method call or HTTP verb
418+ * @param array $args parameters passed with the method call
419+ *
420+ * @return Client|Response|Response[]|null object
421+ */
401422 public function __call ($ name , $ args )
402423 {
403424 $ name = strtolower ($ name );
@@ -421,10 +442,8 @@ public function __call($name, $args)
421442
422443 if ($ this ->isConcurrentRequest ) {
423444 // save request to be sent later
424- $ this ->savedRequests [] = $ this ->createSavedRequest (
425- ['method ' => $ name , 'url ' => $ url , 'body ' => $ body , 'headers ' => $ headers ],
426- $ retryOnLimit
427- );
445+ $ requestData = ['method ' => $ name , 'url ' => $ url , 'body ' => $ body , 'headers ' => $ headers ];
446+ $ this ->savedRequests [] = $ this ->createSavedRequest ($ requestData , $ retryOnLimit );
428447 return null ;
429448 }
430449
0 commit comments