|
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])) { |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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; |
|
|
|
|
|
|
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; |
|
|
|
|
|
|
361
|
|
|
|
|
362
|
|
|
return $this; |
|
363
|
|
|
} |
|
364
|
|
|
} |
|
365
|
|
|
|
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.