HttpRequestCurl   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Importance

Changes 9
Bugs 0 Features 0
Metric Value
wmc 11
eloc 43
c 9
b 0
f 0
dl 0
loc 86
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A request() 0 19 1
A parseResponseHeaders() 0 16 3
A initializeCurl() 0 25 4
A parseBody() 0 9 2
A __construct() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use CurlHandle;
8
use Override;
9
use RuntimeException;
10
11
use function assert;
12
use function count;
13
use function curl_exec;
14
use function curl_getinfo;
15
use function curl_init;
16
use function curl_setopt;
17
use function explode;
18
use function http_build_query;
19
use function json_decode;
20
use function str_contains;
21
use function strtolower;
22
use function substr;
23
use function trim;
24
25
use const CURLINFO_CONTENT_TYPE;
26
use const CURLINFO_HEADER_SIZE;
27
use const CURLINFO_HTTP_CODE;
28
use const CURLOPT_CUSTOMREQUEST;
29
use const CURLOPT_HEADER;
30
use const CURLOPT_HTTPHEADER;
31
use const CURLOPT_POSTFIELDS;
32
use const CURLOPT_RETURNTRANSFER;
33
use const CURLOPT_URL;
34
35
/**
36
 * Sends a HTTP request using cURL
37
 *
38
 * @psalm-import-type Query from Types
39
 * @psalm-import-type HttpHeaders from Types
40
 * @psalm-import-type HttpBody from Types
41
 * @psalm-import-type RequestOptions from Types
42
 */
43
final class HttpRequestCurl implements HttpRequestInterface
44
{
45
    public function __construct(
46
        private readonly HttpRequestHeaders $requestHeaders,
47
    ) {
48
    }
49
50
    /** @inheritDoc */
51
    #[Override]
52
    public function request(string $method, string $uri, array $query): array
53
    {
54
        $body = http_build_query($query);
55
        $curl = $this->initializeCurl($method, $uri, $body);
56
        $response = (string) curl_exec($curl);
57
        $code = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
58
        $headerSize = (int) curl_getinfo($curl, CURLINFO_HEADER_SIZE);
59
        $headerString = substr($response, 0, $headerSize);
60
        $view = substr($response, $headerSize);
61
        $headers = $this->parseResponseHeaders($headerString);
62
63
        $body = $this->parseBody($curl, $view);
64
65
        return [
66
            'code' => $code,
67
            'headers' => $headers,
68
            'body' => $body,
69
            'view' => $view,
70
        ];
71
    }
72
73
    private function initializeCurl(string $method, string $uri, string $body): CurlHandle
74
    {
75
        $curl = curl_init();
76
        if ($curl === false) {
77
            throw new RuntimeException('Failed to initialize cURL'); // @codeCoverageIgnore
78
        }
79
80
        assert($method !== '');
81
        assert($uri !== '');
82
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
83
        curl_setopt($curl, CURLOPT_URL, $uri);
84
85
        if ($this->requestHeaders->headers !== []) {
86
            curl_setopt($curl, CURLOPT_HTTPHEADER, $this->requestHeaders->headers);
87
        }
88
89
        if ($body !== '') {
90
            // Set the request body
91
            curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
92
        }
93
94
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
95
        curl_setopt($curl, CURLOPT_HEADER, true);
96
97
        return $curl;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $curl could return the type resource which is incompatible with the type-hinted return CurlHandle. Consider adding an additional type-check to rule them out.
Loading history...
98
    }
99
100
    /** @return HttpHeaders */
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\HttpHeaders was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
101
    private function parseResponseHeaders(string $responseHeaders): array
102
    {
103
        $responseHeadersArray = [];
104
        $headerLines = explode("\r\n", $responseHeaders);
105
        foreach ($headerLines as $line) {
106
            $parts = explode(':', $line, 2);
107
            if (count($parts) !== 2) {
108
                continue;
109
            }
110
111
            $key = $parts[0];
112
113
            $responseHeadersArray[$key] = trim($parts[1]);
114
        }
115
116
        return $responseHeadersArray;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $responseHeadersArray returns the type array which is incompatible with the documented return type BEAR\Resource\HttpHeaders.
Loading history...
117
    }
118
119
    /** @return HttpBody */
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\HttpBody was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
120
    private function parseBody(CurlHandle $curl, string $view): array
121
    {
122
        $responseBody = [];
123
        $contentType = (string) curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
124
        if (str_contains(strtolower($contentType), 'application/json')) {
125
            return (array) json_decode($view, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return (array)json_decode($view, true) returns the type array which is incompatible with the documented return type BEAR\Resource\HttpBody.
Loading history...
126
        }
127
128
        return $responseBody;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $responseBody returns the type array which is incompatible with the documented return type BEAR\Resource\HttpBody.
Loading history...
129
    }
130
}
131