1 <?php
2
3 namespace Alo;
4
5 use Alo\Exception\ExtensionException as EE;
6 use Log;
7
8 if (!defined('GEN_START')) {
9 http_response_code(404);
10 } else {
11
12 /**
13 * Object-oriented Curl wrapper
14 *
15 * @author Art <a.molcanovas@gmail.com>
16 */
17 class Curl {
18
19 /**
20 * Static reference to the last instance of the class
21 *
22 * @var Curl
23 */
24 static $this;
25
26 /**
27 * The Curl resource
28 *
29 * @var resource
30 */
31 protected $ch;
32
33 /**
34 * Result of exec()
35 *
36 * @var mixed
37 * @see self::exec()
38 */
39 protected $exec;
40
41 /**
42 * Error number of exec()
43 *
44 * @var int
45 * @see self::exec()
46 */
47 protected $errno;
48
49 /**
50 * Error message of exec()
51 *
52 * @var string
53 * @see self::exec()
54 */
55 protected $error;
56
57 /**
58 * Whether the connection is open
59 *
60 * @var boolean
61 */
62 protected $isOpen;
63
64 /**
65 * Instantiates the library
66 *
67 * @author Art <a.molcanovas@gmail.com>
68 *
69 * @param string $url Optionally, the URL for curl_init()
70 *
71 * @throws EE When the curl extention is not loaded
72 */
73 function __construct($url = null) {
74 if (!function_exists('curl_init')) {
75 throw new EE('Curl PHP extension not loaded', EE::E_EXT_NOT_LOADED);
76 } else {
77 $this->ch = curl_init();
78 $this->isOpen = true;
79 curl_setopt_array($this->ch,
80 [CURLOPT_RETURNTRANSFER => true,
81 CURLOPT_SSL_VERIFYPEER => true,
82 CURLOPT_FOLLOWLOCATION => true]);
83
84 Log::debug('Initialised Curl');
85
86 if ($url) {
87 $this->setURL($url);
88 }
89 }
90
91 self::$this = &$this;
92 }
93
94 /**
95 * Sets the connection URL
96 *
97 * @author Art <a.molcanovas@gmail.com>
98 *
99 * @param string $url The URL
100 *
101 * @return Curl
102 */
103 function setURL($url) {
104 Log::debug('Set Curl URL to ' . $url);
105 curl_setopt($this->ch, CURLOPT_URL, $url);
106
107 return $this;
108 }
109
110 /**
111 * Instantiates the library
112 *
113 * @author Art <a.molcanovas@gmail.com>
114 *
115 * @param string $url Optionally, the URL for curl_init()
116 *
117 * @throws EE When the curl extention is not loaded
118 *
119 * @return Curl
120 */
121 static function cURL($url = null) {
122 return new Curl($url);
123 }
124
125 /**
126 * A static wrapper function for __construct()
127 *
128 * @author Art <a.molcanovas@gmail.com>
129 *
130 * @param string $url Optionally, the URL for curl_init()
131 *
132 * @return Curl
133 */
134 static function init($url = null) {
135 return new Curl($url);
136 }
137
138 /**
139 * Sets CURLOPT_NOPROGRESS to FALSE and supplies the progress function
140 *
141 * @author Art <a.molcanovas@gmail.com>
142 *
143 * @param callable $callable The function
144 *
145 * @return bool
146 */
147 function setProgressFunction($callable) {
148 if (!is_callable($callable)) {
149 return false;
150 } else {
151 curl_setopt_array($this->ch,
152 [CURLOPT_NOPROGRESS => false,
153 CURLOPT_PROGRESSFUNCTION => $callable]);
154
155 return true;
156 }
157 }
158
159 /**
160 * Set whether Curl should time out
161 *
162 * @author Art <a.molcanovas@gmail.com>
163 *
164 * @param boolean $enabled The switch
165 *
166 * @return Curl
167 */
168 function notimeout($enabled = true) {
169 $a = $enabled ? [CURLOPT_CONNECTTIMEOUT => 86400,
170 CURLOPT_TIMEOUT => 86400] : [CURLOPT_CONNECTTIMEOUT => 1,
171 CURLOPT_TIMEOUT => 1];
172
173 Log::debug('Curl timeout ' . ($enabled ? 'en' : 'dis') . 'abled');
174
175 return $this->setoptArray($a);
176 }
177
178 /**
179 * Sets an array of options
180 *
181 * @author Art <a.molcanovas@gmail.com>
182 *
183 * @param array $a An array specifying which options to set and their values. The keys should be valid
184 * curl_setopt() constants or their integer equivalents.
185 *
186 * @return Curl
187 * @link http://php.net/manual/en/function.curl-setopt-array.php
188 */
189 function setoptArray(array $a) {
190 curl_setopt_array($this->ch, $a);
191
192 return $this;
193 }
194
195 /**
196 * Toggles lax SSL verification mode which doesn't check certificates
197 *
198 * @author Art <a.molcanovas@gmail.com>
199 *
200 * @param boolean $enabled Whether the mode is enabled or disabled
201 *
202 * @return Curl
203 */
204 function laxSSLMode($enabled = true) {
205 Log::debug(($enabled ? 'Dis' : 'En') . 'abled CURLOPT_SSL_VERIFYPEER');
206 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, !$enabled);
207
208 return $this;
209 }
210
211 /**
212 * Returns a string representation of the object data
213 *
214 * @author Art <a.molcanovas@gmail.com>
215 * @return string
216 */
217 function __toString() {
218 return \debugLite($this);
219 }
220
221 /**
222 * URL encodes the given string
223 *
224 * @author Art <a.molcanovas@gmail.com>
225 *
226 * @param string $str The string
227 *
228 * @return string The escaped string
229 * @link http://php.net/manual/en/function.curl-escape.php
230 */
231 function escape($str) {
232 return curl_escape($this->ch, $str);
233 }
234
235 /**
236 * Pause and unpause a connection
237 *
238 * @author Art <a.molcanovas@gmail.com>
239 *
240 * @param int $bitmask One of CURLPAUSE_* constants.
241 *
242 * @return int An error code (CURLE_OK for no error).
243 * @link http://php.net/manual/en/function.curl-pause.php
244 */
245 function pause($bitmask) {
246 Log::debug('Changed the Curl pause state');
247
248 return curl_pause($this->ch, $bitmask);
249 }
250
251 /**
252 * Gets Curl version information
253 *
254 * @author Art <a.molcanovas@gmail.com>
255 *
256 * @param int $age
257 *
258 * @return array
259 * @link http://php.net/manual/en/function.curl-version.php
260 */
261 function version($age = CURLVERSION_NOW) {
262 return curl_version($age);
263 }
264
265 /**
266 * Checks whether the last transfer was successful
267 *
268 * @author Art <a.molcanovas@gmail.com>
269 * @return boolean|int If successful - true, if not & Curl error code exists
270 * - Curl error code, false otherwise
271 */
272 function wasSuccessful() {
273 if ($this->errno !== CURLE_OK) {
274 return 'Curl error #' . $this->errno;
275 } else {
276 $code = $this->getinfo(CURLINFO_HTTP_CODE);
277 if (!in_array(substr('' . $this->getinfo(CURLINFO_HTTP_CODE), 0, 1), [1, 2, 3])) {
278 return 'HTML response ' . $code;
279 } else {
280 return true;
281 }
282 }
283 }
284
285 /**
286 * Get information regarding a specific transfer
287 *
288 * @author Art <a.molcanovas@gmail.com>
289 *
290 * @param int $opt One of the Curl constants
291 *
292 * @return mixed
293 * @link http://php.net/manual/en/function.curl-getinfo.php
294 */
295 function getinfo($opt = 0) {
296 return curl_getinfo($this->ch, $opt);
297 }
298
299 /**
300 * Reset all options of a libcurl session handle
301 *
302 * @author Art <a.molcanovas@gmail.com>
303 * @return Curl
304 * @link http://php.net/manual/en/function.curl-reset.php
305 */
306 function reset() {
307 Log::debug('Reset Curl');
308 curl_reset($this->ch);
309 curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
310
311 return $this;
312 }
313
314 /**
315 * Decodes the given URL encoded string
316 *
317 * @author Art <a.molcanovas@gmail.com>
318 *
319 * @param string $str
320 *
321 * @return string The decoded string
322 * @link http://php.net/manual/en/function.curl-unescape.php
323 */
324 function unescape($str) {
325 return curl_unescape($this->ch, $str);
326 }
327
328 /**
329 * Auto-cleanup
330 *
331 * @author Art <a.molcanovas@gmail.com>
332 */
333 function __destruct() {
334 $this->close();
335 }
336
337 /**
338 * Closes a Curl connection
339 *
340 * @author Art <a.molcanovas@gmail.com>
341 * @return Curl
342 * @link http://php.net/manual/en/function.curl-close.php
343 */
344 function close() {
345 if ($this->isOpen) {
346 curl_close($this->ch);
347 $this->isOpen = false;
348 Log::debug('Closed Curl connection');
349 }
350
351 return $this;
352 }
353
354 /**
355 * Gets the results of a Curl exec. If $url is set, will exec on that URL
356 *
357 * @author Art <a.molcanovas@gmail.com>
358 *
359 * @param string $url Optional URL override
360 *
361 * @return mixed The results of exec()
362 */
363 function get($url = null) {
364 if ($url !== null) {
365 $this->setURL($url)->exec();
366 }
367
368 return $this->exec;
369 }
370
371 /**
372 * Executes the Curl connection parameters
373 *
374 * @author Art <a.molcanovas@gmail.com>
375 * @return Curl
376 * @link http://php.net/manual/en/function.curl-exec.php
377 */
378 function exec() {
379 Log::debug('Started Curl exec');
380 $this->exec = curl_exec($this->ch);
381 $this->errno = curl_errno($this->ch);
382 $this->error = curl_error($this->ch);
383 Log::debug('Finished curl exec');
384
385 return $this;
386 }
387
388 /**
389 * Returns the error message of the last exec()
390 *
391 * @author Art <a.molcanovas@gmail.com>
392 * @return string
393 * @see self::exec()
394 * @link http://php.net/manual/en/function.curl-error.php
395 */
396 function error() {
397 return $this->error;
398 }
399
400 /**
401 * Returns the error number of the last exec() or 0 if no error occurred
402 *
403 * @author Art <a.molcanovas@gmail.com>
404 * @return int
405 * @see self::exec()
406 * @link http://php.net/manual/en/function.curl-errno.php
407 */
408 function errno() {
409 return $this->errno;
410 }
411
412 /**
413 * Wrapper for setopt()
414 *
415 * @author Art <a.molcanovas@gmail.com>
416 *
417 * @param string $name Param name
418 * @param mixed $value Param value
419 */
420 function __set($name, $value) {
421 $this->setopt($name, $value);
422 }
423
424 /**
425 * Sets an option
426 *
427 * @author Art <a.molcanovas@gmail.com>
428 *
429 * @param int $name The option - see Curl constants
430 * @param mixed $value The option value
431 *
432 * @return Curl
433 * @link http://php.net/manual/en/function.curl-setopt.php
434 */
435 function setopt($name, $value) {
436 if ($name === CURLOPT_POSTFIELDS) {
437 curl_setopt($this->ch, CURLOPT_POST, true);
438 }
439
440 curl_setopt($this->ch, $name, $value);
441
442 return $this;
443 }
444
445 }
446 }
447