PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/application/libraries/tweet.php

https://github.com/netspencer/codeigniter-twitter
PHP | 641 lines | 489 code | 146 blank | 6 comment | 55 complexity | 14ddb646e7e2862c0b3eb8ca9e1bcd49 MD5 | raw file
  1. <?php
  2. class tweet {
  3. private $_oauth = NULL;
  4. function __construct()
  5. {
  6. $this->_oauth = new tweetOauth();
  7. }
  8. function __call($method, $args)
  9. {
  10. if ( method_exists($this, $method) )
  11. {
  12. return call_user_func_array(array($this, $method), $args);
  13. }
  14. return call_user_func_array(array($this->_oauth, $method), $args);
  15. }
  16. function logged_in()
  17. {
  18. return $this->_oauth->loggedIn();
  19. }
  20. function set_callback($url)
  21. {
  22. $this->_oauth->setCallback($url);
  23. }
  24. function login()
  25. {
  26. return $this->_oauth->login();
  27. }
  28. function logout()
  29. {
  30. return $this->_oauth->logout();
  31. }
  32. function get_tokens()
  33. {
  34. $tokens = array(
  35. 'oauth_token' => $this->_oauth->getAccessKey(),
  36. 'oauth_token_secret' => $this->_oauth->getAccessSecret()
  37. );
  38. return $tokens;
  39. }
  40. function set_tokens($tokens)
  41. {
  42. return $this->_oauth->setAccessTokens($tokens);
  43. }
  44. }
  45. class tweetException extends Exception {
  46. function __construct($string)
  47. {
  48. parent::__construct($string);
  49. }
  50. public function __toString() {
  51. return "exception '".__CLASS__ ."' with message '".$this->getMessage()."' in ".$this->getFile().":".$this->getLine()."\nStack trace:\n".$this->getTraceAsString();
  52. }
  53. }
  54. class tweetConnection {
  55. // Allow multi-threading.
  56. private $_mch = NULL;
  57. private $_properties = array();
  58. function __construct()
  59. {
  60. $this->_mch = curl_multi_init();
  61. $this->_properties = array(
  62. 'code' => CURLINFO_HTTP_CODE,
  63. 'time' => CURLINFO_TOTAL_TIME,
  64. 'length' => CURLINFO_CONTENT_LENGTH_DOWNLOAD,
  65. 'type' => CURLINFO_CONTENT_TYPE
  66. );
  67. }
  68. private function _initConnection($url)
  69. {
  70. $this->_ch = curl_init($url);
  71. curl_setopt($this->_ch, CURLOPT_RETURNTRANSFER, TRUE);
  72. }
  73. public function get($url, $params)
  74. {
  75. if ( count($params['request']) > 0 )
  76. {
  77. $url .= '?';
  78. foreach( $params['request'] as $k => $v )
  79. {
  80. $url .= "{$k}={$v}&";
  81. }
  82. $url = substr($url, 0, -1);
  83. }
  84. $this->_initConnection($url);
  85. $response = $this->_addCurl($url, $params);
  86. return $response;
  87. }
  88. public function post($url, $params)
  89. {
  90. // Todo
  91. $post = '';
  92. foreach ( $params['request'] as $k => $v )
  93. {
  94. $post .= "{$k}={$v}&";
  95. }
  96. $post = substr($post, 0, -1);
  97. $this->_initConnection($url, $params);
  98. curl_setopt($this->_ch, CURLOPT_POST, 1);
  99. curl_setopt($this->_ch, CURLOPT_POSTFIELDS, $post);
  100. $response = $this->_addCurl($url, $params);
  101. return $response;
  102. }
  103. private function _addOauthHeaders(&$ch, $url, $oauthHeaders)
  104. {
  105. $_h = array('Expect:');
  106. $urlParts = parse_url($url);
  107. $oauth = 'Authorization: OAuth realm="' . $urlParts['path'] . '",';
  108. foreach ( $oauthHeaders as $name => $value )
  109. {
  110. $oauth .= "{$name}=\"{$value}\",";
  111. }
  112. $_h[] = substr($oauth, 0, -1);
  113. curl_setopt($ch, CURLOPT_HTTPHEADER, $_h);
  114. }
  115. private function _addCurl($url, $params = array())
  116. {
  117. if ( !empty($params['oauth']) )
  118. {
  119. $this->_addOauthHeaders($this->_ch, $url, $params['oauth']);
  120. }
  121. $ch = $this->_ch;
  122. $key = (string) $ch;
  123. $this->_requests[$key] = $ch;
  124. $response = curl_multi_add_handle($this->_mch, $ch);
  125. if ( $response === CURLM_OK || $response === CURLM_CALL_MULTI_PERFORM )
  126. {
  127. do {
  128. $mch = curl_multi_exec($this->_mch, $active);
  129. } while ( $mch === CURLM_CALL_MULTI_PERFORM );
  130. return $this->_getResponse($key);
  131. }
  132. else
  133. {
  134. return $response;
  135. }
  136. }
  137. private function _getResponse($key = NULL)
  138. {
  139. if ( $key == NULL ) return FALSE;
  140. if ( isset($this->_responses[$key]) )
  141. {
  142. return $this->_responses[$key];
  143. }
  144. $running = NULL;
  145. do
  146. {
  147. $response = curl_multi_exec($this->_mch, $running_curl);
  148. if ( $running !== NULL && $running_curl != $running )
  149. {
  150. $this->_setResponse($key);
  151. if ( isset($this->_responses[$key]) )
  152. {
  153. $response = new tweetResponseOauth( (object) $this->_responses[$key] );
  154. if ( $response->__resp->code !== 200 )
  155. {
  156. throw new tweetException($response->__resp->code.' | Request Failed: '.$response->__resp->data->request.' - '.$response->__resp->data->error);
  157. }
  158. return $response;
  159. }
  160. }
  161. $running = $running_curl;
  162. } while ( $running_curl > 0);
  163. }
  164. private function _setResponse($key)
  165. {
  166. while( $done = curl_multi_info_read($this->_mch) )
  167. {
  168. $key = (string) $done['handle'];
  169. $this->_responses[$key]['data'] = curl_multi_getcontent($done['handle']);
  170. foreach ( $this->_properties as $curl_key => $value )
  171. {
  172. $this->_responses[$key][$curl_key] = curl_getinfo($done['handle'], $value);
  173. curl_multi_remove_handle($this->_mch, $done['handle']);
  174. }
  175. }
  176. }
  177. }
  178. class tweetResponseOauth {
  179. private $__construct;
  180. public function __construct($resp)
  181. {
  182. $this->__resp = $resp;
  183. if ( strpos($this->__resp->type, 'json') !== FALSE )
  184. {
  185. $this->__resp->data = json_decode($this->__resp->data);
  186. }
  187. }
  188. public function __get($name)
  189. {
  190. if ($this->__resp->code < 200 || $this->__resp->code > 299) return FALSE;
  191. if ( is_string($this->__resp->data ) )
  192. {
  193. parse_str($this->__resp->data, $result);
  194. }
  195. else
  196. {
  197. $result = $this->__resp->data;
  198. }
  199. foreach($result as $k => $v)
  200. {
  201. $this->$k = $v;
  202. }
  203. if ( $name === '_result')
  204. {
  205. return $result;
  206. }
  207. return $result[$name];
  208. }
  209. }
  210. class tweetOauth extends tweetConnection {
  211. private $_obj;
  212. private $_tokens = array();
  213. private $_authorizationUrl = 'http://api.twitter.com/oauth/authorize';
  214. private $_requestTokenUrl = 'http://api.twitter.com/oauth/request_token';
  215. private $_accessTokenUrl = 'http://api.twitter.com/oauth/access_token';
  216. private $_signatureMethod = 'HMAC-SHA1';
  217. private $_version = '1.0';
  218. private $_apiUrl = 'http://api.twitter.com';
  219. private $_searchUrl = 'http://search.twitter.com/';
  220. private $_callback = NULL;
  221. private $_errors = array();
  222. private $_enable_debug = FALSE;
  223. function __construct()
  224. {
  225. parent::__construct();
  226. $this->_obj =& get_instance();
  227. $this->_obj->load->config('tweet');
  228. $this->_obj->load->library('session');
  229. $this->_obj->load->library('unit_test');
  230. $this->_obj->load->helper('url');
  231. $this->_tokens = array(
  232. 'consumer_key' => $this->_obj->config->item('tweet_consumer_key'),
  233. 'consumer_secret' => $this->_obj->config->item('tweet_consumer_secret'),
  234. 'access_key' => $this->_getAccessKey(),
  235. 'access_secret' => $this->_getAccessSecret()
  236. );
  237. $this->_checkLogin();
  238. }
  239. function __destruct()
  240. {
  241. if ( !$this->_enable_debug ) return;
  242. if ( !empty($this->_errors) )
  243. {
  244. foreach ( $this->_errors as $key => $e )
  245. {
  246. echo '<pre>'.$e.'</pre>';
  247. }
  248. }
  249. }
  250. public function enable_debug($debug)
  251. {
  252. $debug = (bool) $debug;
  253. $this->_enable_debug = $debug;
  254. }
  255. public function call($method, $path, $args = NULL)
  256. {
  257. $response = $this->_httpRequest(strtoupper($method), $this->_apiUrl.'/'.$path.'.json', $args);
  258. // var_dump($response);
  259. // die();
  260. return ( $response === NULL ) ? FALSE : $response->_result;
  261. }
  262. public function search($args = NULL)
  263. {
  264. $response = $this->_httpRequest('GET', $this->_searchUrl.'search.json', $args);
  265. return ( $response === NULL ) ? FALSE : $response->_result;
  266. }
  267. public function loggedIn()
  268. {
  269. $access_key = $this->_getAccessKey();
  270. $access_secret = $this->_getAccessSecret();
  271. $loggedIn = FALSE;
  272. if ( $this->_getAccessKey() !== NULL && $this->_getAccessSecret() !== NULL )
  273. {
  274. $loggedIn = TRUE;
  275. }
  276. $this->_obj->unit->run($loggedIn, TRUE, 'Logged In');
  277. return $loggedIn;
  278. }
  279. private function _checkLogin()
  280. {
  281. if ( isset($_GET['oauth_token']) )
  282. {
  283. $this->_setAccessKey($_GET['oauth_token']);
  284. $token = $this->_getAccessToken();
  285. $token = $token->_result;
  286. $token = ( is_bool($token) ) ? $token : (object) $token;
  287. if ( !empty($token->oauth_token) && !empty($token->oauth_token_secret) )
  288. {
  289. $this->_setAccessKey($token->oauth_token);
  290. $this->_setAccessSecret($token->oauth_token_secret);
  291. }
  292. redirect(current_url());
  293. return NULL;
  294. }
  295. }
  296. public function login()
  297. {
  298. if ( ($this->_getAccessKey() === NULL || $this->_getAccessSecret() === NULL) )
  299. {
  300. header('Location: '.$this->_getAuthorizationUrl());
  301. return;
  302. }
  303. return $this->_checkLogin();
  304. }
  305. public function logout()
  306. {
  307. $this->_obj->session->unset_userdata('twitter_oauth_tokens');
  308. }
  309. public function getTokens()
  310. {
  311. return $this->_tokens;
  312. }
  313. private function _getConsumerKey()
  314. {
  315. return $this->_tokens['consumer_key'];
  316. }
  317. private function _getConsumerSecret()
  318. {
  319. return $this->_tokens['consumer_secret'];
  320. }
  321. public function getAccessKey(){ return $this->_getAccessKey(); }
  322. private function _getAccessKey()
  323. {
  324. $tokens = $this->_obj->session->userdata('twitter_oauth_tokens');
  325. return ( $tokens === FALSE || !isset($tokens['access_key']) || empty($tokens['access_key']) ) ? NULL : $tokens['access_key'];
  326. }
  327. private function _setAccessKey($access_key)
  328. {
  329. $tokens = $this->_obj->session->userdata('twitter_oauth_tokens');
  330. if ( $tokens === FALSE || !is_array($tokens) )
  331. {
  332. $tokens = array('access_key' => $access_key);
  333. }
  334. else
  335. {
  336. $tokens['access_key'] = $access_key;
  337. }
  338. $this->_obj->session->set_userdata('twitter_oauth_tokens', $tokens);
  339. }
  340. public function getAccessSecret(){ return $this->_getAccessSecret(); }
  341. private function _getAccessSecret()
  342. {
  343. $tokens = $this->_obj->session->userdata('twitter_oauth_tokens');
  344. return ( $tokens === FALSE || !isset($tokens['access_secret']) || empty($tokens['access_secret']) ) ? NULL : $tokens['access_secret'];
  345. }
  346. private function _setAccessSecret($access_secret)
  347. {
  348. $tokens = $this->_obj->session->userdata('twitter_oauth_tokens');
  349. if ( $tokens === FALSE || !is_array($tokens) )
  350. {
  351. $tokens = array('access_secret' => $access_secret);
  352. }
  353. else
  354. {
  355. $tokens['access_secret'] = $access_secret;
  356. }
  357. $this->_obj->session->set_userdata('twitter_oauth_tokens', $tokens);
  358. }
  359. private function _setAccessTokens($tokens)
  360. {
  361. $this->_setAccessKey($tokens['oauth_token']);
  362. $this->_setAccessSecret($tokens['oauth_token_secret']);
  363. }
  364. public function setAccessTokens($tokens)
  365. {
  366. return $this->_setAccessTokens($tokens);
  367. }
  368. private function _getAuthorizationUrl()
  369. {
  370. $token = $this->_getRequestToken();
  371. return $this->_authorizationUrl.'?oauth_token=' . $token->oauth_token;
  372. }
  373. private function _getRequestToken()
  374. {
  375. return $this->_httpRequest('GET', $this->_requestTokenUrl);
  376. }
  377. private function _getAccessToken()
  378. {
  379. return $this->_httpRequest('GET', $this->_accessTokenUrl);
  380. }
  381. protected function _httpRequest($method = null, $url = null, $params = null)
  382. {
  383. if( empty($method) || empty($url) ) return FALSE;
  384. if ( empty($params['oauth_signature']) ) $params = $this->_prepareParameters($method, $url, $params);
  385. $this->_connection = new tweetConnection();
  386. try {
  387. switch ( $method )
  388. {
  389. case 'GET':
  390. return $this->_connection->get($url, $params);
  391. break;
  392. case 'POST':
  393. return $this->_connection->post($url, $params);
  394. break;
  395. case 'PUT':
  396. return NULL;
  397. break;
  398. case 'DELETE':
  399. return NULL;
  400. break;
  401. }
  402. } catch (tweetException $e) {
  403. $this->_errors[] = $e;
  404. }
  405. }
  406. private function _getCallback()
  407. {
  408. return $this->_callback;
  409. }
  410. public function setCallback($url)
  411. {
  412. $this->_callback = $url;
  413. }
  414. private function _prepareParameters($method = NULL, $url = NULL, $params = NULL)
  415. {
  416. if ( empty($method) || empty($url) ) return FALSE;
  417. $callback = $this->_getCallback();
  418. if ( !empty($callback) )
  419. {
  420. $oauth['oauth_callback'] = $callback;
  421. }
  422. $this->setCallback(NULL);
  423. $oauth['oauth_consumer_key'] = $this->_getConsumerKey();
  424. $oauth['oauth_token'] = $this->_getAccessKey();
  425. $oauth['oauth_nonce'] = $this->_generateNonce();
  426. $oauth['oauth_timestamp'] = time();
  427. $oauth['oauth_signature_method'] = $this->_signatureMethod;
  428. $oauth['oauth_version'] = $this->_version;
  429. array_walk($oauth, array($this, '_encode_rfc3986'));
  430. if ( is_array($params) )
  431. {
  432. array_walk($params, array($this, '_encode_rfc3986'));
  433. }
  434. $encodedParams = array_merge($oauth, (array)$params);
  435. ksort($encodedParams);
  436. $oauth['oauth_signature'] = $this->_encode_rfc3986($this->_generateSignature($method, $url, $encodedParams));
  437. return array('request' => $params, 'oauth' => $oauth);
  438. }
  439. private function _generateNonce()
  440. {
  441. return md5(uniqid(rand(), TRUE));
  442. }
  443. private function _encode_rfc3986($string)
  444. {
  445. return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode(($string))));
  446. }
  447. private function _generateSignature($method = null, $url = null, $params = null)
  448. {
  449. if( empty($method) || empty($url) ) return FALSE;
  450. // concatenating
  451. $concatenatedParams = '';
  452. foreach ($params as $k => $v)
  453. {
  454. $v = $this->_encode_rfc3986($v);
  455. $concatenatedParams .= "{$k}={$v}&";
  456. }
  457. $concatenatedParams = $this->_encode_rfc3986(substr($concatenatedParams, 0, -1));
  458. // normalize url
  459. $normalizedUrl = $this->_encode_rfc3986($this->_normalizeUrl($url));
  460. $method = $this->_encode_rfc3986($method); // don't need this but why not?
  461. $signatureBaseString = "{$method}&{$normalizedUrl}&{$concatenatedParams}";
  462. return $this->_signString($signatureBaseString);
  463. }
  464. private function _normalizeUrl($url = NULL)
  465. {
  466. $urlParts = parse_url($url);
  467. if ( !isset($urlParts['port']) ) $urlParts['port'] = 80;
  468. $scheme = strtolower($urlParts['scheme']);
  469. $host = strtolower($urlParts['host']);
  470. $port = intval($urlParts['port']);
  471. $retval = "{$scheme}://{$host}";
  472. if ( $port > 0 && ( $scheme === 'http' && $port !== 80 ) || ( $scheme === 'https' && $port !== 443 ) )
  473. {
  474. $retval .= ":{$port}";
  475. }
  476. $retval .= $urlParts['path'];
  477. if ( !empty($urlParts['query']) )
  478. {
  479. $retval .= "?{$urlParts['query']}";
  480. }
  481. return $retval;
  482. }
  483. private function _signString($string)
  484. {
  485. $retval = FALSE;
  486. switch ( $this->_signatureMethod )
  487. {
  488. case 'HMAC-SHA1':
  489. $key = $this->_encode_rfc3986($this->_getConsumerSecret()) . '&' . $this->_encode_rfc3986($this->_getAccessSecret());
  490. $retval = base64_encode(hash_hmac('sha1', $string, $key, true));
  491. break;
  492. }
  493. return $retval;
  494. }
  495. }