Completed
Push — master ( 03b973...1c31e3 )
by Elf
01:16
created

HttpClient::getJsonContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
541
        }
542
543 7
        if (in_array($method, $this->getMagicResponseMethods())) {
544 5
            return $this->getResponseData($method, $parameters);
545
        }
546
547 2
        if ($option = $this->getOptionNameForMethod($method)) {
548 1
            if (empty($parameters)) {
549 1
                throw new InvalidArgumentException("Method [$method] needs one argument.");
550
            }
551
552 1
            return $this->option($option, $parameters[0]);
553
        }
554
555 1
        throw new BadMethodCallException("Method [$method] does not exist.");
556
    }
557
}
558