Skip to content

Commit ee8ca8d

Browse files
committed
CreateCurlOptions method tests added
1 parent 1d9aa9d commit ee8ca8d

File tree

5 files changed

+243
-147
lines changed

5 files changed

+243
-147
lines changed

lib/Client.php

Lines changed: 132 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
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

1414
namespace SendGrid;
1515

@@ -27,39 +27,64 @@
2727
*/
2828
class 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

Comments
 (0)