Completed
Push — master ( cc8235...b61d52 )
by Sean
03:14
created

RestClient::getHttpClient()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 5
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
crap 6
1
<?PHP
2
3
/*
4
 * Copyright (C) 2013-2016 Mailgun
5
 *
6
 * This software may be modified and distributed under the terms
7
 * of the MIT license. See the LICENSE file for details.
8
 */
9
10
namespace Mailgun\Connection;
11
12
use Http\Client\HttpClient;
13
use Http\Discovery\HttpClientDiscovery;
14
use Http\Discovery\MessageFactoryDiscovery;
15
use Http\Message\MultipartStream\MultipartStreamBuilder;
16
use Mailgun\Connection\Exceptions\GenericHTTPError;
17
use Mailgun\Connection\Exceptions\InvalidCredentials;
18
use Mailgun\Connection\Exceptions\MissingEndpoint;
19
use Mailgun\Connection\Exceptions\MissingRequiredParameters;
20
use Mailgun\Constants\Api;
21
use Mailgun\Constants\ExceptionMessages;
22
use Psr\Http\Message\ResponseInterface;
23
24
/**
25
 * This class is a wrapper for the HTTP client.
26
 */
27
class RestClient
28
{
29
    /**
30
     * Your API key.
31
     *
32
     * @var string
33
     */
34
    private $apiKey;
35
36
    /**
37
     * @var HttpClient
38
     */
39
    protected $httpClient;
40
41
    /**
42
     * @var string
43
     */
44
    protected $apiHost;
45
46
    /**
47
     * The version of the API to use.
48
     *
49
     * @var string
50
     */
51
    protected $apiVersion = 'v2';
52
53
    /**
54
     * If we should use SSL or not.
55
     *
56
     * @var bool
57
     *
58
     * @deprecated To be removed in 3.0
59
     */
60
    protected $sslEnabled = true;
61
62
    /**
63
     * @param string     $apiKey
64
     * @param string     $apiHost
65
     * @param HttpClient $httpClient
66
     */
67 5
    public function __construct($apiKey, $apiHost, HttpClient $httpClient = null)
68
    {
69 5
        $this->apiKey = $apiKey;
70 5
        $this->apiHost = $apiHost;
71 5
        $this->httpClient = $httpClient;
72 5
    }
73
74
    /**
75
     * @param string $method
76
     * @param string $uri
77
     * @param mixed  $body
78
     * @param array  $files
79
     * @param array  $headers
80
     *
81
     * @throws GenericHTTPError
82
     * @throws InvalidCredentials
83
     * @throws MissingEndpoint
84
     * @throws MissingRequiredParameters
85
     *
86
     * @return \stdClass
87
     */
88
    protected function send($method, $uri, $body = null, $files = [], array $headers = [])
89
    {
90
        $headers['User-Agent'] = Api::SDK_USER_AGENT.'/'.Api::SDK_VERSION;
91
        $headers['Authorization'] = 'Basic '.base64_encode(sprintf('%s:%s', Api::API_USER, $this->apiKey));
92
93
        if (!empty($files)) {
94
            $builder = new MultipartStreamBuilder();
95
            foreach ($files as $file) {
96
                $builder->addResource($file['name'], $file['contents'], $file);
97
            }
98
            $body = $builder->build();
99
            $headers['Content-Type'] = 'multipart/form-data; boundary='.$builder->getBoundary();
100
        } elseif (is_array($body)) {
101
            $body = http_build_query($body);
102
            $headers['Content-Type'] = 'application/x-www-form-urlencoded';
103
        }
104
105
        $request = MessageFactoryDiscovery::find()->createRequest($method, $this->getApiUrl($uri), $headers, $body);
106
        $response = $this->getHttpClient()->sendRequest($request);
107
108
        return $this->responseHandler($response);
109
    }
110
111
    /**
112
     * @param string $endpointUrl
113
     * @param array  $postData
114
     * @param array  $files
115
     *
116
     * @throws GenericHTTPError
117
     * @throws InvalidCredentials
118
     * @throws MissingEndpoint
119
     * @throws MissingRequiredParameters
120
     *
121
     * @return \stdClass
122
     */
123 5
    public function post($endpointUrl, array $postData = [], $files = [])
124
    {
125 5
        $postFiles = [];
126
127 5
        $fields = ['message', 'attachment', 'inline'];
128 5
        foreach ($fields as $fieldName) {
129 5 View Code Duplication
            if (isset($files[$fieldName])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130 4
                if (is_array($files[$fieldName])) {
131 4
                    $fileIndex = 0;
132 4
                    foreach ($files[$fieldName] as $file) {
133 4
                        $postFiles[] = $this->prepareFile($fieldName, $file, $fileIndex);
134 4
                        ++$fileIndex;
135 4
                    }
136 4
                } else {
137
                    $postFiles[] = $this->prepareFile($fieldName, $files[$fieldName]);
138
                }
139 4
            }
140 5
        }
141
142 5
        $postDataMultipart = [];
143 5 View Code Duplication
        foreach ($postData as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144 5
            if (is_array($value)) {
145 2
                $index = 0;
146 2
                foreach ($value as $subValue) {
147 2
                    $postDataMultipart[] = [
148 2
                        'name' => sprintf('%s[%d]', $key, $index++),
149 2
                        'contents' => $subValue,
150
                    ];
151 2
                }
152 2
            } else {
153 5
                $postDataMultipart[] = [
154 5
                    'name' => $key,
155 5
                    'contents' => $value,
156
                ];
157
            }
158 5
        }
159
160 5
        return $this->send('POST', $endpointUrl, [], array_merge($postDataMultipart, $postFiles));
161
    }
162
163
    /**
164
     * @param string $endpointUrl
165
     * @param array  $queryString
166
     *
167
     * @throws GenericHTTPError
168
     * @throws InvalidCredentials
169
     * @throws MissingEndpoint
170
     * @throws MissingRequiredParameters
171
     *
172
     * @return \stdClass
173
     */
174
    public function get($endpointUrl, $queryString = [])
175
    {
176
        return $this->send('GET', $endpointUrl.'?'.http_build_query($queryString));
177
    }
178
179
    /**
180
     * @param string $endpointUrl
181
     *
182
     * @throws GenericHTTPError
183
     * @throws InvalidCredentials
184
     * @throws MissingEndpoint
185
     * @throws MissingRequiredParameters
186
     *
187
     * @return \stdClass
188
     */
189
    public function delete($endpointUrl)
190
    {
191
        return $this->send('DELETE', $endpointUrl);
192
    }
193
194
    /**
195
     * @param string $endpointUrl
196
     * @param mixed  $putData
197
     *
198
     * @throws GenericHTTPError
199
     * @throws InvalidCredentials
200
     * @throws MissingEndpoint
201
     * @throws MissingRequiredParameters
202
     *
203
     * @return \stdClass
204
     */
205
    public function put($endpointUrl, $putData)
206
    {
207
        return $this->send('PUT', $endpointUrl, $putData);
208
    }
209
210
    /**
211
     * @param ResponseInterface $responseObj
212
     *
213
     * @throws GenericHTTPError
214
     * @throws InvalidCredentials
215
     * @throws MissingEndpoint
216
     * @throws MissingRequiredParameters
217
     *
218
     * @return \stdClass
219
     */
220
    public function responseHandler(ResponseInterface $responseObj)
221
    {
222
        $httpResponseCode = (int) $responseObj->getStatusCode();
223
224
        switch ($httpResponseCode) {
225
        case 200:
226
            $data = (string) $responseObj->getBody();
227
            $jsonResponseData = json_decode($data, false);
228
            $result = new \stdClass();
229
            // return response data as json if possible, raw if not
230
            $result->http_response_body = $data && $jsonResponseData === null ? $data : $jsonResponseData;
231
            $result->http_response_code = $httpResponseCode;
232
233
            return $result;
234
        case 400:
235
            throw new MissingRequiredParameters(ExceptionMessages::EXCEPTION_MISSING_REQUIRED_PARAMETERS.$this->getResponseExceptionMessage($responseObj));
236
        case 401:
237
            throw new InvalidCredentials(ExceptionMessages::EXCEPTION_INVALID_CREDENTIALS);
238
        case 404:
239
            throw new MissingEndpoint(ExceptionMessages::EXCEPTION_MISSING_ENDPOINT.$this->getResponseExceptionMessage($responseObj));
240
        default:
241
            throw new GenericHTTPError(ExceptionMessages::EXCEPTION_GENERIC_HTTP_ERROR, $httpResponseCode, $responseObj->getBody());
242
        }
243
    }
244
245
    /**
246
     * @param ResponseInterface $responseObj
247
     *
248
     * @return string
249
     */
250
    protected function getResponseExceptionMessage(ResponseInterface $responseObj)
251
    {
252
        $body = (string) $responseObj->getBody();
253
        $response = json_decode($body);
254
        if (json_last_error() == JSON_ERROR_NONE && isset($response->message)) {
255
            return ' '.$response->message;
256
        }
257
258
        return '';
259
    }
260
261
    /**
262
     * Prepare a file for the postBody.
263
     *
264
     * @param string       $fieldName
265
     * @param string|array $filePath
266
     * @param int          $fileIndex
267
     *
268
     * @return array
269
     */
270 4
    protected function prepareFile($fieldName, $filePath, $fileIndex = 0)
271
    {
272 4
        $filename = null;
273
274 4
        if (is_array($filePath) && isset($filePath['fileContent'])) {
275
            // File from memory
276 1
            $filename = $filePath['filename'];
277 1
            $resource = fopen('php://temp', 'r+');
278 1
            fwrite($resource, $filePath['fileContent']);
279 1
            rewind($resource);
280 1
        } else {
281
            // Backward compatibility code
282 4
            if (is_array($filePath) && isset($filePath['filePath'])) {
283 4
                $filename = $filePath['remoteName'];
284 4
                $filePath = $filePath['filePath'];
285 4
            }
286
287
            // Remove leading @ symbol
288 4
            if (strpos($filePath, '@') === 0) {
289 2
                $filePath = substr($filePath, 1);
290 2
            }
291
292 4
            $resource = fopen($filePath, 'r');
293
        }
294
295
        // Add index for multiple file support
296 4
        $fieldName .= '['.$fileIndex.']';
297
298
        return [
299 4
            'name' => $fieldName,
300 4
            'contents' => $resource,
301 4
            'filename' => $filename,
302 4
        ];
303
    }
304
305
    /**
306
     * @return HttpClient
307
     */
308
    protected function getHttpClient()
309
    {
310
        if ($this->httpClient === null) {
311
            $this->httpClient = HttpClientDiscovery::find();
312
        }
313
314
        return $this->httpClient;
315
    }
316
317
    /**
318
     * @param string $uri
319
     *
320
     * @return string
321
     */
322
    private function getApiUrl($uri)
323
    {
324
        return $this->generateEndpoint($this->apiHost, $this->apiVersion, $this->sslEnabled).$uri;
0 ignored issues
show
Deprecated Code introduced by
The property Mailgun\Connection\RestClient::$sslEnabled has been deprecated with message: To be removed in 3.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
325
    }
326
327
    /**
328
     * @param string $apiEndpoint
329
     * @param string $apiVersion
330
     * @param bool   $ssl
331
     *
332
     * @return string
333
     */
334
    private function generateEndpoint($apiEndpoint, $apiVersion, $ssl)
335
    {
336
        return ($ssl ? 'https://' : 'http://').$apiEndpoint.'/'.$apiVersion.'/';
337
    }
338
339
    /**
340
     * @param string $apiVersion
341
     *
342
     * @return RestClient
343
     */
344
    public function setApiVersion($apiVersion)
345
    {
346
        $this->apiVersion = $apiVersion;
347
348
        return $this;
349
    }
350
351
    /**
352
     * @param bool $sslEnabled
353
     *
354
     * @return RestClient
355
     *
356
     * @deprecated To be removed in 3.0
357
     */
358
    public function setSslEnabled($sslEnabled)
359
    {
360
        $this->sslEnabled = $sslEnabled;
0 ignored issues
show
Deprecated Code introduced by
The property Mailgun\Connection\RestClient::$sslEnabled has been deprecated with message: To be removed in 3.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
361
362
        return $this;
363
    }
364
}
365