Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
91.58% |
87 / 95 |
|
82.35% |
14 / 17 |
CRAP | |
0.00% |
0 / 1 |
Curl | |
91.58% |
87 / 95 |
|
82.35% |
14 / 17 |
48.32 | |
0.00% |
0 / 1 |
__construct | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
3.02 | |||
setMethod | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
curl | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setOption | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
setOptions | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
setReturnHeader | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setReturnTransfer | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isReturnHeader | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isReturnTransfer | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getOption | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
hasOption | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getInfo | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
open | |
100.00% |
36 / 36 |
|
100.00% |
1 / 1 |
14 | |||
send | |
83.33% |
15 / 18 |
|
0.00% |
0 / 1 |
7.23 | |||
reset | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
version | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
disconnect | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Http\Client; |
15 | |
16 | use Pop\Http\Parser; |
17 | use Pop\Mime\Message; |
18 | |
19 | /** |
20 | * HTTP curl client class |
21 | * |
22 | * @category Pop |
23 | * @package Pop\Http |
24 | * @author Nick Sagona, III <dev@nolainteractive.com> |
25 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
26 | * @license http://www.popphp.org/license New BSD License |
27 | * @version 4.1.0 |
28 | */ |
29 | class Curl extends AbstractClient |
30 | { |
31 | |
32 | /** |
33 | * cURL options |
34 | * @var array |
35 | */ |
36 | protected $options = []; |
37 | |
38 | /** |
39 | * Constructor |
40 | * |
41 | * Instantiate the cURL object |
42 | * |
43 | * @param string $url |
44 | * @param string $method |
45 | * @param array $opts |
46 | * @throws Exception |
47 | */ |
48 | public function __construct($url = null, $method = 'GET', array $opts = null) |
49 | { |
50 | if (!function_exists('curl_init')) { |
51 | throw new Exception('Error: cURL is not available.'); |
52 | } |
53 | |
54 | $this->resource = curl_init(); |
55 | |
56 | parent::__construct($url, $method); |
57 | |
58 | |
59 | $this->setOption(CURLOPT_HEADER, true); |
60 | $this->setOption(CURLOPT_RETURNTRANSFER, true); |
61 | |
62 | if (null !== $opts) { |
63 | $this->setOptions($opts); |
64 | } |
65 | } |
66 | |
67 | /** |
68 | * Set the method |
69 | * |
70 | * @param string $method |
71 | * @param boolean $strict |
72 | * @throws Exception |
73 | * @return Curl |
74 | */ |
75 | public function setMethod($method, $strict = true) |
76 | { |
77 | parent::setMethod($method, $strict); |
78 | |
79 | if ($this->method != 'GET') { |
80 | switch ($this->method) { |
81 | case 'POST': |
82 | $this->setOption(CURLOPT_POST, true); |
83 | break; |
84 | default: |
85 | $this->setOption(CURLOPT_CUSTOMREQUEST, $this->method); |
86 | } |
87 | } |
88 | |
89 | |
90 | return $this; |
91 | } |
92 | |
93 | /** |
94 | * Return cURL resource (alias to $this->getResource()) |
95 | * |
96 | * @return resource |
97 | */ |
98 | public function curl() |
99 | { |
100 | return $this->resource; |
101 | } |
102 | |
103 | /** |
104 | * Set cURL session option |
105 | * |
106 | * @param int $opt |
107 | * @param mixed $val |
108 | * @return Curl |
109 | */ |
110 | public function setOption($opt, $val) |
111 | { |
112 | // Set the protected property to keep track of the cURL options. |
113 | $this->options[$opt] = $val; |
114 | curl_setopt($this->resource, $opt, $val); |
115 | |
116 | return $this; |
117 | } |
118 | |
119 | /** |
120 | * Set cURL session options |
121 | * |
122 | * @param array $opts |
123 | * @return Curl |
124 | */ |
125 | public function setOptions($opts) |
126 | { |
127 | // Set the protected property to keep track of the cURL options. |
128 | foreach ($opts as $k => $v) { |
129 | $this->options[$k] = $v; |
130 | } |
131 | |
132 | curl_setopt_array($this->resource, $opts); |
133 | |
134 | return $this; |
135 | } |
136 | |
137 | /** |
138 | * Set cURL option to return the header |
139 | * |
140 | * @param boolean $header |
141 | * @return Curl |
142 | */ |
143 | public function setReturnHeader($header = true) |
144 | { |
145 | $this->setOption(CURLOPT_HEADER, (bool)$header); |
146 | return $this; |
147 | } |
148 | |
149 | /** |
150 | * Set cURL option to return the transfer |
151 | * |
152 | * @param boolean $transfer |
153 | * @return Curl |
154 | */ |
155 | public function setReturnTransfer($transfer = true) |
156 | { |
157 | $this->setOption(CURLOPT_RETURNTRANSFER, (bool)$transfer); |
158 | return $this; |
159 | } |
160 | |
161 | /** |
162 | * Check if cURL is set to return header |
163 | * |
164 | * @return boolean |
165 | */ |
166 | public function isReturnHeader() |
167 | { |
168 | return (isset($this->options[CURLOPT_HEADER]) && ($this->options[CURLOPT_HEADER] == true)); |
169 | } |
170 | |
171 | /** |
172 | * Check if cURL is set to return transfer |
173 | * |
174 | * @return boolean |
175 | */ |
176 | public function isReturnTransfer() |
177 | { |
178 | return (isset($this->options[CURLOPT_RETURNTRANSFER]) && ($this->options[CURLOPT_RETURNTRANSFER] == true)); |
179 | } |
180 | |
181 | /** |
182 | * Get a cURL session option |
183 | * |
184 | * @param int $opt |
185 | * @return string |
186 | */ |
187 | public function getOption($opt) |
188 | { |
189 | return (isset($this->options[$opt])) ? $this->options[$opt] : null; |
190 | } |
191 | |
192 | /** |
193 | * Has a cURL session option |
194 | * |
195 | * @param int $opt |
196 | * @return boolean |
197 | */ |
198 | public function hasOption($opt) |
199 | { |
200 | return (isset($this->options[$opt])); |
201 | } |
202 | |
203 | /** |
204 | * Return the cURL session last info |
205 | * |
206 | * @param int $opt |
207 | * @return array|string |
208 | */ |
209 | public function getInfo($opt = null) |
210 | { |
211 | return (null !== $opt) ? curl_getinfo($this->resource, $opt) : curl_getinfo($this->resource); |
212 | } |
213 | |
214 | /** |
215 | * Create and open cURL resource |
216 | * |
217 | * @return Curl |
218 | */ |
219 | public function open() |
220 | { |
221 | $url = $this->url; |
222 | |
223 | if (null !== $this->request) { |
224 | // Set query data if there is any |
225 | if ($this->request->hasFields()) { |
226 | // Append GET query string to URL |
227 | if (($this->method == 'GET') && ((!$this->request->hasHeader('Content-Type')) || |
228 | ($this->request->getHeaderValue('Content-Type') == 'application/x-www-form-urlencoded'))) { |
229 | $url .= '?' . $this->request->getQuery(); |
230 | if (!$this->request->hasHeader('Content-Type')) { |
231 | $this->request->addHeader('Content-Type', 'application/x-www-form-urlencoded'); |
232 | } |
233 | // Else, prepare request data for transmission |
234 | } else { |
235 | // If request is JSON |
236 | if ($this->request->getHeaderValue('Content-Type') == 'application/json') { |
237 | $content = json_encode($this->request->getFields(), JSON_PRETTY_PRINT); |
238 | $this->request->addHeader('Content-Length', strlen($content)); |
239 | $this->setOption(CURLOPT_POSTFIELDS, $content); |
240 | // If request is a URL-encoded form |
241 | } else if ($this->request->getHeaderValue('Content-Type') == 'application/x-www-form-urlencoded') { |
242 | $this->request->addHeader('Content-Length', $this->request->getQueryLength()); |
243 | $this->setOption(CURLOPT_POSTFIELDS, $this->request->getQuery()); |
244 | // Else, if request is a multipart form |
245 | } else if ($this->request->isMultipartForm()) { |
246 | $formMessage = Message::createForm($this->request->getFields()); |
247 | $header = $formMessage->getHeader('Content-Type'); |
248 | $content = $formMessage->render(false); |
249 | $formMessage->removeHeader('Content-Type'); |
250 | $this->request->addHeader($header) |
251 | ->addHeader('Content-Length', strlen($content)); |
252 | $this->setOption(CURLOPT_POSTFIELDS, $content); |
253 | // Else, basic request with data |
254 | } else { |
255 | $this->setOption(CURLOPT_POSTFIELDS, $this->request->getFields()); |
256 | if (!$this->request->hasHeader('Content-Type')) { |
257 | $this->request->addHeader('Content-Type', 'application/x-www-form-urlencoded'); |
258 | } |
259 | } |
260 | } |
261 | // Else, if there is raw body content |
262 | } else if ($this->request->hasBodyContent()) { |
263 | $this->request->addHeader('Content-Length', strlen($this->request->getBodyContent())); |
264 | $this->setOption(CURLOPT_POSTFIELDS, $this->request->getBodyContent()); |
265 | } |
266 | |
267 | if ($this->request->hasHeaders()) { |
268 | $headers = []; |
269 | foreach ($this->request->getHeaders() as $header) { |
270 | $headers[] = $header->render(); |
271 | } |
272 | $this->setOption(CURLOPT_HTTPHEADER, $headers); |
273 | } |
274 | } |
275 | |
276 | $this->setOption(CURLOPT_URL, $url); |
277 | |
278 | return $this; |
279 | } |
280 | |
281 | /** |
282 | * Method to send the request and get the response |
283 | * |
284 | * @return void |
285 | */ |
286 | public function send() |
287 | { |
288 | $this->open(); |
289 | |
290 | $response = curl_exec($this->resource); |
291 | |
292 | if ($response === false) { |
293 | $this->throwError('Error: ' . curl_errno($this->resource) . ' => ' . curl_error($this->resource) . '.'); |
294 | } |
295 | |
296 | if (null === $this->response) { |
297 | $this->response = new Response(); |
298 | } |
299 | |
300 | // If the CURLOPT_RETURNTRANSFER option is set, get the response body and parse the headers. |
301 | if (isset($this->options[CURLOPT_RETURNTRANSFER]) && ($this->options[CURLOPT_RETURNTRANSFER] == true)) { |
302 | $headerSize = $this->getInfo(CURLINFO_HEADER_SIZE); |
303 | if ($this->options[CURLOPT_HEADER]) { |
304 | $parsedHeaders = Parser::parseHeaders(substr($response, 0, $headerSize)); |
305 | $this->response->setVersion($parsedHeaders['version']); |
306 | $this->response->setCode($parsedHeaders['code']); |
307 | $this->response->setMessage($parsedHeaders['message']); |
308 | $this->response->addHeaders($parsedHeaders['headers']); |
309 | $this->response->setBody(substr($response, $headerSize)); |
310 | } else { |
311 | $this->response->setBody($response); |
312 | } |
313 | } |
314 | |
315 | if ($this->response->hasHeader('Content-Encoding')) { |
316 | $this->response->decodeBodyContent(); |
317 | } |
318 | } |
319 | /** |
320 | * Method to reset the client object |
321 | * |
322 | * @return CUrl |
323 | */ |
324 | public function reset() |
325 | { |
326 | $this->request = new Request(); |
327 | $this->response = new Response(); |
328 | $this->options = []; |
329 | |
330 | return $this; |
331 | } |
332 | |
333 | /** |
334 | * Return the cURL version |
335 | * |
336 | * @return array |
337 | */ |
338 | public function version() |
339 | { |
340 | return curl_version(); |
341 | } |
342 | |
343 | /** |
344 | * Close the cURL connection |
345 | * |
346 | * @return void |
347 | */ |
348 | public function disconnect() |
349 | { |
350 | if ($this->hasResource()) { |
351 | curl_close($this->resource); |
352 | $this->resource = null; |
353 | } |
354 | } |
355 | |
356 | } |