GinoPane /
php-nano-rest
| 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
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
|
|||
| 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 |