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

MessageFactory::applyOptions()   D

Complexity

Conditions 43
Paths 45

Size

Total Lines 186
Code Lines 113

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 186
rs 4.1818
cc 43
eloc 113
nc 45
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace GuzzleHttp\Message;
3
4
use GuzzleHttp\Cookie\CookieJar;
5
use GuzzleHttp\Cookie\CookieJarInterface;
6
use GuzzleHttp\Event\ListenerAttacherTrait;
7
use GuzzleHttp\Post\PostBody;
8
use GuzzleHttp\Post\PostFile;
9
use GuzzleHttp\Post\PostFileInterface;
10
use GuzzleHttp\Query;
11
use GuzzleHttp\Stream\Stream;
12
use GuzzleHttp\Subscriber\Cookie;
13
use GuzzleHttp\Subscriber\HttpError;
14
use GuzzleHttp\Subscriber\Redirect;
15
use GuzzleHttp\Url;
16
use \InvalidArgumentException as Iae;
17
18
/**
19
 * Default HTTP request factory used to create Request and Response objects.
20
 */
21
class MessageFactory implements MessageFactoryInterface
22
{
23
    use ListenerAttacherTrait;
24
25
    /** @var HttpError */
26
    private $errorPlugin;
27
28
    /** @var Redirect */
29
    private $redirectPlugin;
30
31
    /** @var array */
32
    private $customOptions;
33
34
    /** @var array Request options passed through to request Config object */
35
    private static $configMap = [
36
        'connect_timeout' => 1, 'timeout' => 1, 'verify' => 1, 'ssl_key' => 1,
37
        'cert' => 1, 'proxy' => 1, 'debug' => 1, 'save_to' => 1, 'stream' => 1,
38
        'expect' => 1, 'future' => 1
39
    ];
40
41
    /** @var array Default allow_redirects request option settings  */
42
    private static $defaultRedirect = [
43
        'max'       => 5,
44
        'strict'    => false,
45
        'referer'   => false,
46
        'protocols' => ['http', 'https']
47
    ];
48
49
    /**
50
     * @param array $customOptions Associative array of custom request option
51
     *                             names mapping to functions used to apply
52
     *                             the option. The function accepts the request
53
     *                             and the option value to apply.
54
     */
55
    public function __construct(array $customOptions = [])
56
    {
57
        $this->errorPlugin = new HttpError();
58
        $this->redirectPlugin = new Redirect();
59
        $this->customOptions = $customOptions;
60
    }
61
62
    public function createResponse(
63
        $statusCode,
64
        array $headers = [],
65
        $body = null,
66
        array $options = []
67
    ) {
68
        if (null !== $body) {
69
            $body = Stream::factory($body);
70
        }
71
72
        return new Response($statusCode, $headers, $body, $options);
73
    }
74
75
    public function createRequest($method, $url, array $options = [])
76
    {
77
        // Handle the request protocol version option that needs to be
78
        // specified in the request constructor.
79
        if (isset($options['version'])) {
80
            $options['config']['protocol_version'] = $options['version'];
81
            unset($options['version']);
82
        }
83
84
        $request = new Request($method, $url, [], null,
85
            isset($options['config']) ? $options['config'] : []);
86
87
        unset($options['config']);
88
89
        // Use a POST body by default
90
        if ($method == 'POST'
91
            && !isset($options['body'])
92
            && !isset($options['json'])
93
        ) {
94
            $options['body'] = [];
95
        }
96
97
        if ($options) {
98
            $this->applyOptions($request, $options);
99
        }
100
101
        return $request;
102
    }
103
104
    /**
105
     * Create a request or response object from an HTTP message string
106
     *
107
     * @param string $message Message to parse
108
     *
109
     * @return RequestInterface|ResponseInterface
110
     * @throws \InvalidArgumentException if unable to parse a message
111
     */
112
    public function fromMessage($message)
113
    {
114
        static $parser;
115
        if (!$parser) {
116
            $parser = new MessageParser();
117
        }
118
119
        // Parse a response
120
        if (strtoupper(substr($message, 0, 4)) == 'HTTP') {
121
            $data = $parser->parseResponse($message);
122
            return $this->createResponse(
123
                $data['code'],
124
                $data['headers'],
125
                $data['body'] === '' ? null : $data['body'],
126
                $data
127
            );
128
        }
129
130
        // Parse a request
131
        if (!($data = ($parser->parseRequest($message)))) {
132
            throw new \InvalidArgumentException('Unable to parse request');
133
        }
134
135
        return $this->createRequest(
136
            $data['method'],
137
            Url::buildUrl($data['request_url']),
138
            [
139
                'headers' => $data['headers'],
140
                'body' => $data['body'] === '' ? null : $data['body'],
141
                'config' => [
142
                    'protocol_version' => $data['protocol_version']
143
                ]
144
            ]
145
        );
146
    }
147
148
    /**
149
     * Apply POST fields and files to a request to attempt to give an accurate
150
     * representation.
151
     *
152
     * @param RequestInterface $request Request to update
153
     * @param array            $body    Body to apply
154
     */
155
    protected function addPostData(RequestInterface $request, array $body)
156
    {
157
        static $fields = ['string' => true, 'array' => true, 'NULL' => true,
158
            'boolean' => true, 'double' => true, 'integer' => true];
159
160
        $post = new PostBody();
161
        foreach ($body as $key => $value) {
162
            if (isset($fields[gettype($value)])) {
163
                $post->setField($key, $value);
164
            } elseif ($value instanceof PostFileInterface) {
165
                $post->addFile($value);
166
            } else {
167
                $post->addFile(new PostFile($key, $value));
168
            }
169
        }
170
171
        if ($request->getHeader('Content-Type') == 'multipart/form-data') {
172
            $post->forceMultipartUpload(true);
173
        }
174
175
        $request->setBody($post);
176
    }
177
178
    protected function applyOptions(
179
        RequestInterface $request,
180
        array $options = []
181
    ) {
182
        $config = $request->getConfig();
183
        $emitter = $request->getEmitter();
184
185
        foreach ($options as $key => $value) {
186
187
            if (isset(self::$configMap[$key])) {
188
                $config[$key] = $value;
189
                continue;
190
            }
191
192
            switch ($key) {
193
194
            case 'allow_redirects':
195
196
                if ($value === false) {
197
                    continue;
198
                }
199
200
                if ($value === true) {
201
                    $value = self::$defaultRedirect;
202
                } elseif (!is_array($value)) {
203
                    throw new Iae('allow_redirects must be true, false, or array');
204
                } else {
205
                    // Merge the default settings with the provided settings
206
                    $value += self::$defaultRedirect;
207
                }
208
209
                $config['redirect'] = $value;
210
                $emitter->attach($this->redirectPlugin);
211
                break;
212
213
            case 'decode_content':
214
215
                if ($value === false) {
216
                    continue;
217
                }
218
219
                $config['decode_content'] = true;
220
                if ($value !== true) {
221
                    $request->setHeader('Accept-Encoding', $value);
222
                }
223
                break;
224
225
            case 'headers':
226
227
                if (!is_array($value)) {
228
                    throw new Iae('header value must be an array');
229
                }
230
                foreach ($value as $k => $v) {
231
                    $request->setHeader($k, $v);
232
                }
233
                break;
234
235
            case 'exceptions':
236
237
                if ($value === true) {
238
                    $emitter->attach($this->errorPlugin);
239
                }
240
                break;
241
242
            case 'body':
243
244
                if (is_array($value)) {
245
                    $this->addPostData($request, $value);
246
                } elseif ($value !== null) {
247
                    $request->setBody(Stream::factory($value));
248
                }
249
                break;
250
251
            case 'auth':
252
253
                if (!$value) {
254
                    continue;
255
                }
256
257
                if (is_array($value)) {
258
                    $type = isset($value[2]) ? strtolower($value[2]) : 'basic';
259
                } else {
260
                    $type = strtolower($value);
261
                }
262
263
                $config['auth'] = $value;
264
265
                if ($type == 'basic') {
266
                    $request->setHeader(
267
                        'Authorization',
268
                        'Basic ' . base64_encode("$value[0]:$value[1]")
269
                    );
270
                } elseif ($type == 'digest') {
271
                    // @todo: Do not rely on curl
272
                    $config->setPath('curl/' . CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
273
                    $config->setPath('curl/' . CURLOPT_USERPWD, "$value[0]:$value[1]");
274
                }
275
                break;
276
277
            case 'query':
278
279
                if ($value instanceof Query) {
280
                    $original = $request->getQuery();
281
                    // Do not overwrite existing query string variables by
282
                    // overwriting the object with the query string data passed
283
                    // in the URL
284
                    $value->overwriteWith($original->toArray());
285
                    $request->setQuery($value);
286
                } elseif (is_array($value)) {
287
                    // Do not overwrite existing query string variables
288
                    $query = $request->getQuery();
289
                    foreach ($value as $k => $v) {
290
                        if (!isset($query[$k])) {
291
                            $query[$k] = $v;
292
                        }
293
                    }
294
                } else {
295
                    throw new Iae('query must be an array or Query object');
296
                }
297
                break;
298
299
            case 'cookies':
300
301
                if ($value === true) {
302
                    static $cookie = null;
303
                    if (!$cookie) {
304
                        $cookie = new Cookie();
305
                    }
306
                    $emitter->attach($cookie);
307
                } elseif (is_array($value)) {
308
                    $emitter->attach(
309
                        new Cookie(CookieJar::fromArray($value, $request->getHost()))
310
                    );
311
                } elseif ($value instanceof CookieJarInterface) {
312
                    $emitter->attach(new Cookie($value));
313
                } elseif ($value !== false) {
314
                    throw new Iae('cookies must be an array, true, or CookieJarInterface');
315
                }
316
                break;
317
318
            case 'events':
319
320
                if (!is_array($value)) {
321
                    throw new Iae('events must be an array');
322
                }
323
324
                $this->attachListeners($request,
325
                    $this->prepareListeners(
326
                        $value,
327
                        ['before', 'complete', 'error', 'progress', 'end']
328
                    )
329
                );
330
                break;
331
332
            case 'subscribers':
333
334
                if (!is_array($value)) {
335
                    throw new Iae('subscribers must be an array');
336
                }
337
338
                foreach ($value as $subscribers) {
339
                    $emitter->attach($subscribers);
340
                }
341
                break;
342
343
            case 'json':
344
345
                $request->setBody(Stream::factory(json_encode($value)));
346
                if (!$request->hasHeader('Content-Type')) {
347
                    $request->setHeader('Content-Type', 'application/json');
348
                }
349
                break;
350
351
            default:
352
353
                // Check for custom handler functions.
354
                if (isset($this->customOptions[$key])) {
355
                    $fn = $this->customOptions[$key];
356
                    $fn($request, $value);
357
                    continue;
358
                }
359
360
                throw new Iae("No method can handle the {$key} config key");
361
            }
362
        }
363
    }
364
}
365