PageRenderTime 516ms CodeModel.GetById 41ms RepoModel.GetById 7ms app.codeStats 0ms

/modules/curl/init.php

https://bitbucket.org/seyar/parshin.local
PHP | 580 lines | 215 code | 71 blank | 294 comment | 30 complexity | accf8386d7ded8575cd35760890041d1 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * The CURL library provides an object oriented interface
  4. * to the procedural CURL PHP functions, the class only
  5. * allows for a single CURL session per instance - as curl_init
  6. * is called from the constructor...
  7. *
  8. * $Id: Curl.php 16 2009-05-26 17:37:46Z samsoir $
  9. *
  10. * @package Standard
  11. * @subpackage Libraries
  12. * @category cURL Abstraction
  13. * @author Parnell Springmeyer <parnell@rastermedia.com>
  14. * @author Sam Clark <sam@clark.name>
  15. * @modified Yuriy Klinkov <klinkov@gmail.com>
  16. * example
  17. $curl_config = get_object_vars(Kohana_Config::instance()->load('curl'));
  18. $curl = new Curl_Core( $curl_config );
  19. $weather_xml = $curl->get("http://xml.weather.co.ua/1.2/forecast/24?dayf=1");
  20. if ( !$xml = simplexml_load_string( $weather_xml ) )
  21. {
  22. echo 'Widget get XML Error!';
  23. return;
  24. }
  25. echo Kohana::debug( (string)$xml->city->name );
  26. echo Kohana::debug( (string)$xml->city->country->name );
  27. echo Kohana::debug( $xml->current );
  28. */
  29. class Curl_Core
  30. {
  31. /**
  32. * Configuration for this class
  33. *
  34. * @var array
  35. * @access protected
  36. */
  37. protected $config;
  38. /**
  39. * Specific options for Curl
  40. *
  41. * @var array
  42. * @access protected
  43. */
  44. protected $options;
  45. /**
  46. * Cache library for caching requests
  47. *
  48. * @var Cache
  49. * @access protected
  50. */
  51. protected $cache;
  52. /**
  53. * Curl connectection
  54. *
  55. * @var curl
  56. * @access protected
  57. */
  58. protected $connection;
  59. /**
  60. * Execution status for this instantiation
  61. *
  62. * @var boolean
  63. * @access protected
  64. */
  65. protected $executed;
  66. /**
  67. * Cache status for this request
  68. *
  69. * @var boolean
  70. * @access protected
  71. */
  72. protected $cached_result;
  73. /**
  74. * Headers returned from the request
  75. *
  76. * @var array
  77. * @access protected
  78. */
  79. protected $headers = array();
  80. /**
  81. * Result of the cURL request
  82. *
  83. * @var mixed
  84. * @access protected
  85. */
  86. protected $result;
  87. /**
  88. * Error resulting from the cURL request
  89. *
  90. * @var array
  91. * @access protected
  92. */
  93. protected $error;
  94. /**
  95. * Information resulting from the request
  96. *
  97. * @var array
  98. * @access protected
  99. */
  100. protected $info = array();
  101. /**
  102. * Factory pattern for chaining
  103. *
  104. * @param array $config
  105. * @param string $url
  106. * @return Curl
  107. * @access public
  108. * @static
  109. */
  110. public static function factory(array $config = array(), $url = NULL)
  111. {
  112. return new Curl_Core($config, $url);
  113. }
  114. /**
  115. * Returns the curl version installed
  116. *
  117. * @param string key of curl version information to isolate and return [Optional]
  118. * @return array
  119. * @return mixed
  120. * @throws Kohana_User_Exception
  121. * @access public
  122. * @static
  123. */
  124. public static function version($key = NULL)
  125. {
  126. if ( ! function_exists('curl_version'))
  127. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Curl version information could not be ascertained. '.$e->getMessage());
  128. // get the curl version information
  129. $result = curl_version();
  130. // Return the specified result
  131. if ($key === NULL)
  132. return $result;
  133. else
  134. return $result[$key];
  135. }
  136. /**
  137. * Constructor for the Curl class. Instantiates the Curl library
  138. *
  139. * @param array config the configuration array
  140. * @param string url the url to use for this Curl request
  141. * @return void
  142. * @throws Kohana_User_Exception
  143. * @access public
  144. */
  145. public function __construct(array $config = array(), $url = NULL)
  146. {
  147. // Merge supplied config with the default settings
  148. $config = Kohana::config('curl');
  149. // Assign config to this class
  150. $this->config = $config;
  151. if ( ! function_exists('curl_init'))
  152. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Curl failed to initialise. '.$e->getMessage());
  153. // Init curl
  154. $this->connection = curl_init();
  155. // Assign options by relation
  156. $this->options = & $this->config['options'];
  157. // If there is a URL supplied, add it to the Curl options
  158. if ($url !== NULL)
  159. $this->options[CURLOPT_URL] = $url;
  160. // Set this executed to FALSE;
  161. $this->executed = FALSE;
  162. // Load Cache library instance if required
  163. // if ($this->config['cache'])
  164. // $this->cache = Cache::instance();
  165. }
  166. /**
  167. * __get() method for returning config options, curl exec information or version information
  168. *
  169. * @param string $key
  170. * @return void
  171. * @author Sam Clark
  172. */
  173. public function __get($key)
  174. {
  175. if (in_array($key, array('executed', 'cached_result')))
  176. return $this->$key;
  177. return;
  178. }
  179. /**
  180. * Quickly gets data from a URI. This only works with GET requests but
  181. * can handle HTTP Basic Auth
  182. *
  183. * @param string uri the url to pull from
  184. * @param string username the username for the service [Optional]
  185. * @param string password the password for the user [Optional]
  186. * @return string
  187. * @return void
  188. * @throws Kohana_User_Exception
  189. * @access public
  190. * @static
  191. **/
  192. static public function get($url, $username = FALSE, $password = FALSE)
  193. {
  194. if ( ! Validate::url($url))
  195. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'The URL : '.$url.' is not a valid resource');
  196. // Initiate a curl session based on the URL supplied
  197. $curl = Curl_Core::factory(array(CURLOPT_POST => FALSE), $url);
  198. // If a username/password is supplied
  199. if ($username AND $password)
  200. {
  201. // Add the HTTP Basic Auth headers
  202. $curl->setopt_array(array(CURLOPT_USERPWD => $username.':'.$password));
  203. }
  204. // Run the curl request
  205. $curl->exec();
  206. // If there was an error, return null
  207. if ($curl->error)
  208. return false;
  209. else
  210. return $curl->result;
  211. }
  212. /**
  213. * Sets an array of options, must use curl_setopt consts
  214. *
  215. * @chainable
  216. * @param array options the array of key/value pairs
  217. * @return self
  218. * @access public
  219. * @throws Kohana_User_Exception
  220. */
  221. public function setopt_array(array $options)
  222. {
  223. if ( ! $this->executed)
  224. {
  225. $this->options = $options + $this->options;
  226. return $this;
  227. }
  228. else
  229. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Cannot set Curl options after it has already executed');
  230. }
  231. /**
  232. * Set a single option, must use curl_setopt consts
  233. *
  234. * @chainable
  235. * @param const option the option to set
  236. * @param string value the value to set to the option
  237. * @return self
  238. * @access public
  239. */
  240. public function setopt($option, $value)
  241. {
  242. if ( ! $this->executed)
  243. {
  244. $this->options[$option] = $value;
  245. return $this;
  246. }
  247. else
  248. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Cannot set Curl options after it has already executed');
  249. }
  250. /**
  251. * Alias for self::setopt(). Retained for backwards compatibility
  252. *
  253. * @chainable
  254. * @return self
  255. * @depreciated
  256. */
  257. public function addOption($option, $value)
  258. {
  259. return $this->setopt($option, $value);
  260. }
  261. /**
  262. * Execute the CURL request based using class setup
  263. *
  264. * @chainable
  265. * @param array ignore_errors array of curl error numbers to ignore
  266. * @return self
  267. * @access public
  268. * @throws Kohana_User_Exception
  269. */
  270. public function exec($ignore_errors = array())
  271. {
  272. // If this has already executed, return itself
  273. if ($this->executed)
  274. return $this;
  275. // Set the header to be processed by the parser and turn off header
  276. curl_setopt($this->connection, CURLOPT_HEADERFUNCTION, array($this, 'parse_header'));
  277. curl_setopt($this->connection, CURLOPT_HEADER, FALSE);
  278. //Some servers (like Lighttpd) will not process the curl request without this header and will return error code 417 instead.
  279. //Apache does not need it, but it is safe to use it there as well.
  280. curl_setopt($this->connection, CURLOPT_HTTPHEADER, array("Expect:"));
  281. // If the cache configuration is set, try and load the cache
  282. if ($this->config['cache'])
  283. $cached = $this->load_cache();
  284. // If this has not executed, prepare for execution
  285. if ( ! $this->executed)
  286. {
  287. // Throw an exception if the user defined options cannot be applied
  288. if ( ! curl_setopt_array($this->connection, $this->options))
  289. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'There was a problem setting the curl_setopt_array');
  290. // Execute the Curl request
  291. $this->result = curl_exec($this->connection);
  292. // If the curl error number was not zero
  293. if (($error_number = curl_errno($this->connection)) > 0)
  294. {
  295. // Merge the ignored errors values
  296. $this->config['ignored_errors'] += $ignored_errors;
  297. // Assign the error information to this model
  298. $this->error = array($error_number => curl_error($this->connection));
  299. // If the error number is not in the ignored errors array, throw an exception
  300. if ( ! in_array($error_number, $this->config['ignore_errors']))
  301. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Curl error : ' . $error_number . ' ' . $this->error[$error_number]);
  302. }
  303. else
  304. $this->error = array();
  305. // Get the information from this connection
  306. $this->info = curl_getinfo($this->connection);
  307. // Set executed to TRUE and cached to false
  308. $this->executed = TRUE;
  309. $this->cached_result = FALSE;
  310. // If this should be cached, cache it
  311. if ($this->config['cache'] AND ($this->cache instanceof Cache))
  312. $this->cache();
  313. }
  314. // Return
  315. return $this;
  316. }
  317. /**
  318. * Execute the current CURL session with
  319. * the provided options. Retained for backward compatibility
  320. *
  321. * @chainable
  322. * @param array ignore_error_numbers (ex: 26, 28, etc...)
  323. * @return mixed
  324. * @access public
  325. * @depreciated
  326. */
  327. public function execute($ignore_error_numbers = array())
  328. {
  329. return $this->exec($ignore_error_numbers)->result();
  330. }
  331. /**
  332. * Return a CURL status code, by default
  333. * the HTTPCODE is set.
  334. * Retained for backwards compatibility, you should use self::info(key) in future implementations
  335. *
  336. * @param cURL const code
  337. * @return mixed
  338. * @access public
  339. * @depreciated
  340. */
  341. public function status($code = CURLINFO_HTTP_CODE)
  342. {
  343. return curl_getinfo($this->connection, $code);
  344. }
  345. /**
  346. * Returns the stored result of the cURL operation
  347. *
  348. * @return mixed
  349. * @return void
  350. * @access public
  351. */
  352. public function result()
  353. {
  354. if ($this->executed)
  355. return $this->result;
  356. else
  357. return NULL;
  358. }
  359. /**
  360. * Provides access to the processed info array
  361. *
  362. * @param string key
  363. * @return mixed
  364. * @return void
  365. * @access public
  366. */
  367. public function info($key = NULL)
  368. {
  369. if ($this->executed)
  370. {
  371. if ($key === NULL)
  372. return $this->info;
  373. elseif (array_key_exists($key, $this->info))
  374. return $this->info[$key];
  375. }
  376. return NULL;
  377. }
  378. /**
  379. * Provides access to the processed header array
  380. *
  381. * @param string key
  382. * @return string
  383. * @return void
  384. * @author Sam Clark
  385. */
  386. public function header($key = NULL)
  387. {
  388. if ($this->executed)
  389. {
  390. if ($key === NULL)
  391. return $this->header;
  392. elseif (array_key_exists($key, $this->header))
  393. return $this->header[$key];
  394. }
  395. return NULL;
  396. }
  397. /**
  398. * Clears the cache for Curl library
  399. *
  400. * @param bool all if TRUE will delete all Curl library caches
  401. * @return Curl
  402. * @access public
  403. * @throws Kohana_User_Exception
  404. */
  405. public function clear_cache($all = FALSE)
  406. {
  407. if ($this->config['cache'] === FALSE OR ! ($this->cache instanceof Cache))
  408. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Cache not enabled for this instance. Please check your settings.');
  409. if ($all)
  410. $this->cache->delete_tag($this->config['cache_tags']);
  411. else
  412. $this->cache->delete($this->create_cache_key());
  413. return $this;
  414. }
  415. /**
  416. * Try to load a cached version of this request
  417. *
  418. * @return bool
  419. * @access public
  420. * @throws Kohana_User_Exception
  421. */
  422. protected function load_cache()
  423. {
  424. if ($this->config['cache'] === FALSE OR ! ($this->cache instanceof Cache))
  425. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Cache not enabled for this instance. Please check your settings.');
  426. $result = $this->cache->get($this->create_cache_key());
  427. if ($result === NULL)
  428. return FALSE;
  429. $this->info = $result['info'];
  430. $this->result = $result['result'];
  431. $this->headers = $result['headers'];
  432. $this->executed = TRUE;
  433. $this->cached_result = TRUE;
  434. return TRUE;
  435. }
  436. /**
  437. * Caches the current result set, or loads a saved cache if key is supplied
  438. *
  439. * @return boolean
  440. * @access protected
  441. * @throws Kohana_User_Exception
  442. */
  443. protected function cache()
  444. {
  445. if ( ! $this->executed OR $this->result === NULL)
  446. return;
  447. if ($this->config['cache'] === FALSE OR ! ($this->cache instanceof Cache))
  448. throw new Kohana_User_Exception(__CLASS__.'.'.__METHOD__.'()', 'Cache not enabled for this instance. Please check your settings.');
  449. // Store the correct data
  450. $cache_data = array
  451. (
  452. 'result' => $this->result,
  453. 'headers' => $this->headers,
  454. 'info' => $this->info,
  455. );
  456. return $this->cache->set($this->create_cache_key(), $cache_data, $this->config['cache_tags'], $this->config['cache']);
  457. }
  458. /**
  459. * Creates a hash key for caching indentification
  460. *
  461. * @return string
  462. * @access protected
  463. */
  464. protected function create_cache_key()
  465. {
  466. return hash($this->config['hash'], serialize($this->options));
  467. }
  468. /**
  469. * Puts the header response into a stored variable
  470. *
  471. * @param Curl ch
  472. * @param string header
  473. * @return void
  474. */
  475. protected function parse_header($ch, $header)
  476. {
  477. $result = array();
  478. if (preg_match_all('/(\w[^\s:]*):[ ]*([^\r\n]*(?:\r\n[ \t][^\r\n]*)*)/', $header, $matches))
  479. {
  480. foreach ($matches[0] as $key => $value)
  481. $result[$matches[1][$key]] = $matches[2][$key];
  482. }
  483. if ($result)
  484. $this->headers += $result;
  485. return strlen($header);
  486. }
  487. /**
  488. * Be sure to destroy our CURL session. Retained for backwards compatibility
  489. *
  490. * @return void
  491. * @access public
  492. */
  493. public function __destroy()
  494. {
  495. $this->__destruct();
  496. }
  497. /**
  498. * Destroy the curl connection gracefully
  499. *
  500. * @return void
  501. * @access public
  502. */
  503. public function __destruct()
  504. {
  505. curl_close($this->connection);
  506. }
  507. }