AbstractCURLRequest::call()   F
last analyzed

Complexity

Conditions 24
Paths > 20000

Size

Total Lines 151

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 151
rs 0
c 0
b 0
f 0
cc 24
nc 172032
nop 0

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
3
/**
4
 * This file is part of the curl-library package.
5
 *
6
 * (c) 2017 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\CURL\Request;
13
14
use DateTime;
15
use WBW\Library\Core\Argument\ArgumentHelper;
16
use WBW\Library\Core\Exception\HTTP\InvalidHTTPMethodException;
17
use WBW\Library\Core\Network\HTTP\HTTPInterface;
18
use WBW\Library\CURL\Configuration\CURLConfiguration;
19
use WBW\Library\CURL\Exception\CURLRequestCallException;
20
use WBW\Library\CURL\Response\CURLResponse;
21
22
/**
23
 * Abstract cURL request.
24
 *
25
 * @author webeweb <https://github.com/webeweb/>
26
 * @package WBW\Library\CURL\Request
27
 * @abstract
28
 */
29
abstract class AbstractCURLRequest implements CURLRequestInterface, HTTPInterface {
30
31
    /**
32
     * Content-type application/x-www-form-urlencoded
33
     *
34
     * @var string
35
     */
36
    const CONTENT_TYPE_X_WWW_FORM_URLENCODED = "Content-Type: application/x-www-form-urlencoded";
37
38
    /**
39
     * Configuration.
40
     *
41
     * @var CURLConfiguration
42
     */
43
    private $configuration;
44
45
    /**
46
     * Headers.
47
     *
48
     * @var array
49
     */
50
    private $headers = [];
51
52
    /**
53
     * Method.
54
     *
55
     * @var string
56
     */
57
    private $method = self::HTTP_METHOD_GET;
58
59
    /**
60
     * POST data.
61
     *
62
     * @var array
63
     */
64
    private $postData = [];
65
66
    /**
67
     * Query data.
68
     *
69
     * @var array
70
     */
71
    private $queryData = [];
72
73
    /**
74
     * Resource path.
75
     *
76
     * @var string
77
     */
78
    private $resourcePath;
79
80
    /**
81
     * Constructor.
82
     *
83
     * @param string $method The Method.
84
     * @param CURLConfiguration $configuration The configuration.
85
     * @param string $resourcePath The resource path.
86
     * @throws InvalidHTTPMethodException Throws an invalid HTTP method exception if the method is not implemented.
87
     */
88
    protected function __construct($method, CURLConfiguration $configuration, $resourcePath) {
89
        $this->setConfiguration($configuration);
90
        $this->setMethod($method);
91
        $this->setResourcePath($resourcePath);
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function addHeader($name, $value) {
98
        ArgumentHelper::isTypeOf($name, ArgumentHelper::ARGUMENT_STRING);
99
        $this->headers[$name] = $value;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function addPostData($name, $value) {
106
        ArgumentHelper::isTypeOf($name, ArgumentHelper::ARGUMENT_STRING);
107
        $this->postData[$name] = $value;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function addQueryData($name, $value) {
114
        ArgumentHelper::isTypeOf($name, ArgumentHelper::ARGUMENT_STRING);
115
        $this->queryData[$name] = $value;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function call() {
122
123
        // Define the necessary argurments.
124
        $curlHeaders  = $this->mergeHeaders();
125
        $curlPOSTData = http_build_query($this->getPostData());
126
127
        //
128
        if (true === in_array("Content-Type: application/json", $curlHeaders)) {
129
            $curlPOSTData = json_encode($this->getPostData());
130
        }
131
132
        // Initialize the URL.
133
        $curlURL = $this->mergeURL();
134
        if (0 < count($this->getQueryData())) {
135
            $curlURL = implode("?", [$curlURL, http_build_query($this->getQueryData())]);
136
        }
137
138
        // Initialize cURL.
139
        $stream = curl_init();
140
141
        // Set the connect timeout.
142
        if (0 < $this->getConfiguration()->getConnectTimeout()) {
143
            curl_setopt($stream, CURLOPT_CONNECTTIMEOUT, $this->getConfiguration()->getConnectTimeout());
144
        }
145
146
        // Set the encoding.
147
        if (true === $this->getConfiguration()->getAllowEncoding()) {
148
            curl_setopt($stream, CURLOPT_ENCODING, "");
149
        }
150
151
        // Set the HTTP headers.
152
        curl_setopt($stream, CURLOPT_HTTPHEADER, $curlHeaders);
153
154
        // Set the post.
155
        switch ($this->getMethod()) {
156
157
            case self::HTTP_METHOD_DELETE:
158
            case self::HTTP_METHOD_OPTIONS:
159
            case self::HTTP_METHOD_PATCH:
160
            case self::HTTP_METHOD_PUT:
161
                curl_setopt($stream, CURLOPT_CUSTOMREQUEST, $this->getMethod());
162
                curl_setopt($stream, CURLOPT_POSTFIELDS, $curlPOSTData);
163
                break;
164
165
            case self::HTTP_METHOD_HEAD:
166
                curl_setopt($stream, CURLOPT_NOBODY, true);
167
                break;
168
169
            case self::HTTP_METHOD_POST:
170
                curl_setopt($stream, CURLOPT_POST, true);
171
                curl_setopt($stream, CURLOPT_POSTFIELDS, $curlPOSTData);
172
                break;
173
        }
174
175
        // Set the proxy.
176
        if (null !== $this->getConfiguration()->getProxyHost()) {
177
            curl_setopt($stream, CURLOPT_PROXY, $this->getConfiguration()->getProxyHost());
178
        }
179
        if (null !== $this->getConfiguration()->getProxyPort()) {
180
            curl_setopt($stream, CURLOPT_PROXYPORT, $this->getConfiguration()->getProxyPort());
181
        }
182
        if (null !== $this->getConfiguration()->getProxyType()) {
183
            curl_setopt($stream, CURLOPT_PROXYTYPE, $this->getConfiguration()->getProxyType());
184
        }
185
        if (null !== $this->getConfiguration()->getProxyUsername()) {
186
            curl_setopt($stream, CURLOPT_PROXYUSERPWD, implode(":", [$this->getConfiguration()->getProxyUsername(), $this->getConfiguration()->getProxyPassword()]));
187
        }
188
189
        // Set the return.
190
        curl_setopt($stream, CURLOPT_RETURNTRANSFER, true);
191
192
        // Set the SSL verification.
193
        if (false === $this->getConfiguration()->getSslVerification()) {
194
            curl_setopt($stream, CURLOPT_SSL_VERIFYHOST, 0);
195
            curl_setopt($stream, CURLOPT_SSL_VERIFYPEER, 0);
196
        }
197
198
        // Set the request timeout.
199
        if (0 < $this->getConfiguration()->getRequestTimeout()) {
200
            curl_setopt($stream, CURLOPT_TIMEOUT, $this->getConfiguration()->getRequestTimeout());
201
        }
202
203
        // Set the URL.
204
        curl_setopt($stream, CURLOPT_URL, $curlURL);
205
206
        // Set the user agent.
207
        curl_setopt($stream, CURLOPT_USERAGENT, $this->getConfiguration()->getUserAgent());
208
209
        // Get the HTTP response headers.
210
        curl_setopt($stream, CURLOPT_HEADER, 1);
211
212
        // Set the verbose.
213
        if (true === $this->getConfiguration()->getDebug()) {
214
            curl_setopt($stream, CURLOPT_STDERR, fopen($this->getConfiguration()->getDebugFile(), "a"));
215
            curl_setopt($stream, CURLOPT_VERBOSE, 0);
216
217
            $msg = (new DateTime())->format("c") . " [DEBUG] " . $curlURL . PHP_EOL . "HTTP request body ~BEGIN~" . PHP_EOL . print_r($curlPOSTData, true) . PHP_EOL . "~END~" . PHP_EOL;
218
            error_log($msg, 3, $this->getConfiguration()->getDebugFile());
219
        } else {
220
            if (true === $this->getConfiguration()->getVerbose()) {
221
                curl_setopt($stream, CURLOPT_VERBOSE, 1);
222
            } else {
223
                curl_setopt($stream, CURLOPT_VERBOSE, 0);
224
            }
225
        }
226
227
        // Make the request.
228
        $curlExec     = curl_exec($stream);
229
        $httpHeadSize = curl_getinfo($stream, CURLINFO_HEADER_SIZE);
230
        $httpHead     = $this->parseheader(substr($curlExec, 0, $httpHeadSize));
231
        $httpBody     = substr($curlExec, $httpHeadSize);
232
        $curlInfo     = curl_getinfo($stream);
233
234
        //
235
        if (true === $this->getConfiguration()->getDebug()) {
236
            $msg = (new DateTime())->format("c") . " [DEBUG] " . $curlURL . PHP_EOL . "HTTP response body ~BEGIN~" . PHP_EOL . print_r($httpBody, true) . PHP_EOL . "~END~" . PHP_EOL;
237
            error_log($msg, 3, $this->getConfiguration()->getDebugFile());
238
        }
239
240
        // Initialize the response.
241
        $response = new CURLResponse();
242
        $response->setRequestBody($curlPOSTData);
243
        $response->setRequestHeader($curlHeaders);
244
        $response->setRequestURL($curlURL);
245
        $response->setResponseBody($httpBody);
246
        $response->setResponseHeader($httpHead);
247
        $response->setResponseInfo($curlInfo);
248
249
        // Check HTTP code.
250
        if (200 <= $curlInfo["http_code"] && $curlInfo["http_code"] <= 299) {
251
252
            // Return the response.
253
            return $response;
254
        }
255
256
        // Initialize the parameters.
257
        $cde = $curlInfo["http_code"];
258
        $msg = curl_errno($stream);
259
260
        // Check the HTTP code.
261
        if (0 === $curlInfo["http_code"]) {
262
            if (false === empty(curl_error($stream))) {
263
                $msg = "Call to " . $curlURL . " failed : " . curl_error($stream);
264
            } else {
265
                $msg = "Call to " . $curlURL . " failed, but for an unknown reason. This could happen if you are disconnected from the network.";
266
            }
267
        }
268
269
        // Throw the exception.
270
        throw new CURLRequestCallException($msg, $cde, $response);
271
    }
272
273
    /**
274
     * {@inheritdoc}
275
     */
276
    public function clearHeaders() {
277
        return $this->setHeaders();
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283
    public function clearPostData() {
284
        return $this->setPostData();
285
    }
286
287
    /**
288
     * {@inheritdoc}
289
     */
290
    public function clearQueryData() {
291
        return $this->setQueryData();
292
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297
    public function getConfiguration() {
298
        return $this->configuration;
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304
    public function getHeaders() {
305
        return $this->headers;
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311
    public function getMethod() {
312
        return $this->method;
313
    }
314
315
    /**
316
     * {@inheritdoc}
317
     */
318
    public function getPostData() {
319
        return $this->postData;
320
    }
321
322
    /**
323
     * {@inheritdoc}
324
     */
325
    public function getQueryData() {
326
        return $this->queryData;
327
    }
328
329
    /**
330
     * {@inheritdoc}
331
     */
332
    public function getResourcePath() {
333
        return $this->resourcePath;
334
    }
335
336
    /**
337
     * Merge the headers.
338
     *
339
     * @return array Returns the meged headers.
340
     */
341
    private function mergeHeaders() {
342
343
        // Initialize the merged headers.
344
        $mergedHeaders = [];
345
346
        // Handle each header.
347
        foreach (array_merge($this->getConfiguration()->getHeaders(), $this->getHeaders()) as $key => $value) {
348
            $mergedHeaders[] = implode(": ", [$key, $value]);
349
        }
350
351
        // Return the merged headers.
352
        return $mergedHeaders;
353
    }
354
355
    /**
356
     * Merge the URL.
357
     *
358
     * @return string Returns the merged URL.
359
     */
360
    private function mergeURL() {
361
362
        // Initialize the merged URL.
363
        $mergedURL   = [];
364
        $mergedURL[] = $this->getConfiguration()->getHost();
365
        if (null !== $this->getResourcePath() && "" !== $this->getResourcePath()) {
366
            $mergedURL[] = $this->getResourcePath();
367
        }
368
369
        // Return the merged URL.
370
        return implode("/", $mergedURL);
371
    }
372
373
    /**
374
     * Parse the raw header.
375
     *
376
     * @param string $rawHeader The raw header.
377
     * @return array Returns the headers.
378
     */
379
    private function parseHeader($rawHeader) {
380
381
        // Initialize the headers.
382
        $headers = [];
383
        $key     = "";
384
385
        // Handle each header.
386
        foreach (explode("\n", $rawHeader) as $h) {
387
            $h = explode(":", $h, 2);
388
            if (true === isset($h[1])) {
389
                if (false === isset($headers[$h[0]])) {
390
                    $headers[$h[0]] = trim($h[1]);
391
                } elseif (true === is_array($headers[$h[0]])) {
392
                    $headers[$h[0]] = array_merge($headers[$h[0]], [trim($h[1])]);
393
                } else {
394
                    $headers[$h[0]] = array_merge([$headers[$h[0]]], [trim($h[1])]);
395
                }
396
                $key = $h[0];
397
            } else {
398
                if ("\t" === substr($h[0], 0, 1)) {
399
                    $headers[$key] .= "\r\n\t" . trim($h[0]);
400
                } elseif (!$key) {
401
                    $headers[0] = trim($h[0]);
402
                }
403
                trim($h[0]);
404
            }
405
        }
406
407
        // Return the headers.
408
        return $headers;
409
    }
410
411
    /**
412
     * {@inheritdoc}
413
     */
414
    public function removeHeader($name) {
415
        if (true === array_key_exists($name, $this->headers)) {
416
            unset($this->headers[$name]);
417
        }
418
        return $this;
419
    }
420
421
    /**
422
     * {@inheritdoc}
423
     */
424
    public function removePostData($name) {
425
        if (true === array_key_exists($name, $this->postData)) {
426
            unset($this->postData[$name]);
427
        }
428
        return $this;
429
    }
430
431
    /**
432
     * {@inheritdoc}
433
     */
434
    public function removeQueryData($name) {
435
        if (true === array_key_exists($name, $this->queryData)) {
436
            unset($this->queryData[$name]);
437
        }
438
        return $this;
439
    }
440
441
    /**
442
     * Set the configuration.
443
     *
444
     * @param CURLConfiguration $configuration The configuration.
445
     * @return AbstractCURLRequest Returns this request.
446
     */
447
    protected function setConfiguration(CURLConfiguration $configuration) {
448
        $this->configuration = $configuration;
449
        return $this;
450
    }
451
452
    /**
453
     * Set the headers.
454
     *
455
     * @param array $headers The headers.
456
     * @return AbstractCURLRequest Returns this request.
457
     */
458
    protected function setHeaders(array $headers = []) {
459
        $this->headers = $headers;
460
        return $this;
461
    }
462
463
    /**
464
     * Set the method.
465
     *
466
     * @param string $method The method.
467
     * @return AbstractCURLRequest Returns this request.
468
     * @throws InvalidHTTPMethodException Throws an invalid HTTP method exception if the method is not implemented.
469
     */
470
    protected function setMethod($method) {
471
        switch ($method) {
472
            case self::HTTP_METHOD_DELETE:
473
            case self::HTTP_METHOD_GET:
474
            case self::HTTP_METHOD_HEAD:
475
            case self::HTTP_METHOD_OPTIONS:
476
            case self::HTTP_METHOD_PATCH:
477
            case self::HTTP_METHOD_POST:
478
            case self::HTTP_METHOD_PUT:
479
                $this->method = $method;
480
                break;
481
            default:
482
                throw new InvalidHTTPMethodException($method);
483
        }
484
        return $this;
485
    }
486
487
    /**
488
     * Set the POST data.
489
     *
490
     * @param array $postData The POST data.
491
     * @return AbstractCURLRequest Returns this request.
492
     */
493
    protected function setPostData(array $postData = []) {
494
        $this->postData = $postData;
495
        return $this;
496
    }
497
498
    /**
499
     * Set the query data.
500
     *
501
     * @param array $queryData The query data.
502
     * @return AbstractCURLRequest Returns this request.
503
     */
504
    protected function setQueryData(array $queryData = []) {
505
        $this->queryData = $queryData;
506
        return $this;
507
    }
508
509
    /**
510
     * {@inheritdoc}
511
     */
512
    public function setResourcePath($resourcePath) {
513
        $this->resourcePath = preg_replace("/^\//", "", trim($resourcePath));
514
        return $this;
515
    }
516
517
}
518