CurlHelper::getRequestHandle()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 1
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace GinoPane\NanoRest\Supplemental;
4
5
use GinoPane\NanoRest\{
6
    Request\RequestContext,
7
    Exceptions\TransportException
8
};
9
10
/**
11
 * Class CurlHelper
12
 */
13
class CurlHelper
14
{
15
    /**
16
     * Get a customized request handler to perform calls
17
     *
18
     * @param RequestContext $context
19
     *
20
     * @return resource
21
     */
22
    public function getRequestHandle(RequestContext $context)
23
    {
24
        $curlHandle = curl_init();
25
26
        $defaults = $this->getDefaultSettings($context);
27
28
        $defaults += $this->getCurlTimeoutSettings($context);
29
30
        $defaults += $this->getCurlSslSettings();
31
32
        $defaults += $this->getProxySettings($context);
33
34
        curl_setopt_array(
35
            $curlHandle, //@codeCoverageIgnore
36
            $context->getCurlOptions() + $defaults + $this->getRequestDataAndMethodOptions($context)
37
        );
38
39
        return $curlHandle;
40
    }
41
42
    /**
43
     * Execute curl handle
44
     *
45
     * @param resource $curlHandle
46
     *
47
     * @throws TransportException
48
     *
49
     * @return mixed
50
     */
51
    public function executeRequestHandle($curlHandle)
52
    {
53
        curl_setopt($curlHandle, CURLOPT_VERBOSE, true);
54
        $verbose = fopen('php://temp', 'w+');
55
        curl_setopt($curlHandle, CURLOPT_STDERR, $verbose);
56
57
        @list($headers, $response) = explode("\r\n\r\n", curl_exec($curlHandle), 2);
58
59
        $error = curl_error($curlHandle);
60
        $errorNumber = curl_errno($curlHandle);
61
        $httpStatus = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
62
63
        curl_close($curlHandle);
64
65
        $this->handleCurlError($error, $errorNumber, $verbose);
66
67
        return [
68
            'response' => $response,
69
            'httpStatus' => $httpStatus,
70
            'headers' => $headers
71
        ];
72
    }
73
74
    /**
75
     * Get SSL settings for CURL handler
76
     *
77
     * CA certificate bundle was generated at Wed Jan 17 04:12:05 2018 GMT.
78
     * CURL_SSL_XXX options has recommended values for production environments
79
     *
80
     * @link https://curl.haxx.se/docs/caextract.html
81
     *
82
     * @link https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html
83
     * @link https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html
84
     *
85
     * @return array
86
     */
87
    private function getCurlSslSettings(): array
88
    {
89
        return [
90
            CURLOPT_SSL_VERIFYPEER  => true,
91
            CURLOPT_SSL_VERIFYHOST  => 2,
92
            CURLOPT_CAINFO          => \GinoPane\NanoRest\ROOT_DIRECTORY . DIRECTORY_SEPARATOR . 'cacert.pem'
93
        ];
94
    }
95
96
    /**
97
     * Get proxy settings for cURL handle
98
     *
99
     * @param RequestContext $context
100
     * @return array
101
     */
102
    private function getProxySettings(RequestContext $context): array
103
    {
104
        $proxy = [];
105
106
        if (!is_null($context->getProxy())) {
0 ignored issues
show
introduced by
The condition ! is_null($context->getProxy()) can never be false.
Loading history...
107
            $proxy[CURLOPT_PROXY] = $context->getProxy();
108
        }
109
110
        return $proxy;
111
    }
112
113
    /**
114
     * Get timeout settings for CURL handler
115
     *
116
     * @link https://curl.haxx.se/libcurl/c/CURLOPT_TIMEOUT_MS.html
117
     * @link https://curl.haxx.se/libcurl/c/CURLOPT_CONNECTTIMEOUT_MS.html
118
     *
119
     * @param RequestContext $context
120
     *
121
     * @return array
122
     */
123
    private function getCurlTimeoutSettings(RequestContext $context): array
124
    {
125
        $timeoutOptions = [];
126
127
        $timeout = $context->getTimeout();
128
129
        $timeoutOptions += $this->fillTimeoutOptions($timeout, CURLOPT_TIMEOUT, CURLOPT_TIMEOUT_MS);
130
131
        $connectionTimeout = $context->getConnectionTimeout();
132
133
        $timeoutOptions += $this->fillTimeoutOptions(
134
            $connectionTimeout, //@codeCoverageIgnore
135
            CURLOPT_CONNECTTIMEOUT,
136
            CURLOPT_CONNECTTIMEOUT_MS
137
        );
138
139
        return $timeoutOptions;
140
    }
141
142
    /**
143
     * Get defaults settings for cURL handle
144
     *
145
     * @param RequestContext $context
146
     * @return array
147
     */
148
    private function getDefaultSettings(RequestContext $context): array
149
    {
150
        $defaults = [
151
            CURLOPT_ENCODING => "",
152
            CURLOPT_USERAGENT => "php-nano-rest",
153
            CURLOPT_HEADER => true,
154
            CURLOPT_HTTPHEADER => array_values($context->getRequestHeaders()),
155
            CURLOPT_RETURNTRANSFER => true,
156
        ];
157
158
        return $defaults;
159
    }
160
161
    /**
162
     * @param RequestContext $context
163
     *
164
     * @return array
165
     */
166
    private function getRequestDataAndMethodOptions(RequestContext $context)
167
    {
168
        $curlOptions = array();
169
170
        $requestData = $context->getRequestData();
171
172
        $url = $context->getRequestUrl();
173
174
        switch ($context->getMethod()) {
175
            case RequestContext::METHOD_GET:
176
                $curlOptions[CURLOPT_HTTPGET] = 1;
177
                $url = $context->attachQueryToUrl($url, $requestData);
178
                break;
179
            case RequestContext::METHOD_POST:
180
                $curlOptions[CURLOPT_POST] = 1;
181
                $curlOptions[CURLOPT_POSTFIELDS] = $requestData;
182
                break;
183
            default:
184
                $curlOptions[CURLOPT_CUSTOMREQUEST] = $context->getMethod();
185
                $curlOptions[CURLOPT_POSTFIELDS] = $requestData;
186
        }
187
188
        $curlOptions[CURLOPT_URL] = $url;
189
190
        return $curlOptions;
191
    }
192
193
    /**
194
     * @param $error
195
     * @param $errorNumber
196
     * @param $verbose
197
     *
198
     * @throws TransportException
199
     */
200
    private function handleCurlError($error, $errorNumber, $verbose): void
201
    {
202
        if (!$error) {
203
            return;
204
        }
205
206
        $errorMessage = "\ncURL transport error: $errorNumber - $error";
207
208
        $transportException = new TransportException($errorMessage);
209
210
        rewind($verbose);
211
212
        $verboseLog = stream_get_contents($verbose);
213
214
        if ($verboseLog !== false) {
0 ignored issues
show
introduced by
The condition $verboseLog !== false can never be false.
Loading history...
215
            $verboseLog = htmlspecialchars($verboseLog);
216
217
            $transportException->setData($verboseLog);
218
        }
219
220
        throw $transportException;
221
    }
222
223
    /**
224
     * Fill timeout options
225
     *
226
     * @param $timeout
227
     * @param $optionName
228
     * @param $optionNameMs
229
     *
230
     * @return array
231
     */
232
    private function fillTimeoutOptions($timeout, $optionName, $optionNameMs): array
233
    {
234
        $timeoutOptions = [];
235
236
        if (is_int($timeout)) {
237
            $timeoutOptions[$optionName] = $timeout;
238
        } elseif (is_float($timeout)) {
239
            $timeoutOptions[$optionNameMs] = $timeout * 1000;
240
            $timeoutOptions[CURLOPT_NOSIGNAL] = 1;
241
        }
242
243
        return $timeoutOptions;
244
    }
245
}
246