Passed
Pull Request — master (#35)
by
unknown
03:59 queued 02:21
created

Client::parseRawHeaders()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 16
rs 10
cc 3
nc 3
nop 1
1
<?php
2
3
namespace Rogierw\RwAcme\Http;
4
5
use CurlHandle;
6
use Rogierw\RwAcme\Interfaces\HttpClientInterface;
7
8
class Client implements HttpClientInterface
9
{
10
    public function __construct(private readonly int $timeout = 10)
11
    {
12
    }
13
14
    public function head(string $url): Response
15
    {
16
        return $this->makeCurlRequest('head', $url);
17
    }
18
19
    public function get(string $url, array $headers = [], array $arguments = [], int $maxRedirects = 0): Response
20
    {
21
        return $this->makeCurlRequest('get', $url, $headers, $arguments, $maxRedirects);
22
    }
23
24
    public function post(string $url, array $payload = [], array $headers = [], int $maxRedirects = 0): Response
25
    {
26
        $headers = array_merge(['Content-Type: application/jose+json'], $headers);
27
28
        return $this->makeCurlRequest('post', $url, $headers, $payload, $maxRedirects);
29
    }
30
31
    private function makeCurlRequest(string $httpVerb, string $fullUrl, array $headers = [], array $payload = [], int $maxRedirects = 0): Response
32
    {
33
        $headers = array_merge([
34
            'Content-Type: ' . ($httpVerb === 'post') ? 'application/jose+json' : 'application/json',
35
        ], $headers);
36
37
        $curlHandle = $this->getCurlHandle($fullUrl, $headers, $maxRedirects);
38
39
        switch ($httpVerb) {
40
            case 'head':
41
                curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, 'HEAD');
42
                curl_setopt($curlHandle, CURLOPT_NOBODY, true);
43
                break;
44
45
            case 'get':
46
                curl_setopt($curlHandle, CURLOPT_URL, $fullUrl . '?' . http_build_query($payload));
47
                break;
48
49
            case 'post':
50
                curl_setopt($curlHandle, CURLOPT_POST, true);
51
                $this->attachRequestPayload($curlHandle, $payload);
52
                break;
53
        }
54
55
        $rawResponse = curl_exec($curlHandle);
56
        $headerSize = curl_getinfo($curlHandle, CURLINFO_HEADER_SIZE);
57
        $headers = curl_getinfo($curlHandle);
58
59
        $rawHeaders = mb_substr($rawResponse, 0, $headerSize);
0 ignored issues
show
Bug introduced by
It seems like $rawResponse can also be of type true; however, parameter $string of mb_substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

59
        $rawHeaders = mb_substr(/** @scrutinizer ignore-type */ $rawResponse, 0, $headerSize);
Loading history...
60
        $rawBody = mb_substr($rawResponse, $headerSize);
61
        $body = $rawBody;
62
63
        if (
64
            $headers['content-type'] === 'application/json' ||
65
            $headers['content-type'] === 'application/problem+json'
66
        ) {
67
            $body = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
68
        }
69
70
        $parsedRawHeaders = $this->parseRawHeaders($rawHeaders);
71
72
        return new Response($headers, $parsedRawHeaders['url'] ?? '', $parsedRawHeaders['http_code'] ?? null, $body);
73
    }
74
75
    private function attachRequestPayload(CurlHandle $curlHandle, array $data): void
76
    {
77
        $encoded = json_encode($data, JSON_THROW_ON_ERROR);
78
79
        curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $encoded);
80
    }
81
82
    private function getCurlHandle(string $fullUrl, array $headers = [], int $maxRedirects = 0): CurlHandle
83
    {
84
        $curlHandle = curl_init();
85
86
        curl_setopt($curlHandle, CURLOPT_URL, $fullUrl);
87
88
        curl_setopt($curlHandle, CURLOPT_HTTPHEADER, array_merge([
89
            'Accept: application/json',
90
        ], $headers));
91
92
        curl_setopt($curlHandle, CURLOPT_USERAGENT, 'rogierw/rw-acme-client');
93
        curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
94
        curl_setopt($curlHandle, CURLOPT_TIMEOUT, $this->timeout);
95
        curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, true);
96
        curl_setopt($curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
97
        curl_setopt($curlHandle, CURLOPT_ENCODING, '');
98
        curl_setopt($curlHandle, CURLOPT_HEADER, true);
99
100
        if ($maxRedirects > 0) {
101
            curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, true);
102
            curl_setopt($curlHandle, CURLOPT_MAXREDIRS, $maxRedirects);
103
        }
104
105
        return $curlHandle;
106
    }
107
108
    private function parseRawHeaders(string $rawHeaders): array
109
    {
110
        $headers = explode("\n", $rawHeaders);
111
        $headersArr = [];
112
113
        foreach ($headers as $header) {
114
            if (!str_contains($header, ':')) {
115
                continue;
116
            }
117
118
            [$name, $value] = explode(':', $header, 2);
119
120
            $headersArr[strtolower($name)] = trim($value);
121
        }
122
123
        return $headersArr;
124
    }
125
}
126