|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace ElfSundae; |
|
4
|
|
|
|
|
5
|
|
|
use Exception; |
|
6
|
|
|
use GuzzleHttp\Client; |
|
7
|
|
|
use Illuminate\Support\Arr; |
|
8
|
|
|
use Illuminate\Support\Str; |
|
9
|
|
|
|
|
10
|
|
|
class HttpClient |
|
11
|
|
|
{ |
|
12
|
|
|
/** |
|
13
|
|
|
* The Guzzle client. |
|
14
|
|
|
* |
|
15
|
|
|
* @var \GuzzleHttp\Client |
|
16
|
|
|
*/ |
|
17
|
|
|
protected $client; |
|
18
|
|
|
|
|
19
|
|
|
/** |
|
20
|
|
|
* The Guzzle response. |
|
21
|
|
|
* |
|
22
|
|
|
* @var \GuzzleHttp\Psr7\Response |
|
23
|
|
|
*/ |
|
24
|
|
|
protected $response; |
|
25
|
|
|
|
|
26
|
|
|
/** |
|
27
|
|
|
* The request options. |
|
28
|
|
|
* |
|
29
|
|
|
* @var array |
|
30
|
|
|
*/ |
|
31
|
|
|
protected $options = [ |
|
32
|
|
|
'connect_timeout' => 5, |
|
33
|
|
|
'timeout' => 25, |
|
34
|
|
|
]; |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* Indicates catching Guzzle exceptions. |
|
38
|
|
|
* |
|
39
|
|
|
* @var bool |
|
40
|
|
|
*/ |
|
41
|
|
|
protected $withExceptions = false; |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* The shared http client. |
|
45
|
|
|
* |
|
46
|
|
|
* @var static |
|
47
|
|
|
*/ |
|
48
|
|
|
protected static $sharedClient; |
|
49
|
|
|
|
|
50
|
|
|
/** |
|
51
|
|
|
* Get the shared http client. |
|
52
|
|
|
* |
|
53
|
|
|
* @return static |
|
54
|
|
|
*/ |
|
55
|
|
|
public static function client() |
|
56
|
|
|
{ |
|
57
|
|
|
return static::$sharedClient ?: static::$sharedClient = new static; |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* Handle class methods. |
|
62
|
|
|
* |
|
63
|
|
|
* @param string $method |
|
64
|
|
|
* @param array $args |
|
65
|
|
|
* @return mixed |
|
66
|
|
|
*/ |
|
67
|
|
|
public static function __callStatic($method, $args) |
|
68
|
|
|
{ |
|
69
|
|
|
return call_user_func_array([static::$sharedClient, $method], $args); |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* Create a http client instance. |
|
74
|
|
|
* |
|
75
|
|
|
* @param array|string $config base_uri or any request options |
|
76
|
|
|
*/ |
|
77
|
|
|
public function __construct($config = null) |
|
78
|
|
|
{ |
|
79
|
|
|
if (is_string($config)) { |
|
80
|
|
|
$this->options(['base_uri' => $config]); |
|
81
|
|
|
} elseif (is_array($config)) { |
|
82
|
|
|
$this->options($config); |
|
83
|
|
|
} |
|
84
|
|
|
|
|
85
|
|
|
$this->client = new Client($this->options); |
|
86
|
|
|
} |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* Get the Guzzle client instance. |
|
90
|
|
|
* |
|
91
|
|
|
* @return \GuzzleHttp\Client |
|
92
|
|
|
*/ |
|
93
|
|
|
public function getClient() |
|
94
|
|
|
{ |
|
95
|
|
|
return $this->client; |
|
96
|
|
|
} |
|
97
|
|
|
|
|
98
|
|
|
/** |
|
99
|
|
|
* Trun on/off Guzzle exceptions. |
|
100
|
|
|
* |
|
101
|
|
|
* @param bool $throws |
|
102
|
|
|
* @return $this |
|
103
|
|
|
*/ |
|
104
|
|
|
public function withExceptions($throws) |
|
105
|
|
|
{ |
|
106
|
|
|
$this->withExceptions = (bool) $throws; |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
|
|
/** |
|
110
|
|
|
* Get the request options. |
|
111
|
|
|
* |
|
112
|
|
|
* @return array |
|
113
|
|
|
*/ |
|
114
|
|
|
public function getOptions() |
|
115
|
|
|
{ |
|
116
|
|
|
return $this->options; |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
/** |
|
120
|
|
|
* Merge request options. |
|
121
|
|
|
* |
|
122
|
|
|
* @param array $options |
|
123
|
|
|
* @return $this |
|
124
|
|
|
*/ |
|
125
|
|
|
public function options(array ...$options) |
|
126
|
|
|
{ |
|
127
|
|
|
$this->options = array_merge_recursive($this->options, ...$options); |
|
128
|
|
|
|
|
129
|
|
|
return $this; |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
/** |
|
133
|
|
|
* Remove one or many options using "dot" notation. |
|
134
|
|
|
* |
|
135
|
|
|
* @param string|array|null $key |
|
136
|
|
|
* @return $this |
|
137
|
|
|
*/ |
|
138
|
|
|
public function removeOptions($key) |
|
139
|
|
|
{ |
|
140
|
|
|
if (is_null($key)) { |
|
141
|
|
|
$this->options = []; |
|
142
|
|
|
} |
|
143
|
|
|
|
|
144
|
|
|
Arr::forget($this->options, ...$key); |
|
|
|
|
|
|
145
|
|
|
|
|
146
|
|
|
return $this; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Set a request option using "dot" notation. |
|
151
|
|
|
* |
|
152
|
|
|
* @param string $key |
|
153
|
|
|
* @param mixed $value |
|
154
|
|
|
* @return $this |
|
155
|
|
|
*/ |
|
156
|
|
|
public function option($key, $value) |
|
157
|
|
|
{ |
|
158
|
|
|
if ($key) { |
|
159
|
|
|
Arr::set($this->options, $key, $value); |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
return $this; |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
/** |
|
166
|
|
|
* Set the request header. |
|
167
|
|
|
* |
|
168
|
|
|
* @param string $name |
|
169
|
|
|
* @param mixed $value |
|
170
|
|
|
* @return $this |
|
171
|
|
|
*/ |
|
172
|
|
|
public function header($name, $value) |
|
173
|
|
|
{ |
|
174
|
|
|
return $this->option('headers.'.$name, $value); |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* Set the request content type. |
|
179
|
|
|
* |
|
180
|
|
|
* @param string $type |
|
181
|
|
|
* @return $this |
|
182
|
|
|
*/ |
|
183
|
|
|
public function contentType($type) |
|
184
|
|
|
{ |
|
185
|
|
|
return $this->header('Content-Type', $type); |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
/** |
|
189
|
|
|
* Set the request accept type. |
|
190
|
|
|
* |
|
191
|
|
|
* @param string $type |
|
192
|
|
|
* @return $this |
|
193
|
|
|
*/ |
|
194
|
|
|
public function accept($type) |
|
195
|
|
|
{ |
|
196
|
|
|
return $this->header('Accept', $type); |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
/** |
|
200
|
|
|
* Set the request accept type to JSON. |
|
201
|
|
|
* |
|
202
|
|
|
* @return $this |
|
203
|
|
|
*/ |
|
204
|
|
|
public function acceptJson() |
|
205
|
|
|
{ |
|
206
|
|
|
return $this->accept('application/json'); |
|
207
|
|
|
} |
|
208
|
|
|
|
|
209
|
|
|
/** |
|
210
|
|
|
* Specify where the body of a response will be saved. |
|
211
|
|
|
* Set the "sink" option. |
|
212
|
|
|
* |
|
213
|
|
|
* @param mixed $value |
|
214
|
|
|
* @return $this |
|
215
|
|
|
*/ |
|
216
|
|
|
public function saveTo($value) |
|
217
|
|
|
{ |
|
218
|
|
|
return $this->option('sink', $value); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
/** |
|
222
|
|
|
* Get the Guzzle response instance. |
|
223
|
|
|
* |
|
224
|
|
|
* @return \GuzzleHttp\Psr7\Response|null |
|
225
|
|
|
*/ |
|
226
|
|
|
public function getResponse() |
|
227
|
|
|
{ |
|
228
|
|
|
return $this->response; |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
/** |
|
232
|
|
|
* Get the status code of response. |
|
233
|
|
|
* |
|
234
|
|
|
* @return int |
|
235
|
|
|
*/ |
|
236
|
|
|
public function getStatusCode() |
|
237
|
|
|
{ |
|
238
|
|
|
if ($this->response) { |
|
239
|
|
|
return $this->response->getStatusCode(); |
|
240
|
|
|
} |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
/** |
|
244
|
|
|
* Get the response header value. |
|
245
|
|
|
* |
|
246
|
|
|
* @param string $name |
|
247
|
|
|
* @return mixed |
|
248
|
|
|
*/ |
|
249
|
|
|
public function getHeader($name) |
|
250
|
|
|
{ |
|
251
|
|
|
if ($this->response) { |
|
252
|
|
|
return $this->response->getHeaderLine($name); |
|
253
|
|
|
} |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* Get all response headers values. |
|
258
|
|
|
* |
|
259
|
|
|
* @return array |
|
260
|
|
|
*/ |
|
261
|
|
|
public function getHeaders() |
|
262
|
|
|
{ |
|
263
|
|
|
return $this->response ? $this->response->getHeaders() : []; |
|
264
|
|
|
} |
|
265
|
|
|
|
|
266
|
|
|
/** |
|
267
|
|
|
* Get response body. |
|
268
|
|
|
* |
|
269
|
|
|
* @return \GuzzleHttp\Psr7\Stream|null |
|
270
|
|
|
*/ |
|
271
|
|
|
public function getBody() |
|
272
|
|
|
{ |
|
273
|
|
|
if ($this->response) { |
|
274
|
|
|
return $this->response->getBody(); |
|
275
|
|
|
} |
|
276
|
|
|
} |
|
277
|
|
|
|
|
278
|
|
|
/** |
|
279
|
|
|
* Get response content. |
|
280
|
|
|
* |
|
281
|
|
|
* @return string|null |
|
282
|
|
|
*/ |
|
283
|
|
|
public function getContent() |
|
284
|
|
|
{ |
|
285
|
|
|
if ($body = $this->getBody()) { |
|
286
|
|
|
return (string) $body; |
|
287
|
|
|
} |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
/** |
|
291
|
|
|
* Get JSON decoded response content. |
|
292
|
|
|
* |
|
293
|
|
|
* @param bool $assoc |
|
294
|
|
|
* @return mixed |
|
295
|
|
|
*/ |
|
296
|
|
|
public function getJson($assoc = true) |
|
297
|
|
|
{ |
|
298
|
|
|
if ($content = $this->getContent()) { |
|
299
|
|
|
return json_decode($content, $assoc); |
|
300
|
|
|
} |
|
301
|
|
|
} |
|
302
|
|
|
|
|
303
|
|
|
/** |
|
304
|
|
|
* Make request to an URL. |
|
305
|
|
|
* |
|
306
|
|
|
* @param string $url |
|
307
|
|
|
* @param string $method |
|
308
|
|
|
* @param array $options |
|
309
|
|
|
* @return $this |
|
310
|
|
|
*/ |
|
311
|
|
|
public function request($url, $method = 'GET', $options = []) |
|
312
|
|
|
{ |
|
313
|
|
|
$options = array_merge_recursive($this->options, $options); |
|
314
|
|
|
|
|
315
|
|
|
try { |
|
316
|
|
|
$this->response = $this->client->request($method, $url, $options); |
|
317
|
|
|
} catch (Exception $e) { |
|
318
|
|
|
if ($this->withExceptions) { |
|
319
|
|
|
throw $e; |
|
320
|
|
|
} |
|
321
|
|
|
} |
|
322
|
|
|
|
|
323
|
|
|
return $this; |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
/** |
|
327
|
|
|
* Make request to an URL, expecting JSON content. |
|
328
|
|
|
* |
|
329
|
|
|
* @param string $url |
|
330
|
|
|
* @param string $method |
|
331
|
|
|
* @param array $options |
|
332
|
|
|
* @return $this |
|
333
|
|
|
*/ |
|
334
|
|
|
public function requestJson($url, $method = 'GET', $options = []) |
|
335
|
|
|
{ |
|
336
|
|
|
Arr::set($options, 'headers.Accept', 'application/json'); |
|
337
|
|
|
|
|
338
|
|
|
return $this->request($url, $method, $options); |
|
339
|
|
|
} |
|
340
|
|
|
|
|
341
|
|
|
/** |
|
342
|
|
|
* Request the URL and return the response content. |
|
343
|
|
|
* |
|
344
|
|
|
* @param string $url |
|
345
|
|
|
* @param string $method |
|
346
|
|
|
* @param array $options |
|
347
|
|
|
* @return string|null |
|
348
|
|
|
*/ |
|
349
|
|
|
public function fetchContent($url, $method = 'GET', $options = []) |
|
350
|
|
|
{ |
|
351
|
|
|
return $this->request($url, $method, $options)->getContent(); |
|
352
|
|
|
} |
|
353
|
|
|
|
|
354
|
|
|
/** |
|
355
|
|
|
* Request the URL and return the JSON decoded response content. |
|
356
|
|
|
* |
|
357
|
|
|
* @param string $url |
|
358
|
|
|
* @param string $method |
|
359
|
|
|
* @param array $options |
|
360
|
|
|
* @param bool $assoc |
|
361
|
|
|
* @return mixed |
|
362
|
|
|
*/ |
|
363
|
|
|
public function fetchJson($url, $method = 'GET', $options = [], $assoc = true) |
|
364
|
|
|
{ |
|
365
|
|
|
return $this->requestJson($url, $method, $options)->getJson($assoc); |
|
366
|
|
|
} |
|
367
|
|
|
|
|
368
|
|
|
/** |
|
369
|
|
|
* Any unhandled methods will be sent to $this->option() to set request option. |
|
370
|
|
|
* |
|
371
|
|
|
* @param string $name |
|
372
|
|
|
* @param array $args |
|
373
|
|
|
* @return $this |
|
374
|
|
|
*/ |
|
375
|
|
|
public function __call($name, $args) |
|
376
|
|
|
{ |
|
377
|
|
|
return $this->option(Str::snake($name), $args[0]); |
|
378
|
|
|
} |
|
379
|
|
|
} |
|
380
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.