Passed
Push — master ( 17369b...488f9a )
by Elf
02:47
created

HttpClient::acceptJson()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace ElfSundae;
4
5
use Closure;
6
use Exception;
7
use ReflectionClass;
8
use GuzzleHttp\Client;
9
use BadMethodCallException;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Str;
12
use InvalidArgumentException;
13
use GuzzleHttp\RequestOptions;
14
use Psr\Http\Message\UriInterface;
15
16
/**
17
 * @method $this get(string|UriInterface $uri = '', array $options = [])
18
 * @method $this head(string|UriInterface $uri = '', array $options = [])
19
 * @method $this post(string|UriInterface $uri = '', array $options = [])
20
 * @method $this put(string|UriInterface $uri = '', array $options = [])
21
 * @method $this patch(string|UriInterface $uri = '', array $options = [])
22
 * @method $this delete(string|UriInterface $uri = '', array $options = [])
23
 * @method $this options(string|UriInterface $uri = '', array $options = [])
24
 * @method \GuzzleHttp\Promise\PromiseInterface getAsync(string|UriInterface $uri = '', array $options = [])
25
 * @method \GuzzleHttp\Promise\PromiseInterface headAsync(string|UriInterface $uri = '', array $options = [])
26
 * @method \GuzzleHttp\Promise\PromiseInterface postAsync(string|UriInterface $uri = '', array $options = [])
27
 * @method \GuzzleHttp\Promise\PromiseInterface putAsync(string|UriInterface $uri = '', array $options = [])
28
 * @method \GuzzleHttp\Promise\PromiseInterface patchAsync(string|UriInterface $uri = '', array $options = [])
29
 * @method \GuzzleHttp\Promise\PromiseInterface deleteAsync(string|UriInterface $uri = '', array $options = [])
30
 * @method \GuzzleHttp\Promise\PromiseInterface optionsAsync(string|UriInterface $uri = '', array $options = [])
31
 * @method int getStatusCode()
32
 * @method string getReasonPhrase()
33
 * @method string getProtocolVersion()
34
 * @method array getHeaders()
35
 * @method bool hasHeader(string $header)
36
 * @method array getHeader(string $header)
37
 * @method string getHeaderLine(string $header)
38
 * @method \Psr\Http\Message\StreamInterface getBody()
39
 * @method $this allowRedirects(bool|array $value)
40
 * @method $this auth(array|string|null $value)
41
 * @method $this body(mixed $value)
42
 * @method $this cert(string|array $value)
43
 * @method $this cookies(bool|\GuzzleHttp\Cookie\CookieJarInterface $value)
44
 * @method $this connectTimeout(float $value)
45
 * @method $this debug(bool|resource $value)
46
 * @method $this decodeContent(bool $value)
47
 * @method $this delay(int|float $value)
48
 * @method $this expect(bool|int $value)
49
 * @method $this formParams(array $value)
50
 * @method $this headers(array $value)
51
 * @method $this httpErrors(bool $value)
52
 * @method $this json(mixed $value)
53
 * @method $this multipart(array $value)
54
 * @method $this onHeaders(callable $value)
55
 * @method $this onStats(callable $value)
56
 * @method $this progress(callable $value)
57
 * @method $this proxy(string|array $value)
58
 * @method $this query(array|string $value)
59
 * @method $this sink(string|resource|\Psr\Http\Message\StreamInterface $value)
60
 * @method $this sslKey(array|string $value)
61
 * @method $this stream(bool $value)
62
 * @method $this verify(bool|string $value)
63
 * @method $this timeout(float $value)
64
 * @method $this readTimeout(float $value)
65
 * @method $this version(float|string $value)
66
 * @method $this forceIpResolve(string $value)
67
 *
68
 * @see http://docs.guzzlephp.org/en/stable/request-options.html Request Options
69
 */
70
class HttpClient
71
{
72
    /**
73
     * The default request options.
74
     *
75
     * @var array
76
     */
77
    protected static $defaultOptions = [
78
        'connect_timeout' => 5,
79
        'timeout' => 20,
80
        'http_errors' => false,
81
    ];
82
83
    /**
84
     * The Guzzle client.
85
     *
86
     * @var \GuzzleHttp\Client
87
     */
88
    protected $client;
89
90
    /**
91
     * The request options.
92
     *
93
     * @var array
94
     */
95
    protected $options = [];
96
97
    /**
98
     * The Guzzle response.
99
     *
100
     * @var \GuzzleHttp\Psr7\Response
101
     */
102
    protected $response;
103
104
    /**
105
     * Indicate whether to catch Guzzle exceptions.
106
     *
107
     * @var bool
108
     */
109
    protected $catchExceptions = true;
110
111
    /**
112
     * Get the default request options.
113
     *
114
     * @return array
115
     */
116 114
    public static function defaultOptions()
117
    {
118 114
        return static::$defaultOptions;
119
    }
120
121
    /**
122
     * Set the default request options.
123
     *
124
     * @param  array  $options
125
     * @return void
126
     */
127 118
    public static function setDefaultOptions(array $options)
128
    {
129 118
        static::$defaultOptions = $options;
130 118
    }
131
132
    /**
133
     * Create a new HTTP client instance.
134
     *
135
     * @param  array|string|\Psr\Http\Message\UriInterface  $options  base URI or any request options
136
     * @return static
137
     */
138 8
    public static function create($options = [])
139
    {
140 8
        return new static($options);
141
    }
142
143
    /**
144
     * Create a new HTTP client instance.
145
     *
146
     * @param  array|string|\Psr\Http\Message\UriInterface  $options  base URI or any request options
147
     *
148
     * @throws \InvalidArgumentException
149
     */
150 118
    public function __construct($options = [])
151
    {
152 118
        if (is_string($options) || $options instanceof UriInterface) {
153 4
            $options = ['base_uri' => $options];
154 116
        } elseif (! is_array($options)) {
155 4
            throw new InvalidArgumentException('Options must be a string, UriInterface, or an array');
156
        }
157
158 114
        $this->client = new Client(
159 114
            array_replace_recursive(static::defaultOptions(), $options)
160 58
        );
161
162 114
        $this->options = $this->client->getConfig();
163 114
    }
164
165
    /**
166
     * Get the Guzzle client instance.
167
     *
168
     * @return \GuzzleHttp\Client
169
     */
170 20
    public function getClient()
171
    {
172 20
        return $this->client;
173
    }
174
175
    /**
176
     * Get whether to catch Guzzle exceptions or not.
177
     *
178
     * @return bool
179
     */
180 4
    public function areExceptionsCaught()
181
    {
182 4
        return $this->catchExceptions;
183
    }
184
185
    /**
186
     * Set whether to catch Guzzle exceptions or not.
187
     *
188
     * @param  bool  $catch
189
     * @return $this
190
     */
191 8
    public function catchExceptions($catch)
192
    {
193 8
        $this->catchExceptions = (bool) $catch;
194
195 8
        return $this;
196
    }
197
198
    /**
199
     * Get the request options using "dot" notation.
200
     *
201
     * @param  string|null  $key
202
     * @return mixed
203
     */
204 48
    public function getOption($key = null)
205
    {
206 48
        return Arr::get($this->options, $key);
207
    }
208
209
    /**
210
     * Set the request options using "dot" notation.
211
     *
212
     * @param  string|array  $key
213
     * @param  mixed  $value
214
     * @return $this
215
     */
216 36
    public function option($key, $value = null)
217
    {
218 36
        $keys = is_array($key) ? $key : [$key => $value];
219
220 36
        foreach ($keys as $key => $value) {
221 36
            Arr::set($this->options, $key, $value);
222 18
        }
223
224 36
        return $this;
225
    }
226
227
    /**
228
     * Merge the given options to the request options.
229
     *
230
     * @param  array  $options
231
     * @return $this
232
     */
233 4
    public function mergeOptions(array $options)
234
    {
235 4
        $this->options = array_replace_recursive($this->options, $options);
236
237 4
        return $this;
238
    }
239
240
    /**
241
     * Remove one or many request options using "dot" notation.
242
     *
243
     * @param  array|string  $keys
244
     * @return $this
245
     */
246 8
    public function removeOption($keys)
247
    {
248 8
        Arr::forget($this->options, is_array($keys) ? $keys : func_get_args());
249
250 8
        return $this;
251
    }
252
253
    /**
254
     * Set a request header.
255
     *
256
     * @param  string  $name
257
     * @param  mixed  $value
258
     * @return $this
259
     */
260 24
    public function header($name, $value)
261
    {
262 24
        return $this->option('headers.'.$name, $value);
263
    }
264
265
    /**
266
     * Set the request accept type.
267
     *
268
     * @param  string  $type
269
     * @return $this
270
     */
271 12
    public function accept($type)
272
    {
273 12
        return $this->header('Accept', $type);
274
    }
275
276
    /**
277
     * Set the request accept type to "application/json".
278
     *
279
     * @return $this
280
     */
281 4
    public function acceptJson()
282
    {
283 4
        return $this->accept('application/json');
284
    }
285
286
    /**
287
     * Set user agent for the request.
288
     *
289
     * @param  string  $value
290
     * @return $this
291
     */
292 4
    public function userAgent($value)
293
    {
294 4
        return $this->header('User-Agent', $value);
295
    }
296
297
    /**
298
     * Set the request content type.
299
     *
300
     * @param  string  $type
301
     * @return $this
302
     */
303 4
    public function contentType($type)
304
    {
305 4
        return $this->header('Content-Type', $type);
306
    }
307
308
    /**
309
     * Specify where the body of the response will be saved.
310
     * Set the "sink" option.
311
     *
312
     * @param  string|resource|\Psr\Http\Message\StreamInterface  $dest
313
     * @return $this
314
     */
315 4
    public function saveTo($dest)
316
    {
317 4
        return $this->removeOption('save_to')->option('sink', $dest);
318
    }
319
320
    /**
321
     * Get the Guzzle response instance.
322
     *
323
     * @return \GuzzleHttp\Psr7\Response|null
324
     */
325 12
    public function getResponse()
326
    {
327 12
        return $this->response;
328
    }
329
330
    /**
331
     * Get data from the response.
332
     *
333
     * @param  string|\Closure  $callback
334
     * @param  mixed  $parameters
335
     * @param  mixed  $default
336
     * @return mixed
337
     */
338 24
    public function getResponseData($callback, $parameters = [], $default = null)
339
    {
340 24
        if ($this->response) {
341 12
            return $callback instanceof Closure
342 14
                ? $callback($this->response, ...(array) $parameters)
343 24
                : $this->response->$callback(...(array) $parameters);
344
        }
345
346 4
        return $default;
347
    }
348
349
    /**
350
     * Get the response content.
351
     *
352
     * @return string
353
     */
354 16
    public function getContent()
355
    {
356 16
        return (string) $this->getBody();
357
    }
358
359
    /**
360
     * Get the JSON-decoded response content.
361
     *
362
     * @param  bool  $assoc
363
     * @return mixed
364
     */
365 8
    public function getJson($assoc = true)
366
    {
367 8
        return json_decode($this->getContent(), $assoc);
368
    }
369
370
    /**
371
     * Make request to a URI.
372
     *
373
     * @param  string|\Psr\Http\Message\UriInterface  $uri
374
     * @param  string  $method
375
     * @param  array  $options
376
     * @return $this
377
     */
378 42
    public function request($uri = '', $method = 'GET', array $options = [])
379
    {
380 42
        $this->response = null;
381
382 42
        $method = strtoupper($method);
383 42
        $options = array_replace_recursive($this->options, $options);
384
385
        try {
386 42
            $this->response = $this->client->request($method, $uri, $options);
387 24
        } catch (Exception $e) {
388 4
            if (! $this->catchExceptions) {
389 4
                throw $e;
390
            }
391
        }
392
393 42
        return $this;
394
    }
395
396
    /**
397
     * Make request to a URI, expecting JSON content.
398
     *
399
     * @param  string|\Psr\Http\Message\UriInterface  $uri
400
     * @param  string  $method
401
     * @param  array  $options
402
     * @return $this
403
     */
404 8
    public function requestJson($uri = '', $method = 'GET', array $options = [])
405
    {
406 8
        $options = $this->addAcceptableJsonType(
407 8
            array_replace_recursive($this->options, $options)
408 4
        );
409
410 8
        return $this->request($uri, $method, $options);
411
    }
412
413
    /**
414
     * Add JSON type to the "Accept" header for the request options.
415
     *
416
     * @param  array  $options
417
     * @return array
418
     */
419 8
    protected function addAcceptableJsonType(array $options)
420
    {
421 8
        $accept = Arr::get($options, 'headers.Accept', '');
422
423 8
        if (! Str::contains($accept, ['/json', '+json'])) {
424 8
            $accept = rtrim('application/json,'.$accept, ',');
425 8
            Arr::set($options, 'headers.Accept', $accept);
426 4
        }
427
428 8
        return $options;
429
    }
430
431
    /**
432
     * Make asynchronous request to a URI.
433
     *
434
     * @param  string|\Psr\Http\Message\UriInterface  $uri
435
     * @param  string  $method
436
     * @param  array  $options
437
     * @return \GuzzleHttp\Promise\PromiseInterface
438
     */
439 8
    public function requestAsync($uri = '', $method = 'GET', array $options = [])
440
    {
441 8
        return $this->client->requestAsync(
442 8
            strtoupper($method),
443 8
            $uri,
444 8
            array_replace_recursive($this->options, $options)
445 4
        );
446
    }
447
448
    /**
449
     * Request the URI and return the response content.
450
     *
451
     * @param  string|\Psr\Http\Message\UriInterface  $uri
452
     * @param  string  $method
453
     * @param  array  $options
454
     * @return string
455
     */
456 4
    public function fetchContent($uri = '', $method = 'GET', array $options = [])
457
    {
458 4
        return $this->request($uri, $method, $options)->getContent();
459
    }
460
461
    /**
462
     * Request the URI and return the JSON-decoded response content.
463
     *
464
     * @param  string|\Psr\Http\Message\UriInterface  $uri
465
     * @param  string  $method
466
     * @param  array  $options
467
     * @return mixed
468
     */
469 4
    public function fetchJson($uri = '', $method = 'GET', array $options = [])
470
    {
471 4
        return $this->requestJson($uri, $method, $options)->getJson();
472
    }
473
474
    /**
475
     * Get all allowed magic request methods.
476
     *
477
     * @return array
478
     */
479 34
    protected function getMagicRequestMethods()
480
    {
481
        return [
482 34
            'get', 'head', 'post', 'put', 'patch', 'delete', 'options',
483 18
        ];
484
    }
485
486
    /**
487
     * Determine if the given method is a magic request method.
488
     *
489
     * @param  string  $method
490
     * @param  string  &$requestMethod
491
     * @param  string  &$httpMethod
492
     * @return bool
493
     */
494 34
    protected function isMagicRequest($method, &$requestMethod, &$httpMethod)
495
    {
496 34
        if (strlen($method) > 5 && $pos = strrpos($method, 'Async', -5)) {
497 4
            $httpMethod = substr($method, 0, $pos);
498 4
            $requestMethod = 'requestAsync';
499 2
        } else {
500 34
            $httpMethod = $method;
501 34
            $requestMethod = 'request';
502
        }
503
504 34
        if (in_array($httpMethod, $this->getMagicRequestMethods())) {
505 6
            return true;
506
        }
507
508 28
        $httpMethod = $requestMethod = null;
509
510 28
        return false;
511
    }
512
513
    /**
514
     * Get parameters for $this->request() from the magic request call.
515
     *
516
     * @param  string  $method
517
     * @param  array  $parameters
518
     * @return array
519
     */
520 6
    protected function getRequestParameters($method, array $parameters)
521
    {
522 6
        if (empty($parameters)) {
523 2
            $parameters = ['', $method];
524 2
        } else {
525 6
            array_splice($parameters, 1, 0, $method);
526
        }
527
528 6
        return $parameters;
529
    }
530
531
    /**
532
     * Get all allowed magic response methods.
533
     *
534
     * @return array
535
     */
536 28
    protected function getMagicResponseMethods()
537
    {
538
        return [
539 28
            'getStatusCode', 'getReasonPhrase', 'getProtocolVersion',
540 14
            'getHeaders', 'hasHeader', 'getHeader', 'getHeaderLine', 'getBody',
541 14
        ];
542
    }
543
544
    /**
545
     * Get all allowed magic option methods.
546
     *
547
     * @return array
548
     */
549 8
    protected function getMagicOptionMethods()
550
    {
551 8
        static $optionMethods = null;
552
553 8
        if (is_null($optionMethods)) {
554 4
            $reflector = new ReflectionClass(RequestOptions::class);
555 4
            $optionMethods = array_map(
556 4
                [Str::class, 'camel'],
557 4
                array_values($reflector->getConstants())
558 2
            );
559 2
        }
560
561 8
        return $optionMethods;
562
    }
563
564
    /**
565
     * Get the option name for the given magic option method.
566
     *
567
     * @param  string  $method
568
     * @return string|null
569
     */
570 8
    protected function getOptionNameForMethod($method)
571
    {
572 8
        if (in_array($method, $this->getMagicOptionMethods())) {
573 4
            return Str::snake($method);
574
        }
575 4
    }
576
577
    /**
578
     * Handle magic method to send request, get response data, or set
579
     * request options.
580
     *
581
     * @param  string  $method
582
     * @param  array  $parameters
583
     * @return mixed
584
     *
585
     * @throws \InvalidArgumentException
586
     * @throws \BadMethodCallException
587
     */
588 34
    public function __call($method, $parameters)
589
    {
590 34
        if ($this->isMagicRequest($method, $requestMethod, $httpMethod)) {
591 6
            return $this->$requestMethod(...$this->getRequestParameters($httpMethod, $parameters));
592
        }
593
594 28
        if (in_array($method, $this->getMagicResponseMethods())) {
595 20
            return $this->getResponseData($method, $parameters);
596
        }
597
598 8
        if ($option = $this->getOptionNameForMethod($method)) {
599 4
            if (empty($parameters)) {
600 4
                throw new InvalidArgumentException("Method [$method] needs one argument.");
601
            }
602
603 4
            return $this->option($option, $parameters[0]);
604
        }
605
606 4
        throw new BadMethodCallException("Method [$method] does not exist.");
607
    }
608
}
609