Completed
Push — master ( 216ea6...1c74a8 )
by Elf
01:16
created

HttpClient   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 401
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 4

Test Coverage

Coverage 84.21%

Importance

Changes 0
Metric Value
wmc 45
lcom 2
cbo 4
dl 0
loc 401
ccs 80
cts 95
cp 0.8421
rs 8.3673
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A defaultOptions() 0 4 1
A setDefaultOptions() 0 4 1
A __construct() 0 12 4
A getClient() 0 4 1
A withExceptions() 0 6 1
A getOptions() 0 4 1
A setOptions() 0 6 1
A options() 0 4 1
A removeOptions() 0 10 3
A option() 0 8 2
A getOption() 0 4 1
A header() 0 4 1
A contentType() 0 4 1
A accept() 0 4 1
A acceptJson() 0 4 1
A saveTo() 0 4 1
A getResponse() 0 4 1
A getStatusCode() 0 6 2
A getHeader() 0 6 2
A getHeaders() 0 4 2
A getBody() 0 6 2
A getContent() 0 6 2
A getJson() 0 6 2
A request() 0 14 3
A requestJson() 0 6 1
A fetchContent() 0 4 1
A fetchJson() 0 4 1
A __call() 0 17 4

How to fix   Complexity   

Complex Class

Complex classes like HttpClient often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HttpClient, and based on these observations, apply Extract Interface, too.

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