PHP-script based on CURL to get data from schools timetable management software "WebUntis"
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

1061 Zeilen
27 KiB

  1. <?php
  2. namespace Curl;
  3. abstract class CurlCookieConst
  4. {
  5. private static $RFC2616 = array();
  6. private static $RFC6265 = array();
  7. public static function Init() {
  8. self::$RFC2616 = array_fill_keys(array(
  9. // RFC2616: "any CHAR except CTLs or separators".
  10. '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
  11. 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
  12. 'W', 'X', 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
  13. 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~',
  14. ), true);
  15. self::$RFC6265 = array_fill_keys(array(
  16. // RFC6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash".
  17. // %x21
  18. '!',
  19. // %x23-2B
  20. '#', '$', '%', '&', "'", '(', ')', '*', '+',
  21. // %x2D-3A
  22. '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':',
  23. // %x3C-5B
  24. '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  25. 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[',
  26. // %x5D-7E
  27. ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  28. 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
  29. ), true);
  30. }
  31. public static function RFC2616() {
  32. return self::$RFC2616;
  33. }
  34. public static function RFC6265() {
  35. return self::$RFC6265;
  36. }
  37. }
  38. CurlCookieConst::Init();
  39. class Curl
  40. {
  41. const VERSION = '4.8.2';
  42. const DEFAULT_TIMEOUT = 30;
  43. public $curl;
  44. public $id = null;
  45. public $error = false;
  46. public $errorCode = 0;
  47. public $errorMessage = null;
  48. public $curlError = false;
  49. public $curlErrorCode = 0;
  50. public $curlErrorMessage = null;
  51. public $httpError = false;
  52. public $httpStatusCode = 0;
  53. public $httpErrorMessage = null;
  54. public $baseUrl = null;
  55. public $url = null;
  56. public $requestHeaders = null;
  57. public $responseHeaders = null;
  58. public $rawResponseHeaders = '';
  59. public $response = null;
  60. public $rawResponse = null;
  61. public $beforeSendFunction = null;
  62. public $downloadCompleteFunction = null;
  63. private $successFunction = null;
  64. private $errorFunction = null;
  65. private $completeFunction = null;
  66. private $cookies = array();
  67. private $responseCookies = array();
  68. private $headers = array();
  69. private $options = array();
  70. private $jsonDecoder = null;
  71. private $jsonPattern = '/^(?:application|text)\/(?:[a-z]+(?:[\.-][0-9a-z]+){0,}[\+\.]|x-)?json(?:-[a-z]+)?/i';
  72. private $xmlPattern = '~^(?:text/|application/(?:atom\+|rss\+)?)xml~i';
  73. /**
  74. * Construct
  75. *
  76. * @access public
  77. * @param $base_url
  78. * @throws \ErrorException
  79. */
  80. public function __construct($base_url = null)
  81. {
  82. if (!extension_loaded('curl')) {
  83. throw new \ErrorException('cURL library is not loaded');
  84. }
  85. $this->curl = curl_init();
  86. $this->id = 1;
  87. $this->setDefaultUserAgent();
  88. $this->setDefaultJsonDecoder();
  89. $this->setDefaultTimeout();
  90. $this->setOpt(CURLINFO_HEADER_OUT, true);
  91. $this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'headerCallback'));
  92. $this->setOpt(CURLOPT_RETURNTRANSFER, true);
  93. $this->headers = new CaseInsensitiveArray();
  94. $this->setURL($base_url);
  95. }
  96. /**
  97. * Before Send
  98. *
  99. * @access public
  100. * @param $callback
  101. */
  102. public function beforeSend($callback)
  103. {
  104. $this->beforeSendFunction = $callback;
  105. }
  106. /**
  107. * Build Post Data
  108. *
  109. * @access public
  110. * @param $data
  111. *
  112. * @return array|string
  113. */
  114. public function buildPostData($data)
  115. {
  116. if (is_array($data)) {
  117. if (self::is_array_multidim($data)) {
  118. if (isset($this->headers['Content-Type']) &&
  119. preg_match($this->jsonPattern, $this->headers['Content-Type'])) {
  120. $json_str = json_encode($data);
  121. if (!($json_str === false)) {
  122. $data = $json_str;
  123. }
  124. } else {
  125. $data = self::http_build_multi_query($data);
  126. }
  127. } else {
  128. $binary_data = false;
  129. foreach ($data as $key => $value) {
  130. // Fix "Notice: Array to string conversion" when $value in curl_setopt($ch, CURLOPT_POSTFIELDS,
  131. // $value) is an array that contains an empty array.
  132. if (is_array($value) && empty($value)) {
  133. $data[$key] = '';
  134. // Fix "curl_setopt(): The usage of the @filename API for file uploading is deprecated. Please use
  135. // the CURLFile class instead". Ignore non-file values prefixed with the @ character.
  136. } elseif (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) {
  137. $binary_data = true;
  138. if (class_exists('CURLFile')) {
  139. $data[$key] = new \CURLFile(substr($value, 1));
  140. }
  141. } elseif ($value instanceof \CURLFile) {
  142. $binary_data = true;
  143. }
  144. }
  145. if (!$binary_data) {
  146. if (isset($this->headers['Content-Type']) &&
  147. preg_match($this->jsonPattern, $this->headers['Content-Type'])) {
  148. $json_str = json_encode($data);
  149. if (!($json_str === false)) {
  150. $data = $json_str;
  151. }
  152. } else {
  153. $data = http_build_query($data, '', '&');
  154. }
  155. }
  156. }
  157. }
  158. return $data;
  159. }
  160. /**
  161. * Call
  162. *
  163. * @access public
  164. */
  165. public function call()
  166. {
  167. $args = func_get_args();
  168. $function = array_shift($args);
  169. if (is_callable($function)) {
  170. array_unshift($args, $this);
  171. call_user_func_array($function, $args);
  172. }
  173. }
  174. /**
  175. * Close
  176. *
  177. * @access public
  178. */
  179. public function close()
  180. {
  181. if (is_resource($this->curl)) {
  182. curl_close($this->curl);
  183. }
  184. $this->options = null;
  185. $this->jsonDecoder = null;
  186. }
  187. /**
  188. * Complete
  189. *
  190. * @access public
  191. * @param $callback
  192. */
  193. public function complete($callback)
  194. {
  195. $this->completeFunction = $callback;
  196. }
  197. /**
  198. * Progress
  199. *
  200. * @access public
  201. * @param $callback
  202. */
  203. public function progress($callback)
  204. {
  205. $this->setOpt(CURLOPT_PROGRESSFUNCTION, $callback);
  206. $this->setOpt(CURLOPT_NOPROGRESS, false);
  207. }
  208. /**
  209. * Delete
  210. *
  211. * @access public
  212. * @param $url
  213. * @param $query_parameters
  214. * @param $data
  215. *
  216. * @return string
  217. */
  218. public function delete($url, $query_parameters = array(), $data = array())
  219. {
  220. if (is_array($url)) {
  221. $data = $query_parameters;
  222. $query_parameters = $url;
  223. $url = $this->baseUrl;
  224. }
  225. $this->setURL($url, $query_parameters);
  226. $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
  227. $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));
  228. return $this->exec();
  229. }
  230. /**
  231. * Download Complete
  232. *
  233. * @access public
  234. * @param $fh
  235. */
  236. public function downloadComplete($fh)
  237. {
  238. if (!$this->error && $this->downloadCompleteFunction) {
  239. rewind($fh);
  240. $this->call($this->downloadCompleteFunction, $fh);
  241. $this->downloadCompleteFunction = null;
  242. }
  243. if (is_resource($fh)) {
  244. fclose($fh);
  245. }
  246. // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the
  247. // PHP script from stdin. Using null causes "Warning: curl_setopt():
  248. // supplied argument is not a valid File-Handle resource".
  249. if (!defined('STDOUT')) {
  250. define('STDOUT', fopen('php://stdout', 'w'));
  251. }
  252. // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE
  253. // resource has gone away, resetting to default".
  254. $this->setOpt(CURLOPT_FILE, STDOUT);
  255. // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent
  256. // responses as the return value of curl_exec(). Without this,
  257. // curl_exec() will revert to returning boolean values.
  258. $this->setOpt(CURLOPT_RETURNTRANSFER, true);
  259. }
  260. /**
  261. * Download
  262. *
  263. * @access public
  264. * @param $url
  265. * @param $mixed_filename
  266. *
  267. * @return boolean
  268. */
  269. public function download($url, $mixed_filename)
  270. {
  271. if (is_callable($mixed_filename)) {
  272. $this->downloadCompleteFunction = $mixed_filename;
  273. $fh = tmpfile();
  274. } else {
  275. $filename = $mixed_filename;
  276. $fh = fopen($filename, 'wb');
  277. }
  278. $this->setOpt(CURLOPT_FILE, $fh);
  279. $this->get($url);
  280. $this->downloadComplete($fh);
  281. return ! $this->error;
  282. }
  283. /**
  284. * Error
  285. *
  286. * @access public
  287. * @param $callback
  288. */
  289. public function error($callback)
  290. {
  291. $this->errorFunction = $callback;
  292. }
  293. /**
  294. * Exec
  295. *
  296. * @access public
  297. * @param $ch
  298. *
  299. * @return string
  300. */
  301. public function exec($ch = null)
  302. {
  303. $this->responseCookies = array();
  304. if (!($ch === null)) {
  305. $this->rawResponse = curl_multi_getcontent($ch);
  306. } else {
  307. $this->call($this->beforeSendFunction);
  308. $this->rawResponse = curl_exec($this->curl);
  309. $this->curlErrorCode = curl_errno($this->curl);
  310. }
  311. $this->curlErrorMessage = curl_error($this->curl);
  312. $this->curlError = !($this->curlErrorCode === 0);
  313. $this->httpStatusCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
  314. $this->httpError = in_array(floor($this->httpStatusCode / 100), array(4, 5));
  315. $this->error = $this->curlError || $this->httpError;
  316. $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0;
  317. // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders
  318. // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);).
  319. if ($this->getOpt(CURLINFO_HEADER_OUT) === true) {
  320. $this->requestHeaders = $this->parseRequestHeaders(curl_getinfo($this->curl, CURLINFO_HEADER_OUT));
  321. }
  322. $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders);
  323. list($this->response, $this->rawResponse) = $this->parseResponse($this->responseHeaders, $this->rawResponse);
  324. $this->httpErrorMessage = '';
  325. if ($this->error) {
  326. if (isset($this->responseHeaders['Status-Line'])) {
  327. $this->httpErrorMessage = $this->responseHeaders['Status-Line'];
  328. }
  329. }
  330. $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage;
  331. if (!$this->error) {
  332. $this->call($this->successFunction);
  333. } else {
  334. $this->call($this->errorFunction);
  335. }
  336. $this->call($this->completeFunction);
  337. return $this->response;
  338. }
  339. /**
  340. * Get
  341. *
  342. * @access public
  343. * @param $url
  344. * @param $data
  345. *
  346. * @return string
  347. */
  348. public function get($url, $data = array())
  349. {
  350. if (is_array($url)) {
  351. $data = $url;
  352. $url = $this->baseUrl;
  353. }
  354. $this->setURL($url, $data);
  355. $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET');
  356. $this->setOpt(CURLOPT_HTTPGET, true);
  357. return $this->exec();
  358. }
  359. /**
  360. * Get Opt
  361. *
  362. * @access public
  363. * @param $option
  364. *
  365. * @return mixed
  366. */
  367. public function getOpt($option)
  368. {
  369. return $this->options[$option];
  370. }
  371. /**
  372. * Head
  373. *
  374. * @access public
  375. * @param $url
  376. * @param $data
  377. *
  378. * @return string
  379. */
  380. public function head($url, $data = array())
  381. {
  382. if (is_array($url)) {
  383. $data = $url;
  384. $url = $this->baseUrl;
  385. }
  386. $this->setURL($url, $data);
  387. $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD');
  388. $this->setOpt(CURLOPT_NOBODY, true);
  389. return $this->exec();
  390. }
  391. /**
  392. * Header Callback
  393. *
  394. * @access public
  395. * @param $ch
  396. * @param $header
  397. *
  398. * @return integer
  399. */
  400. public function headerCallback($ch, $header)
  401. {
  402. if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) == 1) {
  403. $this->responseCookies[$cookie[1]] = $cookie[2];
  404. }
  405. $this->rawResponseHeaders .= $header;
  406. return strlen($header);
  407. }
  408. /**
  409. * Options
  410. *
  411. * @access public
  412. * @param $url
  413. * @param $data
  414. *
  415. * @return string
  416. */
  417. public function options($url, $data = array())
  418. {
  419. if (is_array($url)) {
  420. $data = $url;
  421. $url = $this->baseUrl;
  422. }
  423. $this->setURL($url, $data);
  424. $this->unsetHeader('Content-Length');
  425. $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS');
  426. return $this->exec();
  427. }
  428. /**
  429. * Patch
  430. *
  431. * @access public
  432. * @param $url
  433. * @param $data
  434. *
  435. * @return string
  436. */
  437. public function patch($url, $data = array())
  438. {
  439. if (is_array($url)) {
  440. $data = $url;
  441. $url = $this->baseUrl;
  442. }
  443. if (is_array($data) && empty($data)) {
  444. $this->unsetHeader('Content-Length');
  445. }
  446. $this->setURL($url);
  447. $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
  448. $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));
  449. return $this->exec();
  450. }
  451. /**
  452. * Post
  453. *
  454. * @access public
  455. * @param $url
  456. * @param $data
  457. *
  458. * @return string
  459. */
  460. public function post($url, $data = array())
  461. {
  462. if (is_array($url)) {
  463. $data = $url;
  464. $url = $this->baseUrl;
  465. }
  466. $this->setURL($url);
  467. $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');
  468. $this->setOpt(CURLOPT_POST, true);
  469. $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data));
  470. return $this->exec();
  471. }
  472. /**
  473. * Put
  474. *
  475. * @access public
  476. * @param $url
  477. * @param $data
  478. *
  479. * @return string
  480. */
  481. public function put($url, $data = array())
  482. {
  483. if (is_array($url)) {
  484. $data = $url;
  485. $url = $this->baseUrl;
  486. }
  487. $this->setURL($url);
  488. $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
  489. $put_data = $this->buildPostData($data);
  490. if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) {
  491. $this->setHeader('Content-Length', strlen($put_data));
  492. }
  493. $this->setOpt(CURLOPT_POSTFIELDS, $put_data);
  494. return $this->exec();
  495. }
  496. /**
  497. * Set Basic Authentication
  498. *
  499. * @access public
  500. * @param $username
  501. * @param $password
  502. */
  503. public function setBasicAuthentication($username, $password = '')
  504. {
  505. $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  506. $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
  507. }
  508. /**
  509. * Set Digest Authentication
  510. *
  511. * @access public
  512. * @param $username
  513. * @param $password
  514. */
  515. public function setDigestAuthentication($username, $password = '')
  516. {
  517. $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
  518. $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
  519. }
  520. /**
  521. * Set Cookie
  522. *
  523. * @access public
  524. * @param $key
  525. * @param $value
  526. */
  527. public function setCookie($key, $value)
  528. {
  529. $name_chars = array();
  530. foreach (str_split($key) as $name_char) {
  531. if (!array_key_exists($name_char, CurlCookieConst::RFC2616())) {
  532. $name_chars[] = rawurlencode($name_char);
  533. } else {
  534. $name_chars[] = $name_char;
  535. }
  536. }
  537. $value_chars = array();
  538. foreach (str_split($value) as $value_char) {
  539. if (!array_key_exists($value_char, CurlCookieConst::RFC6265())) {
  540. $value_chars[] = rawurlencode($value_char);
  541. } else {
  542. $value_chars[] = $value_char;
  543. }
  544. }
  545. $this->cookies[implode('', $name_chars)] = implode('', $value_chars);
  546. $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function($k, $v) {
  547. return $k . '=' . $v;
  548. }, array_keys($this->cookies), array_values($this->cookies))));
  549. }
  550. /**
  551. * Get cookie.
  552. *
  553. * @access public
  554. * @param $key
  555. */
  556. public function getCookie($key)
  557. {
  558. return $this->getResponseCookie($key);
  559. }
  560. /**
  561. * Get response cookie.
  562. *
  563. * @access public
  564. * @param $key
  565. */
  566. public function getResponseCookie($key)
  567. {
  568. return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null;
  569. }
  570. /**
  571. * Set Port
  572. *
  573. * @access public
  574. * @param $port
  575. */
  576. public function setPort($port)
  577. {
  578. $this->setOpt(CURLOPT_PORT, intval($port));
  579. }
  580. /**
  581. * Set Connect Timeout
  582. *
  583. * @access public
  584. * @param $seconds
  585. */
  586. public function setConnectTimeout($seconds)
  587. {
  588. $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds);
  589. }
  590. /**
  591. * Set Cookie File
  592. *
  593. * @access public
  594. * @param $cookie_file
  595. */
  596. public function setCookieFile($cookie_file)
  597. {
  598. $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file);
  599. }
  600. /**
  601. * Set Cookie Jar
  602. *
  603. * @access public
  604. * @param $cookie_jar
  605. */
  606. public function setCookieJar($cookie_jar)
  607. {
  608. $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar);
  609. }
  610. /**
  611. * Set Default JSON Decoder
  612. *
  613. * @access public
  614. */
  615. public function setDefaultJsonDecoder()
  616. {
  617. $this->jsonDecoder = function($response) {
  618. $json_obj = json_decode($response, false);
  619. if (!($json_obj === null)) {
  620. $response = $json_obj;
  621. }
  622. return $response;
  623. };
  624. }
  625. /**
  626. * Set Default Timeout
  627. *
  628. * @access public
  629. */
  630. public function setDefaultTimeout()
  631. {
  632. $this->setTimeout(self::DEFAULT_TIMEOUT);
  633. }
  634. /**
  635. * Set Default User Agent
  636. *
  637. * @access public
  638. */
  639. public function setDefaultUserAgent()
  640. {
  641. $user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)';
  642. $user_agent .= ' PHP/' . PHP_VERSION;
  643. $curl_version = curl_version();
  644. $user_agent .= ' curl/' . $curl_version['version'];
  645. $this->setUserAgent($user_agent);
  646. }
  647. /**
  648. * Set Header
  649. *
  650. * @access public
  651. * @param $key
  652. * @param $value
  653. *
  654. * @return string
  655. */
  656. public function setHeader($key, $value)
  657. {
  658. $this->headers[$key] = $value;
  659. $headers = array();
  660. foreach ($this->headers as $key => $value) {
  661. $headers[] = $key . ': ' . $value;
  662. }
  663. $this->setOpt(CURLOPT_HTTPHEADER, $headers);
  664. }
  665. /**
  666. * Set JSON Decoder
  667. *
  668. * @access public
  669. * @param $function
  670. */
  671. public function setJsonDecoder($function)
  672. {
  673. if (is_callable($function)) {
  674. $this->jsonDecoder = $function;
  675. }
  676. }
  677. /**
  678. * Set Opt
  679. *
  680. * @access public
  681. * @param $option
  682. * @param $value
  683. *
  684. * @return boolean
  685. */
  686. public function setOpt($option, $value)
  687. {
  688. $required_options = array(
  689. CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER',
  690. );
  691. if (in_array($option, array_keys($required_options), true) && !($value === true)) {
  692. trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING);
  693. }
  694. $this->options[$option] = $value;
  695. return curl_setopt($this->curl, $option, $value);
  696. }
  697. /**
  698. * Set Referer
  699. *
  700. * @access public
  701. * @param $referer
  702. */
  703. public function setReferer($referer)
  704. {
  705. $this->setReferrer($referer);
  706. }
  707. /**
  708. * Set Referrer
  709. *
  710. * @access public
  711. * @param $referrer
  712. */
  713. public function setReferrer($referrer)
  714. {
  715. $this->setOpt(CURLOPT_REFERER, $referrer);
  716. }
  717. /**
  718. * Set Timeout
  719. *
  720. * @access public
  721. * @param $seconds
  722. */
  723. public function setTimeout($seconds)
  724. {
  725. $this->setOpt(CURLOPT_TIMEOUT, $seconds);
  726. }
  727. /**
  728. * Set Url
  729. *
  730. * @access public
  731. * @param $url
  732. * @param $data
  733. */
  734. public function setURL($url, $data = array())
  735. {
  736. $this->baseUrl = $url;
  737. $this->url = $this->buildURL($url, $data);
  738. $this->setOpt(CURLOPT_URL, $this->url);
  739. }
  740. /**
  741. * Set User Agent
  742. *
  743. * @access public
  744. * @param $user_agent
  745. */
  746. public function setUserAgent($user_agent)
  747. {
  748. $this->setOpt(CURLOPT_USERAGENT, $user_agent);
  749. }
  750. /**
  751. * Success
  752. *
  753. * @access public
  754. * @param $callback
  755. */
  756. public function success($callback)
  757. {
  758. $this->successFunction = $callback;
  759. }
  760. /**
  761. * Unset Header
  762. *
  763. * @access public
  764. * @param $key
  765. */
  766. public function unsetHeader($key)
  767. {
  768. $this->setHeader($key, '');
  769. unset($this->headers[$key]);
  770. }
  771. /**
  772. * Verbose
  773. *
  774. * @access public
  775. * @param $on
  776. */
  777. public function verbose($on = true, $output=STDERR)
  778. {
  779. // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side
  780. // effect of causing Curl::requestHeaders to be empty.
  781. if ($on) {
  782. $this->setOpt(CURLINFO_HEADER_OUT, false);
  783. }
  784. $this->setOpt(CURLOPT_VERBOSE, $on);
  785. $this->setOpt(CURLOPT_STDERR, $output);
  786. }
  787. /**
  788. * Destruct
  789. *
  790. * @access public
  791. */
  792. public function __destruct()
  793. {
  794. $this->close();
  795. }
  796. /**
  797. * Build Url
  798. *
  799. * @access private
  800. * @param $url
  801. * @param $data
  802. *
  803. * @return string
  804. */
  805. private function buildURL($url, $data = array())
  806. {
  807. return $url . (empty($data) ? '' : '?' . http_build_query($data));
  808. }
  809. /**
  810. * Parse Headers
  811. *
  812. * @access private
  813. * @param $raw_headers
  814. *
  815. * @return array
  816. */
  817. private function parseHeaders($raw_headers)
  818. {
  819. $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY);
  820. $http_headers = new CaseInsensitiveArray();
  821. $raw_headers_count = count($raw_headers);
  822. for ($i = 1; $i < $raw_headers_count; $i++) {
  823. list($key, $value) = explode(':', $raw_headers[$i], 2);
  824. $key = trim($key);
  825. $value = trim($value);
  826. // Use isset() as array_key_exists() and ArrayAccess are not compatible.
  827. if (isset($http_headers[$key])) {
  828. $http_headers[$key] .= ',' . $value;
  829. } else {
  830. $http_headers[$key] = $value;
  831. }
  832. }
  833. return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers);
  834. }
  835. /**
  836. * Parse Request Headers
  837. *
  838. * @access private
  839. * @param $raw_headers
  840. *
  841. * @return array
  842. */
  843. private function parseRequestHeaders($raw_headers)
  844. {
  845. $request_headers = new CaseInsensitiveArray();
  846. list($first_line, $headers) = $this->parseHeaders($raw_headers);
  847. $request_headers['Request-Line'] = $first_line;
  848. foreach ($headers as $key => $value) {
  849. $request_headers[$key] = $value;
  850. }
  851. return $request_headers;
  852. }
  853. /**
  854. * Parse Response
  855. *
  856. * @access private
  857. * @param $response_headers
  858. * @param $raw_response
  859. *
  860. * @return array
  861. */
  862. private function parseResponse($response_headers, $raw_response)
  863. {
  864. $response = $raw_response;
  865. if (isset($response_headers['Content-Type'])) {
  866. if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) {
  867. $json_decoder = $this->jsonDecoder;
  868. if (is_callable($json_decoder)) {
  869. $response = $json_decoder($response);
  870. }
  871. } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) {
  872. $xml_obj = @simplexml_load_string($response);
  873. if (!($xml_obj === false)) {
  874. $response = $xml_obj;
  875. }
  876. }
  877. }
  878. return array($response, $raw_response);
  879. }
  880. /**
  881. * Parse Response Headers
  882. *
  883. * @access private
  884. * @param $raw_response_headers
  885. *
  886. * @return array
  887. */
  888. private function parseResponseHeaders($raw_response_headers)
  889. {
  890. $response_header_array = explode("\r\n\r\n", $raw_response_headers);
  891. $response_header = '';
  892. for ($i = count($response_header_array) - 1; $i >= 0; $i--) {
  893. if (stripos($response_header_array[$i], 'HTTP/') === 0) {
  894. $response_header = $response_header_array[$i];
  895. break;
  896. }
  897. }
  898. $response_headers = new CaseInsensitiveArray();
  899. list($first_line, $headers) = $this->parseHeaders($response_header);
  900. $response_headers['Status-Line'] = $first_line;
  901. foreach ($headers as $key => $value) {
  902. $response_headers[$key] = $value;
  903. }
  904. return $response_headers;
  905. }
  906. /**
  907. * Http Build Multi Query
  908. *
  909. * @access public
  910. * @param $data
  911. * @param $key
  912. *
  913. * @return string
  914. */
  915. public static function http_build_multi_query($data, $key = null)
  916. {
  917. $query = array();
  918. if (empty($data)) {
  919. return $key . '=';
  920. }
  921. $is_array_assoc = self::is_array_assoc($data);
  922. foreach ($data as $k => $value) {
  923. if (is_string($value) || is_numeric($value)) {
  924. $brackets = $is_array_assoc ? '[' . $k . ']' : '[]';
  925. $query[] = urlencode($key === null ? $k : $key . $brackets) . '=' . rawurlencode($value);
  926. } elseif (is_array($value)) {
  927. $nested = $key === null ? $k : $key . '[' . $k . ']';
  928. $query[] = self::http_build_multi_query($value, $nested);
  929. }
  930. }
  931. return implode('&', $query);
  932. }
  933. /**
  934. * Is Array Assoc
  935. *
  936. * @access public
  937. * @param $array
  938. *
  939. * @return boolean
  940. */
  941. public static function is_array_assoc($array)
  942. {
  943. return (bool)count(array_filter(array_keys($array), 'is_string'));
  944. }
  945. /**
  946. * Is Array Multidim
  947. *
  948. * @access public
  949. * @param $array
  950. *
  951. * @return boolean
  952. */
  953. public static function is_array_multidim($array)
  954. {
  955. if (!is_array($array)) {
  956. return false;
  957. }
  958. return (bool)count(array_filter($array, 'is_array'));
  959. }
  960. }
  961. ?>