Issues (55)

src/HttpRequestCurl.php (1 issue)

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 str_contains;
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
/**
34
 * Sends a HTTP request using cURL
35
 *
36
 * @psalm-type RequestOptions = array<null>|array{"body?": string, "headers?": array<string, string>}
37
 * @psalm-type RequestHeaders = array<string, string>
38
 * @psalm-type Body = array<mixed>
39
 */
40
final class HttpRequestCurl implements HttpRequestInterface
41
{
42
    public function __construct(
43
        private readonly HttpRequestHeaders $requestHeaders,
44
    ) {
45
    }
46
47
    /** @inheritDoc */
48
    public function request(string $method, string $uri, array $query): array
49
    {
50
        $body = http_build_query($query);
51
        $curl = $this->initializeCurl($method, $uri, $body);
52
        $response = (string) curl_exec($curl);
53
        $code = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
54
        $headerSize = (int) curl_getinfo($curl, CURLINFO_HEADER_SIZE);
55
        $headerString = substr($response, 0, $headerSize);
56
        $view = substr($response, $headerSize);
57
        $headers = $this->parseResponseHeaders($headerString);
58
        curl_close($curl);
59
60
        $body = $this->parseBody($curl, $view);
61
62
        return [
63
            'code' => $code,
64
            'headers' => $headers,
65
            'body' => $body,
66
            'view' => $view,
67
        ];
68
    }
69
70
    private function initializeCurl(string $method, string $uri, string $body): CurlHandle
71
    {
72
        $curl = curl_init();
73
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
74
        curl_setopt($curl, CURLOPT_URL, $uri);
75
76
        if ($this->requestHeaders->headers !== []) {
77
            curl_setopt($curl, CURLOPT_HTTPHEADER, $this->requestHeaders->headers);
78
        }
79
80
        if ($body !== '') {
81
            // Set the request body
82
            curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
83
        }
84
85
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
86
        curl_setopt($curl, CURLOPT_HEADER, true);
87
88
        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...
89
    }
90
91
    /** @return array<string, string> */
92
    private function parseResponseHeaders(string $responseHeaders): array
93
    {
94
        $responseHeadersArray = [];
95
        $headerLines = explode("\r\n", $responseHeaders);
96
        foreach ($headerLines as $line) {
97
            $parts = explode(':', $line, 2);
98
            if (count($parts) !== 2) {
99
                continue;
100
            }
101
102
            $key = $parts[0];
103
104
            $responseHeadersArray[$key] = trim($parts[1]);
105
        }
106
107
        return $responseHeadersArray;
108
    }
109
110
    /** @return array<mixed> */
111
    private function parseBody(CurlHandle $curl, string $view): array
112
    {
113
        $responseBody = [];
114
        $contentType = (string) curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
115
        if (str_contains(strtolower($contentType), 'application/json')) {
116
            return (array) json_decode($view, true);
117
        }
118
119
        return $responseBody;
120
    }
121
}
122