Completed
Push — master ( 2b1385...7c6a84 )
by Thomas
07:21
created

Client   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 335
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 54
c 1
b 0
f 0
lcom 1
cbo 16
dl 0
loc 335
rs 5.1027

How to fix   Complexity   

Complex Class

Complex classes like Client 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 Client, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace GuzzleHttp;
3
4
use GuzzleHttp\Event\HasEmitterTrait;
5
use GuzzleHttp\Message\MessageFactory;
6
use GuzzleHttp\Message\MessageFactoryInterface;
7
use GuzzleHttp\Message\RequestInterface;
8
use GuzzleHttp\Message\FutureResponse;
9
use GuzzleHttp\Ring\Core;
10
use GuzzleHttp\Ring\Future\FutureInterface;
11
use GuzzleHttp\Exception\RequestException;
12
use React\Promise\FulfilledPromise;
13
use React\Promise\RejectedPromise;
14
15
/**
16
 * HTTP client
17
 */
18
class Client implements ClientInterface
19
{
20
    use HasEmitterTrait;
21
22
    /** @var MessageFactoryInterface Request factory used by the client */
23
    private $messageFactory;
24
25
    /** @var Url Base URL of the client */
26
    private $baseUrl;
27
28
    /** @var array Default request options */
29
    private $defaults;
30
31
    /** @var callable Request state machine */
32
    private $fsm;
33
34
    /**
35
     * Clients accept an array of constructor parameters.
36
     *
37
     * Here's an example of creating a client using an URI template for the
38
     * client's base_url and an array of default request options to apply
39
     * to each request:
40
     *
41
     *     $client = new Client([
42
     *         'base_url' => [
43
     *              'http://www.foo.com/{version}/',
44
     *              ['version' => '123']
45
     *          ],
46
     *         'defaults' => [
47
     *             'timeout'         => 10,
48
     *             'allow_redirects' => false,
49
     *             'proxy'           => '192.168.16.1:10'
50
     *         ]
51
     *     ]);
52
     *
53
     * @param array $config Client configuration settings
54
     *     - base_url: Base URL of the client that is merged into relative URLs.
55
     *       Can be a string or an array that contains a URI template followed
56
     *       by an associative array of expansion variables to inject into the
57
     *       URI template.
58
     *     - handler: callable RingPHP handler used to transfer requests
59
     *     - message_factory: Factory used to create request and response object
60
     *     - defaults: Default request options to apply to each request
61
     *     - emitter: Event emitter used for request events
62
     *     - fsm: (internal use only) The request finite state machine. A
63
     *       function that accepts a transaction and optional final state. The
64
     *       function is responsible for transitioning a request through its
65
     *       lifecycle events.
66
     */
67
    public function __construct(array $config = [])
68
    {
69
        $this->configureBaseUrl($config);
70
        $this->configureDefaults($config);
71
72
        if (isset($config['emitter'])) {
73
            $this->emitter = $config['emitter'];
74
        }
75
76
        $this->messageFactory = isset($config['message_factory'])
77
            ? $config['message_factory']
78
            : new MessageFactory();
79
80
        if (isset($config['fsm'])) {
81
            $this->fsm = $config['fsm'];
82
        } else {
83
            if (isset($config['handler'])) {
84
                $handler = $config['handler'];
85
            } elseif (isset($config['adapter'])) {
86
                $handler = $config['adapter'];
87
            } else {
88
                $handler = Utils::getDefaultHandler();
89
            }
90
            $this->fsm = new RequestFsm($handler, $this->messageFactory);
91
        }
92
    }
93
94
    public function getDefaultOption($keyOrPath = null)
95
    {
96
        return $keyOrPath === null
97
            ? $this->defaults
98
            : Utils::getPath($this->defaults, $keyOrPath);
99
    }
100
101
    public function setDefaultOption($keyOrPath, $value)
102
    {
103
        Utils::setPath($this->defaults, $keyOrPath, $value);
104
    }
105
106
    public function getBaseUrl()
107
    {
108
        return (string) $this->baseUrl;
109
    }
110
111
    public function createRequest($method, $url = null, array $options = [])
112
    {
113
        $options = $this->mergeDefaults($options);
114
        // Use a clone of the client's emitter
115
        $options['config']['emitter'] = clone $this->getEmitter();
116
        $url = $url || (is_string($url) && strlen($url))
117
            ? $this->buildUrl($url)
118
            : (string) $this->baseUrl;
119
120
        return $this->messageFactory->createRequest($method, $url, $options);
121
    }
122
123
    public function get($url = null, $options = [])
124
    {
125
        return $this->send($this->createRequest('GET', $url, $options));
126
    }
127
128
    public function head($url = null, array $options = [])
129
    {
130
        return $this->send($this->createRequest('HEAD', $url, $options));
131
    }
132
133
    public function delete($url = null, array $options = [])
134
    {
135
        return $this->send($this->createRequest('DELETE', $url, $options));
136
    }
137
138
    public function put($url = null, array $options = [])
139
    {
140
        return $this->send($this->createRequest('PUT', $url, $options));
141
    }
142
143
    public function patch($url = null, array $options = [])
144
    {
145
        return $this->send($this->createRequest('PATCH', $url, $options));
146
    }
147
148
    public function post($url = null, array $options = [])
149
    {
150
        return $this->send($this->createRequest('POST', $url, $options));
151
    }
152
153
    public function options($url = null, array $options = [])
154
    {
155
        return $this->send($this->createRequest('OPTIONS', $url, $options));
156
    }
157
158
    public function send(RequestInterface $request)
159
    {
160
        $isFuture = $request->getConfig()->get('future');
161
        $trans = new Transaction($this, $request, $isFuture);
162
        $fn = $this->fsm;
163
164
        try {
165
            $fn($trans);
166
            if ($isFuture) {
167
                // Turn the normal response into a future if needed.
168
                return $trans->response instanceof FutureInterface
169
                    ? $trans->response
170
                    : new FutureResponse(new FulfilledPromise($trans->response));
171
            }
172
            // Resolve deep futures if this is not a future
173
            // transaction. This accounts for things like retries
174
            // that do not have an immediate side-effect.
175
            while ($trans->response instanceof FutureInterface) {
176
                $trans->response = $trans->response->wait();
177
            }
178
            return $trans->response;
179
        } catch (\Exception $e) {
180
            if ($isFuture) {
181
                // Wrap the exception in a promise
182
                return new FutureResponse(new RejectedPromise($e));
183
            }
184
            throw RequestException::wrapException($trans->request, $e);
185
        }
186
    }
187
188
    /**
189
     * Get an array of default options to apply to the client
190
     *
191
     * @return array
192
     */
193
    protected function getDefaultOptions()
194
    {
195
        $settings = [
196
            'allow_redirects' => true,
197
            'exceptions'      => true,
198
            'decode_content'  => true,
199
            'verify'          => true
200
        ];
201
202
        // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set
203
        if ($proxy = getenv('HTTP_PROXY')) {
204
            $settings['proxy']['http'] = $proxy;
205
        }
206
207
        if ($proxy = getenv('HTTPS_PROXY')) {
208
            $settings['proxy']['https'] = $proxy;
209
        }
210
211
        return $settings;
212
    }
213
214
    /**
215
     * Expand a URI template and inherit from the base URL if it's relative
216
     *
217
     * @param string|array $url URL or an array of the URI template to expand
218
     *                          followed by a hash of template varnames.
219
     * @return string
220
     * @throws \InvalidArgumentException
221
     */
222
    private function buildUrl($url)
223
    {
224
        // URI template (absolute or relative)
225
        if (!is_array($url)) {
226
            return strpos($url, '://')
227
                ? (string) $url
228
                : (string) $this->baseUrl->combine($url);
229
        }
230
231
        if (!isset($url[1])) {
232
            throw new \InvalidArgumentException('You must provide a hash of '
233
                . 'varname options in the second element of a URL array.');
234
        }
235
236
        // Absolute URL
237
        if (strpos($url[0], '://')) {
238
            return Utils::uriTemplate($url[0], $url[1]);
239
        }
240
241
        // Combine the relative URL with the base URL
242
        return (string) $this->baseUrl->combine(
243
            Utils::uriTemplate($url[0], $url[1])
244
        );
245
    }
246
247
    private function configureBaseUrl(&$config)
248
    {
249
        if (!isset($config['base_url'])) {
250
            $this->baseUrl = new Url('', '');
251
        } elseif (!is_array($config['base_url'])) {
252
            $this->baseUrl = Url::fromString($config['base_url']);
253
        } elseif (count($config['base_url']) < 2) {
254
            throw new \InvalidArgumentException('You must provide a hash of '
255
                . 'varname options in the second element of a base_url array.');
256
        } else {
257
            $this->baseUrl = Url::fromString(
258
                Utils::uriTemplate(
259
                    $config['base_url'][0],
260
                    $config['base_url'][1]
261
                )
262
            );
263
            $config['base_url'] = (string) $this->baseUrl;
264
        }
265
    }
266
267
    private function configureDefaults($config)
268
    {
269
        if (!isset($config['defaults'])) {
270
            $this->defaults = $this->getDefaultOptions();
271
        } else {
272
            $this->defaults = array_replace(
273
                $this->getDefaultOptions(),
274
                $config['defaults']
275
            );
276
        }
277
278
        // Add the default user-agent header
279
        if (!isset($this->defaults['headers'])) {
280
            $this->defaults['headers'] = [
281
                'User-Agent' => Utils::getDefaultUserAgent()
282
            ];
283
        } elseif (!Core::hasHeader($this->defaults, 'User-Agent')) {
284
            // Add the User-Agent header if one was not already set
285
            $this->defaults['headers']['User-Agent'] = Utils::getDefaultUserAgent();
286
        }
287
    }
288
289
    /**
290
     * Merges default options into the array passed by reference.
291
     *
292
     * @param array $options Options to modify by reference
293
     *
294
     * @return array
295
     */
296
    private function mergeDefaults($options)
297
    {
298
        $defaults = $this->defaults;
299
300
        // Case-insensitively merge in default headers if both defaults and
301
        // options have headers specified.
302
        if (!empty($defaults['headers']) && !empty($options['headers'])) {
303
            // Create a set of lowercased keys that are present.
304
            $lkeys = [];
305
            foreach (array_keys($options['headers']) as $k) {
306
                $lkeys[strtolower($k)] = true;
307
            }
308
            // Merge in lowercase default keys when not present in above set.
309
            foreach ($defaults['headers'] as $key => $value) {
310
                if (!isset($lkeys[strtolower($key)])) {
311
                    $options['headers'][$key] = $value;
312
                }
313
            }
314
            // No longer need to merge in headers.
315
            unset($defaults['headers']);
316
        }
317
318
        $result = array_replace_recursive($defaults, $options);
319
        foreach ($options as $k => $v) {
320
            if ($v === null) {
321
                unset($result[$k]);
322
            }
323
        }
324
325
        return $result;
326
    }
327
328
    /**
329
     * @deprecated Use {@see GuzzleHttp\Pool} instead.
330
     * @see GuzzleHttp\Pool
331
     */
332
    public function sendAll($requests, array $options = [])
333
    {
334
        Pool::send($this, $requests, $options);
335
    }
336
337
    /**
338
     * @deprecated Use GuzzleHttp\Utils::getDefaultHandler
339
     */
340
    public static function getDefaultHandler()
341
    {
342
        return Utils::getDefaultHandler();
343
    }
344
345
    /**
346
     * @deprecated Use GuzzleHttp\Utils::getDefaultUserAgent
347
     */
348
    public static function getDefaultUserAgent()
349
    {
350
        return Utils::getDefaultUserAgent();
351
    }
352
}
353