Passed
Push — master ( d47200...add212 )
by Elf
02:40
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 Exception;
6
use ReflectionClass;
7
use GuzzleHttp\Client;
8
use BadMethodCallException;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Str;
11
use InvalidArgumentException;
12
use GuzzleHttp\RequestOptions;
13
14
/**
15
 * @method \Psr\Http\Message\ResponseInterface|null get(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
16
 * @method \Psr\Http\Message\ResponseInterface|null head(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
17
 * @method \Psr\Http\Message\ResponseInterface|null post(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
18
 * @method \Psr\Http\Message\ResponseInterface|null put(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
19
 * @method \Psr\Http\Message\ResponseInterface|null patch(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
20
 * @method \Psr\Http\Message\ResponseInterface|null delete(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
21
 * @method \Psr\Http\Message\ResponseInterface|null options(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
22
 * @method mixed getJson(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
23
 * @method mixed postJson(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
24
 * @method mixed putJson(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
25
 * @method mixed patchJson(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
26
 * @method mixed deleteJson(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
27
 * @method \GuzzleHttp\Promise\PromiseInterface getAsync(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
28
 * @method \GuzzleHttp\Promise\PromiseInterface headAsync(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
29
 * @method \GuzzleHttp\Promise\PromiseInterface postAsync(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
30
 * @method \GuzzleHttp\Promise\PromiseInterface putAsync(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
31
 * @method \GuzzleHttp\Promise\PromiseInterface patchAsync(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
32
 * @method \GuzzleHttp\Promise\PromiseInterface deleteAsync(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
33
 * @method \GuzzleHttp\Promise\PromiseInterface optionsAsync(string|\Psr\Http\Message\UriInterface $uri = '', array $options = [])
34
 * @method $this allowRedirects(bool|array $value)
35
 * @method $this auth(array|string|null $value)
36
 * @method $this body(string|resource|\Psr\Http\Message\StreamInterface $value)
37
 * @method $this cert(string|array $value)
38
 * @method $this cookies(bool|\GuzzleHttp\Cookie\CookieJarInterface $value)
39
 * @method $this connectTimeout(float $value)
40
 * @method $this debug(bool|resource $value)
41
 * @method $this decodeContent(string|bool $value)
42
 * @method $this delay(int|float $value)
43
 * @method $this expect(bool|int $value)
44
 * @method $this forceIpResolve(string $value)
45
 * @method $this formParams(array $value)
46
 * @method $this headers(array $value)
47
 * @method $this httpErrors(bool $value)
48
 * @method $this json(mixed $value)
49
 * @method $this onHeaders(callable $value)
50
 * @method $this onStats(callable $value)
51
 * @method $this progress(callable $value)
52
 * @method $this proxy(string|array $value)
53
 * @method $this query(array|string $value)
54
 * @method $this readTimeout(float $value)
55
 * @method $this sink(string|resource|\Psr\Http\Message\StreamInterface $value)
56
 * @method $this sslKey(string|array $value)
57
 * @method $this stream(bool $value)
58
 * @method $this verify(bool|string $value)
59
 * @method $this timeout(float $value)
60
 * @method $this version(float|string $value)
61
 *
62
 * @see http://docs.guzzlephp.org/en/stable/request-options.html Request Options
63
 */
64
class HttpClient
65
{
66
    /**
67
     * The default request options.
68
     *
69
     * @var array
70
     */
71
    protected static $defaultOptions = [
72
        'catch_exceptions' => true,
73
        'http_errors' => false,
74
        'connect_timeout' => 5,
75
        'timeout' => 20,
76
    ];
77
78
    /**
79
     * The Guzzle client.
80
     *
81
     * @var \GuzzleHttp\Client
82
     */
83
    protected $client;
84
85
    /**
86
     * The request options.
87
     *
88
     * @var array
89
     */
90
    protected $options = [];
91
92
    /**
93
     * All allowed magic request methods (verbs).
94
     *
95
     * @var array
96
     */
97
    protected $magicRequestMethods = [
98
        'get', 'head', 'post', 'put', 'patch', 'delete', 'options',
99
    ];
100
101
    /**
102
     * Get the default request options.
103
     *
104
     * @return array
105
     */
106 104
    public static function defaultOptions()
107
    {
108 104
        return static::$defaultOptions;
109
    }
110
111
    /**
112
     * Set the default request options.
113
     *
114
     * @param  array  $options
115
     * @return void
116
     */
117 104
    public static function setDefaultOptions(array $options)
118
    {
119 104
        static::$defaultOptions = $options;
120 104
    }
121
122
    /**
123
     * Create a new HTTP client instance.
124
     *
125
     * @param  array|string|\Psr\Http\Message\UriInterface  $options  base URI or any request options
126
     * @return static
127
     */
128 10
    public static function create($options = [])
129
    {
130 10
        return new static($options);
131
    }
132
133
    /**
134
     * Create a new HTTP client instance.
135
     *
136
     * @param  array|string|\Psr\Http\Message\UriInterface  $options  base URI or any request options
137
     */
138 104
    public function __construct($options = [])
139
    {
140 104
        if (! is_array($options)) {
141 4
            $options = ['base_uri' => $options];
142 2
        }
143
144 104
        $this->option(static::defaultOptions())->option($options);
145
146 104
        $this->client = new Client($this->options);
147
148 104
        $this->options = $this->client->getConfig();
149 104
    }
150
151
    /**
152
     * Get the Guzzle client instance.
153
     *
154
     * @return \GuzzleHttp\Client
155
     */
156 20
    public function getClient()
157
    {
158 20
        return $this->client;
159
    }
160
161
    /**
162
     * Get the request options using "dot" notation.
163
     *
164
     * @param  string|null  $key
165
     * @param  mixed  $default
166
     * @return mixed
167
     */
168 72
    public function getOption($key = null, $default = null)
169
    {
170 72
        return Arr::get($this->options, $key, $default);
171
    }
172
173
    /**
174
     * Set the request options using "dot" notation.
175
     *
176
     * @param  string|array  $key
177
     * @param  mixed  $value
178
     * @return $this
179
     */
180 104
    public function option($key, $value = null)
181
    {
182 104
        $keys = is_array($key) ? $key : [$key => $value];
183
184 104
        foreach ($keys as $key => $value) {
185 92
            Arr::set($this->options, $key, $value);
186 52
        }
187
188 104
        return $this;
189
    }
190
191
    /**
192
     * Remove one or many request options using "dot" notation.
193
     *
194
     * @param  array|string  $keys
195
     * @return $this
196
     */
197 36
    public function removeOption($keys)
198
    {
199 36
        Arr::forget($this->options, is_array($keys) ? $keys : func_get_args());
200
201 36
        return $this;
202
    }
203
204
    /**
205
     * Set a request header.
206
     *
207
     * @param  string  $name
208
     * @param  mixed  $value
209
     * @return $this
210
     */
211 20
    public function header($name, $value)
212
    {
213 20
        return $this->option('headers.'.$name, $value);
214
    }
215
216
    /**
217
     * Remove one or many request headers.
218
     *
219
     * @param  array|string  $names
220
     * @return $this
221
     */
222 4
    public function removeHeader($names)
223
    {
224 4
        if (is_array($headers = $this->getOption('headers'))) {
225 4
            $names = is_array($names) ? $names : func_get_args();
226
227 4
            $this->option('headers', Arr::except($headers, $names));
228 2
        }
229
230 4
        return $this;
231
    }
232
233
    /**
234
     * Set the request accept type.
235
     *
236
     * @param  string  $type
237
     * @return $this
238
     */
239 8
    public function accept($type)
240
    {
241 8
        return $this->header('Accept', $type);
242
    }
243
244
    /**
245
     * Set the request accept type to "application/json".
246
     *
247
     * @return $this
248
     */
249 4
    public function acceptJson()
250
    {
251 4
        return $this->accept('application/json');
252
    }
253
254
    /**
255
     * Set user agent for the request.
256
     *
257
     * @param  string  $value
258
     * @return $this
259
     */
260 4
    public function userAgent($value)
261
    {
262 4
        return $this->header('User-Agent', $value);
263
    }
264
265
    /**
266
     * Set the request content type.
267
     *
268
     * @param  string  $type
269
     * @return $this
270
     */
271 4
    public function contentType($type)
272
    {
273 4
        return $this->header('Content-Type', $type);
274
    }
275
276
    /**
277
     * Specify where the body of the response will be saved.
278
     * Set the "sink" option.
279
     *
280
     * @param  string|resource|\Psr\Http\Message\StreamInterface  $dest
281
     * @return $this
282
     */
283 4
    public function saveTo($dest)
284
    {
285 4
        return $this->option('sink', $dest);
286
    }
287
288
    /**
289
     * Set the body of the request to a multipart/form-data form.
290
     *
291
     * @param  array  $data
292
     * @return $this
293
     */
294 4
    public function multipart(array $data)
295
    {
296 4
        $multipart = [];
297
298 4
        foreach ($data as $key => $value) {
299 4
            if (! is_array($value)) {
300 4
                $value = ['contents' => $value];
301 2
            }
302
303 4
            if (! is_int($key)) {
304 4
                $value['name'] = $key;
305 2
            }
306
307 4
            $multipart[] = $value;
308 2
        }
309
310 4
        return $this->option('multipart', $multipart);
311
    }
312
313
    /**
314
     * Determine whether to catch Guzzle exceptions.
315
     *
316
     * @return bool
317
     */
318 16
    public function areExceptionsCaught()
319
    {
320 16
        return $this->getOption('catch_exceptions', false);
321
    }
322
323
    /**
324
     * Set whether to catch Guzzle exceptions or not.
325
     *
326
     * @param  bool  $catch
327
     * @return $this
328
     */
329 16
    public function catchExceptions($catch)
330
    {
331 16
        return $this->option('catch_exceptions', (bool) $catch);
332
    }
333
334
    /**
335
     * Send request to a URI.
336
     *
337
     * @param  string|\Psr\Http\Message\UriInterface  $uri
338
     * @param  string  $method
339
     * @param  array  $options
340
     * @return \Psr\Http\Message\ResponseInterface|null
341
     */
342 28
    public function request($uri = '', $method = 'GET', array $options = [])
343
    {
344
        try {
345 28
            return $this->client->request(
346 28
                $method, $uri, $this->getRequestOptions($options)
347 14
            );
348 12
        } catch (Exception $e) {
349 12
            if (! $this->areExceptionsCaught()) {
350 12
                throw $e;
351
            }
352
        }
353 12
    }
354
355
    /**
356
     * Send request to a URI, expecting JSON content.
357
     *
358
     * @param  string|\Psr\Http\Message\UriInterface  $uri
359
     * @param  string  $method
360
     * @param  array  $options
361
     * @return \Psr\Http\Message\ResponseInterface|null
362
     */
363 12
    public function requestJson($uri = '', $method = 'GET', array $options = [])
364
    {
365 12
        Arr::set($options, 'headers.Accept', 'application/json');
366
367 12
        return $this->request($uri, $method, $options);
368
    }
369
370
    /**
371
     * Send asynchronous request to a URI.
372
     *
373
     * @param  string|\Psr\Http\Message\UriInterface  $uri
374
     * @param  string  $method
375
     * @param  array  $options
376
     * @return \GuzzleHttp\Promise\PromiseInterface
377
     */
378 8
    public function requestAsync($uri = '', $method = 'GET', array $options = [])
379
    {
380 8
        return $this->client->requestAsync(
381 8
            $method, $uri, $this->getRequestOptions($options)
382 4
        );
383
    }
384
385
    /**
386
     * Get options for the Guzzle request method.
387
     *
388
     * @param  array  $options
389
     * @return array
390
     */
391 32
    protected function getRequestOptions(array $options = [])
392
    {
393 32
        $options = array_replace_recursive($this->options, $options);
394
395 32
        $this->removeOption([
396 32
            'body', 'form_params', 'multipart', 'json', 'query',
397 16
            'sink', 'save_to', 'stream',
398 16
            'on_headers', 'on_stats', 'progress',
399 16
            'headers.Content-Type',
400 16
        ]);
401
402 32
        return $options;
403
    }
404
405
    /**
406
     * Request the URI and return the response content.
407
     *
408
     * @param  string|\Psr\Http\Message\UriInterface  $uri
409
     * @param  string  $method
410
     * @param  array  $options
411
     * @return string|null
412
     */
413 4
    public function fetchContent($uri = '', $method = 'GET', array $options = [])
414
    {
415 4
        if ($response = $this->request($uri, $method, $options)) {
416 4
            return (string) $response->getBody();
417
        }
418 4
    }
419
420
    /**
421
     * Request the URI and return the JSON-decoded response content.
422
     *
423
     * @param  string|\Psr\Http\Message\UriInterface  $uri
424
     * @param  string  $method
425
     * @param  array  $options
426
     * @return mixed
427
     */
428 8
    public function fetchJson($uri = '', $method = 'GET', array $options = [])
429
    {
430 8
        if ($response = $this->requestJson($uri, $method, $options)) {
431 8
            return json_decode($response->getBody(), true);
432
        }
433 4
    }
434
435
    /**
436
     * Determine if the given method is a magic request method.
437
     *
438
     * @param  string  $method
439
     * @param  string  &$requestMethod
440
     * @param  string  &$httpMethod
441
     * @return bool
442
     */
443 16
    protected function isMagicRequestMethod($method, &$requestMethod, &$httpMethod)
444
    {
445 16
        $requestMethod = $httpMethod = null;
446
447 16
        foreach ($this->magicRequestMethods as $verb) {
448 16
            if ($method == $verb) {
449 8
                $requestMethod = 'request';
450 16
            } elseif ($method == $verb.'Async') {
451 4
                $requestMethod = 'requestAsync';
452 16
            } elseif ($method == $verb.'Json') {
453 4
                $requestMethod = 'fetchJson';
454 2
            }
455
456 16
            if ($requestMethod) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $requestMethod of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
457 12
                return (bool) $httpMethod = $verb;
458
            }
459 8
        }
460
461 8
        return false;
462
    }
463
464
    /**
465
     * Get parameters for the request() method from the magic request call.
466
     *
467
     * @param  string  $method
468
     * @param  array  $parameters
469
     * @return array
470
     */
471 8
    protected function getRequestParameters($method, array $parameters)
472
    {
473 8
        if (empty($parameters)) {
474 4
            $parameters = ['', $method];
475 2
        } else {
476 8
            array_splice($parameters, 1, 0, $method);
477
        }
478
479 8
        return $parameters;
480
    }
481
482
    /**
483
     * Get all allowed magic option methods.
484
     *
485
     * @return array
486
     */
487 8
    protected function getMagicOptionMethods()
488
    {
489 8
        static $optionMethods = null;
490
491 8
        if (is_null($optionMethods)) {
1 ignored issue
show
introduced by
The condition is_null($optionMethods) can never be false.
Loading history...
492 4
            $reflector = new ReflectionClass(RequestOptions::class);
493 4
            $optionMethods = array_map(
494 4
                [Str::class, 'camel'],
495 4
                array_values($reflector->getConstants())
496 2
            );
497 2
        }
498
499 8
        return $optionMethods;
500
    }
501
502
    /**
503
     * Determine if the given method is a magic option method.
504
     *
505
     * @param  string  $method
506
     * @return bool
507
     */
508 8
    protected function isMagicOptionMethod($method, &$option)
509
    {
510 8
        $option = in_array($method, $this->getMagicOptionMethods())
511 8
            ? Str::snake($method) : null;
512
513 8
        return (bool) $option;
514
    }
515
516
    /**
517
     * Handle magic option/request methods.
518
     *
519
     * @param  string  $method
520
     * @param  array  $parameters
521
     * @return mixed
522
     *
523
     * @throws \InvalidArgumentException
524
     * @throws \BadMethodCallException
525
     */
526 16
    public function __call($method, $parameters)
527
    {
528 16
        if ($this->isMagicRequestMethod($method, $requestMethod, $httpMethod)) {
529 8
            return $this->$requestMethod(...$this->getRequestParameters($httpMethod, $parameters));
530
        }
531
532 8
        if ($this->isMagicOptionMethod($method, $option)) {
533 4
            if (empty($parameters)) {
534 4
                throw new InvalidArgumentException("Method [$method] needs one argument.");
535
            }
536
537 4
            return $this->option($option, $parameters[0]);
538
        }
539
540 4
        throw new BadMethodCallException("Method [$method] does not exist.");
541
    }
542
}
543