Passed
Pull Request — 1.x (#296)
by Akihito
14:13
created

HttpRequestCurl::initializeCurl()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 2
b 0
f 0
nc 4
nop 3
dl 0
loc 19
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use CurlHandle;
8
9
use function count;
10
use function curl_close;
11
use function curl_exec;
12
use function curl_getinfo;
13
use function curl_init;
14
use function curl_setopt;
15
use function explode;
16
use function http_build_query;
17
use function json_decode;
18
use function strpos;
19
use function strtolower;
20
use function substr;
21
use function trim;
22
23
use const CURLINFO_CONTENT_TYPE;
24
use const CURLINFO_HEADER_SIZE;
25
use const CURLINFO_HTTP_CODE;
26
use const CURLOPT_CUSTOMREQUEST;
27
use const CURLOPT_HEADER;
28
use const CURLOPT_HTTPHEADER;
29
use const CURLOPT_POSTFIELDS;
30
use const CURLOPT_RETURNTRANSFER;
31
use const CURLOPT_URL;
32
33
// ... Other code is omitted for brevity
34
35
/**
36
 * Sends a HTTP request using cURL
37
 *
38
 * @psalm-type RequestOptions = array<null>|array{"body?": string, "headers?": array<string, string>}
39
 * @psalm-type RequestHeaders = array<string, string>
40
 * @psalm-type Body = array<mixed>
41
 */
42
final class HttpRequestCurl implements HttpRequestInterface
43
{
44
    public function __construct(
45
        private HttpRequestHeaders $requestHeaders,
46
    ) {
47
    }
48
49
    /**
50
     * @inheritdoc
51
     */
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
        curl_close($curl);
63
64
        $body = $this->parseBody($curl, $view);
65
66
        return [
67
            'code' => $code,
68
            'headers' => $headers,
69
            'body' => $body,
70
            'view' => $view,
71
        ];
72
    }
73
74
    /** @param RequestOptions $options $ */
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\RequestOptions 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...
75
    private function initializeCurl(string $method, string $uri, string $body): CurlHandle
76
    {
77
        $curl = curl_init();
78
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
79
        curl_setopt($curl, CURLOPT_URL, $uri);
80
81
        if ($this->requestHeaders->headers !== []) {
82
            curl_setopt($curl, CURLOPT_HTTPHEADER, $this->requestHeaders->headers);
83
        }
84
85
        if ($body !== '') {
86
            // Set the request body
87
            curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
88
        }
89
90
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
91
        curl_setopt($curl, CURLOPT_HEADER, true);
92
93
        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...
94
    }
95
96
    /** @return array<string, string> */
97
    private function parseResponseHeaders(string $responseHeaders): array
98
    {
99
        $responseHeadersArray = [];
100
        $headerLines = explode("\r\n", $responseHeaders);
101
        foreach ($headerLines as $line) {
102
            $parts = explode(':', $line, 2);
103
            if (count($parts) !== 2) {
104
                continue;
105
            }
106
107
            $key = $parts[0];
108
109
            $responseHeadersArray[$key] = trim($parts[1]);
110
        }
111
112
        return $responseHeadersArray;
113
    }
114
115
    /** @return array<mixed> */
116
    private function parseBody(CurlHandle $curl, string $view): array
117
    {
118
        $responseBody = [];
119
        $contentType = (string) curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
120
        if (strpos(strtolower($contentType), 'application/json') !== false) {
121
            return (array) json_decode($view, true);
122
        }
123
124
        return $responseBody;
125
    }
126
}
127