PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/OOCurl.php

https://github.com/jessedc/oocurl
PHP | 582 lines | 168 code | 50 blank | 364 comment | 15 complexity | e8dcf19cd4517b0e11758bdb1d3c5c81 MD5 | raw file
  1. <?php
  2. /**
  3. * OOCurl
  4. *
  5. * Provides an Object-Oriented interface to the PHP cURL
  6. * functions and clean up some of the curl_setopt() calls.
  7. *
  8. * @package OOCurl
  9. * @author James Socol <me@jamessocol.com>
  10. * @version 0.3.0
  11. * @copyright Copyright (c) 2008, James Socol
  12. * @license http://www.opensource.org/licenses/mit-license.php
  13. */
  14. /* Copyright (c) 2008 James Socol
  15. Permission is hereby granted, free of charge, to any person obtaining a copy
  16. of this software and associated documentation files (the "Software"), to deal
  17. in the Software without restriction, including without limitation the rights
  18. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  19. copies of the Software, and to permit persons to whom the Software is
  20. furnished to do so, subject to the following conditions:
  21. The above copyright notice and this permission notice shall be included in
  22. all copies or substantial portions of the Software.
  23. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  26. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  28. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  29. THE SOFTWARE.
  30. */
  31. /**
  32. * Curl connection object
  33. *
  34. * Provides an Object-Oriented interface to the PHP cURL
  35. * functions and a clean way to replace curl_setopt().
  36. *
  37. * Instead of requiring a setopt() function and the CURLOPT_*
  38. * constants, which are cumbersome and ugly at best, this object
  39. * implements curl_setopt() through overloaded getter and setter
  40. * methods.
  41. *
  42. * For example, if you wanted to include the headers in the output,
  43. * the old way would be
  44. *
  45. * <code>
  46. * curl_setopt($ch, CURLOPT_HEADER, true);
  47. * </code>
  48. *
  49. * But with this object, it's simply
  50. *
  51. * <code>
  52. * $ch->header = true;
  53. * </code>
  54. *
  55. * <b>NB:</b> Since, in my experience, the vast majority
  56. * of cURL scripts set CURLOPT_RETURNTRANSFER to true, the {@link Curl}
  57. * class sets it by default. If you do not want CURLOPT_RETURNTRANSFER,
  58. * you'll need to do this:
  59. *
  60. * <code>
  61. * $c = new Curl;
  62. * $c->returntransfer = false;
  63. * </code>
  64. *
  65. * @package OOCurl
  66. * @author James Socol <me@jamessocol.com>
  67. * @version 0.3.0
  68. * @copyright Copyright (c) 2008-9, James Socol
  69. * @license http://www.opensource.org/licenses/mit-license.php
  70. */
  71. class Curl
  72. {
  73. /**
  74. * Store the curl_init() resource.
  75. * @var resource
  76. */
  77. protected $ch = NULL;
  78. /**
  79. * Store the CURLOPT_* values.
  80. *
  81. * Do not access directly. Access is through {@link __get()}
  82. * and {@link __set()} magic methods.
  83. *
  84. * @var array
  85. */
  86. protected $curlopt = array();
  87. /**
  88. * Flag the Curl object as linked to a {@link CurlParallel}
  89. * object.
  90. *
  91. * @var bool
  92. */
  93. protected $multi = false;
  94. /**
  95. * Store the response. Used with {@link fetch()} and
  96. * {@link fetch_json()}.
  97. *
  98. * @var string
  99. */
  100. protected $response;
  101. /**
  102. * The version of the OOCurl library.
  103. * @var string
  104. */
  105. const VERSION = '0.3';
  106. /**
  107. * Create the new {@link Curl} object, with the
  108. * optional URL parameter.
  109. *
  110. * @param string $url The URL to open (optional)
  111. * @return Curl A new Curl object.
  112. * @throws ErrorException
  113. */
  114. public function __construct ( $url = NULL )
  115. {
  116. // Make sure the cURL extension is loaded
  117. if ( !extension_loaded('curl') )
  118. throw new ErrorException("cURL library is not loaded. Please recompile PHP with the cURL library.");
  119. // Create the cURL resource
  120. $this->ch = curl_init();
  121. // Set some default options
  122. $this->url = $url;
  123. $this->returntransfer = true;
  124. // Applications can override this User Agent value
  125. $this->useragent = 'OOCurl '.self::VERSION;
  126. // Return $this for chaining
  127. return $this;
  128. }
  129. /**
  130. * When destroying the object, be sure to free resources.
  131. */
  132. public function __destruct()
  133. {
  134. $this->close();
  135. }
  136. /**
  137. * If the session was closed with {@link Curl::close()}, it can be reopened.
  138. *
  139. * This does not re-execute {@link Curl::__construct()}, but will reset all
  140. * the values in {@link $curlopt}.
  141. *
  142. * @param string $url The URL to open (optional)
  143. * @return bool|Curl
  144. */
  145. public function init ( $url = NULL )
  146. {
  147. // If it's still init'ed, return false.
  148. if ( $this->ch ) return false;
  149. // init a new cURL session
  150. $this->ch = curl_init();
  151. // reset all the values that were already set
  152. foreach ( $this->curlopt as $const => $value ) {
  153. curl_setopt($this->ch, constant($const), $value);
  154. }
  155. // finally if there's a new URL, set that
  156. if ( !empty($url) ) $this->url = $url;
  157. // return $this for chaining
  158. return $this;
  159. }
  160. /**
  161. * Execute the cURL transfer.
  162. *
  163. * @return mixed
  164. */
  165. public function exec ()
  166. {
  167. return curl_exec($this->ch);
  168. }
  169. /**
  170. * If the Curl object was added to a {@link CurlParallel}
  171. * object, then you can use this function to get the
  172. * returned data (whatever that is). Otherwise it's similar
  173. * to {@link exec()} except it saves the output, instead of
  174. * running the request repeatedly.
  175. *
  176. * @see $multi
  177. * @return mixed
  178. */
  179. public function fetch ()
  180. {
  181. if ( $this->multi ) {
  182. return curl_multi_getcontent($this->ch);
  183. } else {
  184. if ( $this->response ) {
  185. return $this->response;
  186. } else {
  187. $this->response = curl_exec($this->ch);
  188. return $this->response;
  189. }
  190. }
  191. }
  192. /**
  193. * Fetch a JSON encoded value and return a JSON
  194. * object. Requires the PHP JSON functions. Pass TRUE
  195. * to return an associative array instead of an object.
  196. *
  197. * @param bool array optional. Return an array instead of an object.
  198. * @return mixed an array or object (possibly null).
  199. */
  200. public function fetch_json ( $array = false )
  201. {
  202. return json_decode($this->fetch(), $array);
  203. }
  204. /**
  205. * Close the cURL session and free the resource.
  206. */
  207. public function close ()
  208. {
  209. curl_close($this->ch);
  210. }
  211. /**
  212. * Return an error string from the last execute (if any).
  213. *
  214. * @return string
  215. */
  216. public function error()
  217. {
  218. return curl_error($this->ch);
  219. }
  220. /**
  221. * Return the error number from the last execute (if any).
  222. *
  223. * @return integer
  224. */
  225. public function errno()
  226. {
  227. return curl_errno($this->ch);
  228. }
  229. /**
  230. * Get cURL version information (and adds OOCurl version info)
  231. *
  232. * @return array
  233. */
  234. public function version ()
  235. {
  236. $version = curl_version();
  237. $version['oocurl_version'] = self::VERSION;
  238. $version['oocurlparallel_version'] = CurlParallel::VERSION;
  239. return $version;
  240. }
  241. /**
  242. * Get information about this transfer.
  243. *
  244. * Accepts any of the following as a parameter:
  245. * - Nothing, and returns an array of all info values
  246. * - A CURLINFO_* constant, and returns a string
  247. * - A string of the second half of a CURLINFO_* constant,
  248. * for example, the string 'effective_url' is equivalent
  249. * to the CURLINFO_EFFECTIVE_URL constant. Not case
  250. * sensitive.
  251. *
  252. * @param mixed $opt A string or constant (optional).
  253. * @return mixed An array or string.
  254. */
  255. public function info ( $opt = false )
  256. {
  257. if (false === $opt) {
  258. return curl_getinfo($this->ch);
  259. }
  260. if ( is_int($opt) || ctype_digit($opt) ) {
  261. return curl_getinfo($this->ch,$opt);
  262. }
  263. if (constant('CURLINFO_'.strtoupper($opt))) {
  264. return curl_getinfo($this->ch,constant('CURLINFO_'.strtoupper($opt)));
  265. }
  266. }
  267. /**
  268. * Magic property setter.
  269. *
  270. * A sneaky way to access curl_setopt(). If the
  271. * constant CURLOPT_$opt exists, then we try to set
  272. * the option using curl_setopt() and return its
  273. * success. If it doesn't exist, just return false.
  274. *
  275. * Also stores the variable in {@link $curlopt} so
  276. * its value can be retrieved with {@link __get()}.
  277. *
  278. * @param string $opt The second half of the CURLOPT_* constant, not case sensitive
  279. * @param mixed $value
  280. * @return void
  281. */
  282. public function __set ( $opt, $value )
  283. {
  284. $const = 'CURLOPT_'.strtoupper($opt);
  285. if ( defined($const) ) {
  286. if (curl_setopt($this->ch,
  287. constant($const),
  288. $value)) {
  289. $this->curlopt[$const] = $value;
  290. }
  291. }
  292. }
  293. /**
  294. * Magic property getter.
  295. *
  296. * When options are set with {@link __set()}, they
  297. * are also stored in {@link $curlopt} so that we
  298. * can always find out what the options are now.
  299. *
  300. * The default cURL functions lack this ability.
  301. *
  302. * @param string $opt The second half of the CURLOPT_* constant, not case sensitive
  303. * @return mixed The set value of CURLOPT_<var>$opt</var>, or NULL if it hasn't been set (ie: is still default).
  304. */
  305. public function __get ( $opt )
  306. {
  307. return $this->curlopt['CURLOPT_'.strtoupper($opt)];
  308. }
  309. /**
  310. * Magic property isset()
  311. *
  312. * Can tell if a CURLOPT_* value was set by using
  313. * <code>
  314. * isset($curl->*)
  315. * </code>
  316. *
  317. * The default cURL functions lack this ability.
  318. *
  319. * @param string $opt The second half of the CURLOPT_* constant, not case sensitive
  320. * @return bool
  321. */
  322. public function __isset ( $opt )
  323. {
  324. return isset($this->curlopt['CURLOPT_'.strtoupper($opt)]);
  325. }
  326. /**
  327. * Magic property unset()
  328. *
  329. * Unfortunately, there is no way, short of writing an
  330. * extremely long, but mostly NULL-filled array, to
  331. * implement a decent version of
  332. * <code>
  333. * unset($curl->option);
  334. * </code>
  335. *
  336. * @todo Consider implementing an array of all the CURLOPT_*
  337. * constants and their default values.
  338. * @param string $opt The second half of the CURLOPT_* constant, not case sensitive
  339. * @return void
  340. */
  341. public function __unset ( $opt )
  342. {
  343. // Since we really can't reset a CURLOPT_* to its
  344. // default value without knowing the default value,
  345. // just do nothing.
  346. }
  347. /**
  348. * Grants access to {@link Curl::$ch $ch} to a {@link CurlParallel} object.
  349. *
  350. * @param CurlParallel $mh The CurlParallel object that needs {@link Curl::$ch $ch}.
  351. */
  352. public function grant ( CurlParallel $mh )
  353. {
  354. $mh->accept($this->ch);
  355. $this->multi = true;
  356. }
  357. /**
  358. * Removes access to {@link Curl::$ch $ch} from a {@link CurlParallel} object.
  359. *
  360. * @param CurlParallel $mh The CurlParallel object that no longer needs {@link Curl::$ch $ch}.
  361. */
  362. public function revoke ( CurlParallel $mh )
  363. {
  364. $mh->release($this->ch);
  365. $this->multi = false;
  366. }
  367. }
  368. /**
  369. * Implements parallel-processing for cURL requests.
  370. *
  371. * The PHP cURL library allows two or more requests to run in
  372. * parallel (at the same time). If you have multiple requests
  373. * that may have high latency but can then be processed quickly
  374. * in series (one after the other), then running them at the
  375. * same time may save time, overall.
  376. *
  377. * You must create individual {@link Curl} objects first, add them to
  378. * the CurlParallel object, execute the CurlParallel object,
  379. * then get the data from the individual {@link Curl} objects. (Yes,
  380. * it's annoying, but it's limited by the PHP cURL library.)
  381. *
  382. * For example:
  383. *
  384. * <code>
  385. * $a = new Curl("http://www.yahoo.com/");
  386. * $b = new Curl("http://www.microsoft.com/");
  387. *
  388. * $m = new CurlParallel($a, $b);
  389. *
  390. * $m->exec(); // Now we play the waiting game.
  391. *
  392. * printf("Yahoo is %n characters.\n", strlen($a->fetch()));
  393. * printf("Microsoft is %n characters.\n", strlen($a->fetch()));
  394. * </code>
  395. *
  396. * You can add any number of {@link Curl} objects to the
  397. * CurlParallel object's constructor (including 0), or you
  398. * can add with the {@link add()} method:
  399. *
  400. * <code>
  401. * $m = new CurlParallel;
  402. *
  403. * $a = new Curl("http://www.yahoo.com/");
  404. * $b = new Curl("http://www.microsoft.com/");
  405. *
  406. * $m->add($a);
  407. * $m->add($b);
  408. *
  409. * $m->exec(); // Now we play the waiting game.
  410. *
  411. * printf("Yahoo is %n characters.\n", strlen($a->fetch()));
  412. * printf("Microsoft is %n characters.\n", strlen($a->fetch()));
  413. * </code>
  414. *
  415. * @package OOCurl
  416. * @author James Socol <me@jamessocol.com>
  417. * @version 0.3.0
  418. * @since 0.1.2
  419. * @copyright Copyright (c) 2008-9, James Socol
  420. * @license http://www.opensource.org/licenses/mit-license.php
  421. */
  422. class CurlParallel
  423. {
  424. /**
  425. * Store the cURL master resource.
  426. * @var resource
  427. */
  428. protected $mh;
  429. /**
  430. * Store the resource handles that were
  431. * added to the session.
  432. * @var array
  433. */
  434. protected $ch = array();
  435. /**
  436. * Store the version number of this class.
  437. */
  438. const VERSION = '0.3.0';
  439. /**
  440. * Initialize the multisession handler.
  441. *
  442. * @uses add()
  443. * @param Curl $curl,... {@link Curl} objects to add to the Parallelizer.
  444. * @return CurlParallel
  445. */
  446. public function __construct ()
  447. {
  448. $this->mh = curl_multi_init();
  449. foreach ( func_get_args() as $ch ) {
  450. $this->add($ch);
  451. }
  452. return $this;
  453. }
  454. /**
  455. * On destruction, frees resources.
  456. */
  457. public function __destruct ()
  458. {
  459. $this->close();
  460. }
  461. /**
  462. * Close the current session and free resources.
  463. */
  464. public function close ()
  465. {
  466. foreach ( $this->ch as $ch ) {
  467. curl_multi_remove_handle($this->mh, $ch);
  468. }
  469. curl_multi_close($this->mh);
  470. }
  471. /**
  472. * Add a {@link Curl} object to the Parallelizer.
  473. *
  474. * Will throw a catchable fatal error if passed a non-Curl object.
  475. *
  476. * @uses Curl::grant()
  477. * @uses CurlParallel::accept()
  478. * @param Curl $ch Curl object.
  479. */
  480. public function add ( Curl $ch )
  481. {
  482. // get the protected resource
  483. $ch->grant($this);
  484. }
  485. /**
  486. * Remove a {@link Curl} object from the Parallelizer.
  487. *
  488. * @param Curl $ch Curl object.
  489. * @uses Curl::revoke()
  490. * @uses CurlParallel::release()
  491. */
  492. public function remove ( Curl $ch )
  493. {
  494. $ch->revoke($this);
  495. }
  496. /**
  497. * Execute the parallel cURL requests.
  498. */
  499. public function exec ()
  500. {
  501. do {
  502. curl_multi_exec($this->mh, $running);
  503. } while ($running > 0);
  504. }
  505. /**
  506. * Accept a resource handle from a {@link Curl} object and
  507. * add it to the master.
  508. *
  509. * @param resource $ch A resource returned by curl_init().
  510. */
  511. public function accept ( $ch )
  512. {
  513. $this->ch[] = $ch;
  514. curl_multi_add_handle($this->mh, $ch);
  515. }
  516. /**
  517. * Accept a resource handle from a {@link Curl} object and
  518. * remove it from the master.
  519. *
  520. * @param resource $ch A resource returned by curl_init().
  521. */
  522. public function release ( $ch )
  523. {
  524. if ( false !== $key = array_search($this->ch, $ch) ) {
  525. unset($this->ch[$key]);
  526. curl_multi_remove_handle($this->mh, $ch);
  527. }
  528. }
  529. }