Completed
Push — master ( 77ddfb...baf6b1 )
by WEBEWEB
01:16
created

AbstractCurlRequest   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 413
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 53
lcom 1
cbo 5
dl 0
loc 413
rs 6.96
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A addHeader() 0 5 1
A addPostData() 0 5 1
A addQueryData() 0 5 1
B call() 0 55 8
A clearHeaders() 0 3 1
A clearPostData() 0 3 1
A clearQueryData() 0 3 1
A getConfiguration() 0 3 1
A getHeaders() 0 3 1
A getMethod() 0 3 1
A getPostData() 0 3 1
A getQueryData() 0 3 1
A getResourcePath() 0 3 1
A mergeHeaders() 0 9 2
A mergeUrl() 0 12 3
B parseHeader() 0 28 7
A prepareResponse() 0 12 1
A removeHeader() 0 6 2
A removePostData() 0 6 2
A removeQueryData() 0 6 2
A setConfiguration() 0 4 1
A setHeaders() 0 4 1
B setMethod() 0 18 8
A setPostData() 0 4 1
A setQueryData() 0 4 1
A setResourcePath() 0 4 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/*
4
 * This file is part of the core-library package.
5
 *
6
 * (c) 2018 WEBEWEB
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace WBW\Library\Core\Network\CURL\Request;
13
14
use DateTime;
15
use InvalidArgumentException;
16
use WBW\Library\Core\Argument\ArgumentHelper;
17
use WBW\Library\Core\Network\CURL\API\CurlRequestInterface;
18
use WBW\Library\Core\Network\CURL\API\CurlResponseInterface;
19
use WBW\Library\Core\Network\CURL\Configuration\CurlConfiguration;
20
use WBW\Library\Core\Network\CURL\Exception\CurlRequestCallException;
21
use WBW\Library\Core\Network\CURL\Factory\CurlFactory;
22
use WBW\Library\Core\Network\CURL\Helper\CurlHelper;
23
use WBW\Library\Core\Network\HTTP\HttpInterface;
24
25
/**
26
 * Abstract cURL request.
27
 *
28
 * @author webeweb <https://github.com/webeweb/>
29
 * @package WBW\Library\Core\Network\CURL\Request
30
 * @abstract
31
 */
32
abstract class AbstractCurlRequest implements CurlRequestInterface, HttpInterface {
33
34
    /**
35
     * Content-Type "application/x-www-form-urlencoded".
36
     * @var string
37
     */
38
    const CONTENT_TYPE_X_WWW_FORM_URLENCODED = "Content-Type: application/x-www-form-urlencoded";
39
40
    /**
41
     * Configuration.
42
     *
43
     * @var CurlConfiguration
44
     */
45
    private $configuration;
46
47
    /**
48
     * Headers.
49
     *
50
     * @var array
51
     */
52
    private $headers = [];
53
54
    /**
55
     * Method.
56
     *
57
     * @var string
58
     */
59
    private $method = self::HTTP_METHOD_GET;
60
61
    /**
62
     * POST data.
63
     *
64
     * @var array
65
     */
66
    private $postData = [];
67
68
    /**
69
     * Query data.
70
     *
71
     * @var array
72
     */
73
    private $queryData = [];
74
75
    /**
76
     * Resource path.
77
     *
78
     * @var string
79
     */
80
    private $resourcePath;
81
82
    /**
83
     * Constructor.
84
     *
85
     * @param string $method The Method.
86
     * @param CurlConfiguration $configuration The configuration.
87
     * @param string $resourcePath The resource path.
88
     * @throws InvalidArgumentException Throws an invalid argument exception if the method is invalid.
89
     */
90
    protected function __construct($method, CurlConfiguration $configuration, $resourcePath) {
91
        $this->setConfiguration($configuration);
92
        $this->setMethod($method);
93
        $this->setResourcePath($resourcePath);
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function addHeader($name, $value) {
100
        ArgumentHelper::isTypeOf($name, ArgumentHelper::ARGUMENT_STRING);
101
        $this->headers[$name] = $value;
102
        return $this;
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function addPostData($name, $value) {
109
        ArgumentHelper::isTypeOf($name, ArgumentHelper::ARGUMENT_STRING);
110
        $this->postData[$name] = $value;
111
        return $this;
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function addQueryData($name, $value) {
118
        ArgumentHelper::isTypeOf($name, ArgumentHelper::ARGUMENT_STRING);
119
        $this->queryData[$name] = $value;
120
        return $this;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function call() {
127
128
        $requestHeader = $this->mergeHeaders();
129
        $requestBody   = http_build_query($this->getPostData());
130
131
        if (true === in_array("Content-Type: application/json", $requestHeader)) {
132
            $requestBody = json_encode($this->getPostData());
133
        }
134
135
        $requestUrl = $this->mergeUrl();
136
        if (0 < count($this->getQueryData())) {
137
            $requestUrl = implode("?", [$requestUrl, http_build_query($this->getQueryData())]);
138
        }
139
140
        $stream = CurlHelper::initStream($requestUrl, $this->getConfiguration());
141
142
        CurlHelper::setHeaders($stream, $requestHeader);
143
        CurlHelper::setPost($stream, $this->getMethod(), $requestBody);
144
        CurlHelper::setProxy($stream, $this->getConfiguration());
145
        CurlHelper::setReturnTransfer($stream);
146
        CurlHelper::setSsl($stream, $this->getConfiguration());
147
        CurlHelper::setTimeout($stream, $this->getConfiguration());
148
        CurlHelper::setUserAgent($stream, $this->getConfiguration());
149
        CurlHelper::setVerbose($stream, $this->getConfiguration(), $requestUrl, $requestBody);
150
151
        $curlExec    = curl_exec($stream);
152
        $curlGetInfo = curl_getinfo($stream, CURLINFO_HEADER_SIZE);
153
154
        $responseHeader = $this->parseheader(substr($curlExec, 0, $curlGetInfo));
155
        $responseBody   = substr($curlExec, $curlGetInfo);
156
        $responseInfo   = curl_getinfo($stream);
157
158
        if (true === $this->getConfiguration()->getDebug()) {
159
            $msg = (new DateTime())->format("c") . " [DEBUG] {$requestUrl}" . PHP_EOL . "HTTP response body ~BEGIN~" . PHP_EOL . print_r($responseBody, true) . PHP_EOL . "~END~" . PHP_EOL;
160
            error_log($msg, 3, $this->getConfiguration()->getDebugFile());
161
        }
162
163
        $response = $this->prepareResponse($requestBody, $requestHeader, $requestUrl, $responseBody, $responseHeader, $responseInfo);
164
165
        $curlHttpCode = $responseInfo["http_code"];
166
        if (200 <= $curlHttpCode && $curlHttpCode <= 299) {
167
            return $response;
168
        }
169
170
        $msg = curl_errno($stream);
171
        if (0 === $curlHttpCode) {
172
            if (false === empty(curl_error($stream))) {
173
                $msg = "Call to ${requestUrl} failed : " . curl_error($stream);
174
            } else {
175
                $msg = "Call to ${requestUrl} failed, but for an unknown reason. This could happen if you are disconnected from the network.";
176
            }
177
        }
178
179
        throw new CurlRequestCallException($msg, $curlHttpCode, $response);
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185
    public function clearHeaders() {
186
        return $this->setHeaders([]);
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function clearPostData() {
193
        return $this->setPostData([]);
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199
    public function clearQueryData() {
200
        return $this->setQueryData([]);
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function getConfiguration() {
207
        return $this->configuration;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function getHeaders() {
214
        return $this->headers;
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220
    public function getMethod() {
221
        return $this->method;
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227
    public function getPostData() {
228
        return $this->postData;
229
    }
230
231
    /**
232
     * {@inheritdoc}
233
     */
234
    public function getQueryData() {
235
        return $this->queryData;
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    public function getResourcePath() {
242
        return $this->resourcePath;
243
    }
244
245
    /**
246
     * Merge the headers.
247
     *
248
     * @return array Returns the merged headers.
249
     */
250
    private function mergeHeaders() {
251
252
        $headers = [];
253
        foreach (array_merge($this->getConfiguration()->getHeaders(), $this->getHeaders()) as $key => $value) {
254
            $headers[] = implode(": ", [$key, $value]);
255
        }
256
257
        return $headers;
258
    }
259
260
    /**
261
     * Merge the URL.
262
     *
263
     * @return string Returns the merged URL.
264
     */
265
    private function mergeUrl() {
266
267
        $mergedURL = [
268
            $this->getConfiguration()->getHost(),
269
        ];
270
271
        if (null !== $this->getResourcePath() && "" !== $this->getResourcePath()) {
272
            $mergedURL[] = $this->getResourcePath();
273
        }
274
275
        return implode("/", $mergedURL);
276
    }
277
278
    /**
279
     * Parse the raw header.
280
     *
281
     * @param string $rawHeader The raw header.
282
     * @return array Returns the headers.
283
     */
284
    private function parseHeader($rawHeader) {
285
286
        $headers = [];
287
        $key     = "";
288
289
        foreach (explode("\n", $rawHeader) as $h) {
290
            $h = explode(":", $h, 2);
291
            if (true === isset($h[1])) {
292
                if (false === isset($headers[$h[0]])) {
293
                    $headers[$h[0]] = trim($h[1]);
294
                } else if (true === is_array($headers[$h[0]])) {
295
                    $headers[$h[0]] = array_merge($headers[$h[0]], [trim($h[1])]);
296
                } else {
297
                    $headers[$h[0]] = array_merge([$headers[$h[0]]], [trim($h[1])]);
298
                }
299
                $key = $h[0];
300
            } else {
301
                if ("\t" === substr($h[0], 0, 1)) {
302
                    $headers[$key] .= "\r\n\t" . trim($h[0]);
303
                } else if (!$key) {
304
                    $headers[0] = trim($h[0]);
305
                }
306
                trim($h[0]);
307
            }
308
        }
309
310
        return $headers;
311
    }
312
313
    /**
314
     * Prepare a response.
315
     *
316
     * @param string $requestBody The request body.
317
     * @param array $requestHeader The request header.
318
     * @param string $requestUri The request URI.
319
     * @param string $responseBody The response body.
320
     * @param array $responseHeader The response header.
321
     * @param array $responseInfo The response info.
322
     * @return CurlResponseInterface Returns the response.
323
     */
324
    private function prepareResponse($requestBody, array $requestHeader, $requestUri, $responseBody, array $responseHeader, array $responseInfo) {
325
326
        $response = CurlFactory::newCURLResponse();
327
        $response->setRequestBody($requestBody);
328
        $response->setRequestHeader($requestHeader);
329
        $response->setRequestUrl($requestUri);
330
        $response->setResponseBody($responseBody);
331
        $response->setResponseHeader($responseHeader);
332
        $response->setResponseInfo($responseInfo);
333
334
        return $response;
335
    }
336
337
    /**
338
     * {@inheritdoc}
339
     */
340
    public function removeHeader($name) {
341
        if (true === array_key_exists($name, $this->headers)) {
342
            unset($this->headers[$name]);
343
        }
344
        return $this;
345
    }
346
347
    /**
348
     * {@inheritdoc}
349
     */
350
    public function removePostData($name) {
351
        if (true === array_key_exists($name, $this->postData)) {
352
            unset($this->postData[$name]);
353
        }
354
        return $this;
355
    }
356
357
    /**
358
     * {@inheritdoc}
359
     */
360
    public function removeQueryData($name) {
361
        if (true === array_key_exists($name, $this->queryData)) {
362
            unset($this->queryData[$name]);
363
        }
364
        return $this;
365
    }
366
367
    /**
368
     * Set the configuration.
369
     *
370
     * @param CurlConfiguration $configuration The configuration.
371
     * @return CurlRequestInterface Returns this cURL request.
372
     */
373
    protected function setConfiguration(CurlConfiguration $configuration) {
374
        $this->configuration = $configuration;
375
        return $this;
376
    }
377
378
    /**
379
     * Set the headers.
380
     *
381
     * @param array $headers The headers.
382
     * @return CurlRequestInterface Returns this cURL request.
383
     */
384
    protected function setHeaders(array $headers) {
385
        $this->headers = $headers;
386
        return $this;
387
    }
388
389
    /**
390
     * Set the method.
391
     *
392
     * @param string $method The method.
393
     * @return CurlRequestInterface Returns this cURL request.
394
     * @throws InvalidArgumentException Throws an invalid argument exception if the method is invalid.
395
     */
396
    protected function setMethod($method) {
397
398
        switch ($method) {
399
            case self::HTTP_METHOD_DELETE:
400
            case self::HTTP_METHOD_GET:
401
            case self::HTTP_METHOD_HEAD:
402
            case self::HTTP_METHOD_OPTIONS:
403
            case self::HTTP_METHOD_PATCH:
404
            case self::HTTP_METHOD_POST:
405
            case self::HTTP_METHOD_PUT:
406
                $this->method = $method;
407
                break;
408
            default:
409
                throw new InvalidArgumentException(sprintf("The HTTP method \"%s\" is invalid", $method));
410
        }
411
412
        return $this;
413
    }
414
415
    /**
416
     * Set the POST data.
417
     *
418
     * @param array $postData The POST data.
419
     * @return CurlRequestInterface Returns this cURL request.
420
     */
421
    protected function setPostData(array $postData) {
422
        $this->postData = $postData;
423
        return $this;
424
    }
425
426
    /**
427
     * Set the query data.
428
     *
429
     * @param array $queryData The query data.
430
     * @return CurlRequestInterface Returns this cURL request.
431
     */
432
    protected function setQueryData(array $queryData) {
433
        $this->queryData = $queryData;
434
        return $this;
435
    }
436
437
    /**
438
     * {@inheritdoc}
439
     */
440
    public function setResourcePath($resourcePath) {
441
        $this->resourcePath = preg_replace("/^\//", "", trim($resourcePath));
442
        return $this;
443
    }
444
}
445