TwitterAPIExchange.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <?php
  2. /**
  3. * Twitter-API-PHP : Simple PHP wrapper for the v1.1 API
  4. *
  5. * PHP version 5.3.10
  6. *
  7. * @category Awesomeness
  8. * @package Twitter-API-PHP
  9. * @author James Mallison <me@j7mbo.co.uk>
  10. * @license MIT License
  11. * @version 1.0.4
  12. * @link http://github.com/j7mbo/twitter-api-php
  13. */
  14. class TwitterAPIExchange
  15. {
  16. /**
  17. * @var string
  18. */
  19. private $oauth_access_token;
  20. /**
  21. * @var string
  22. */
  23. private $oauth_access_token_secret;
  24. /**
  25. * @var string
  26. */
  27. private $consumer_key;
  28. /**
  29. * @var string
  30. */
  31. private $consumer_secret;
  32. /**
  33. * @var array
  34. */
  35. private $postfields;
  36. /**
  37. * @var string
  38. */
  39. private $getfield;
  40. /**
  41. * @var mixed
  42. */
  43. protected $oauth;
  44. /**
  45. * @var string
  46. */
  47. public $url;
  48. /**
  49. * @var string
  50. */
  51. public $requestMethod;
  52. /**
  53. * The HTTP status code from the previous request
  54. *
  55. * @var int
  56. */
  57. protected $httpStatusCode;
  58. /**
  59. * Create the API access object. Requires an array of settings::
  60. * oauth access token, oauth access token secret, consumer key, consumer secret
  61. * These are all available by creating your own application on dev.twitter.com
  62. * Requires the cURL library
  63. *
  64. * @throws \RuntimeException When cURL isn't loaded
  65. * @throws \InvalidArgumentException When incomplete settings parameters are provided
  66. *
  67. * @param array $settings
  68. */
  69. public function __construct(array $settings)
  70. {
  71. if (!function_exists('curl_init'))
  72. {
  73. throw new RuntimeException('TwitterAPIExchange requires cURL extension to be loaded, see: http://curl.haxx.se/docs/install.html');
  74. }
  75. if (!isset($settings['oauth_access_token'])
  76. || !isset($settings['oauth_access_token_secret'])
  77. || !isset($settings['consumer_key'])
  78. || !isset($settings['consumer_secret']))
  79. {
  80. throw new InvalidArgumentException('Incomplete settings passed to TwitterAPIExchange');
  81. }
  82. $this->oauth_access_token = $settings['oauth_access_token'];
  83. $this->oauth_access_token_secret = $settings['oauth_access_token_secret'];
  84. $this->consumer_key = $settings['consumer_key'];
  85. $this->consumer_secret = $settings['consumer_secret'];
  86. }
  87. /**
  88. * Set postfields array, example: array('screen_name' => 'J7mbo')
  89. *
  90. * @param array $array Array of parameters to send to API
  91. *
  92. * @throws \Exception When you are trying to set both get and post fields
  93. *
  94. * @return TwitterAPIExchange Instance of self for method chaining
  95. */
  96. public function setPostfields(array $array)
  97. {
  98. if (!is_null($this->getGetfield()))
  99. {
  100. throw new Exception('You can only choose get OR post fields (post fields include put).');
  101. }
  102. if (isset($array['status']) && substr($array['status'], 0, 1) === '@')
  103. {
  104. $array['status'] = sprintf("\0%s", $array['status']);
  105. }
  106. foreach ($array as $key => &$value)
  107. {
  108. if (is_bool($value))
  109. {
  110. $value = ($value === true) ? 'true' : 'false';
  111. }
  112. }
  113. $this->postfields = $array;
  114. // rebuild oAuth
  115. if (isset($this->oauth['oauth_signature']))
  116. {
  117. $this->buildOauth($this->url, $this->requestMethod);
  118. }
  119. return $this;
  120. }
  121. /**
  122. * Set getfield string, example: '?screen_name=J7mbo'
  123. *
  124. * @param string $string Get key and value pairs as string
  125. *
  126. * @throws \Exception
  127. *
  128. * @return \TwitterAPIExchange Instance of self for method chaining
  129. */
  130. public function setGetfield($string)
  131. {
  132. if (!is_null($this->getPostfields()))
  133. {
  134. throw new Exception('You can only choose get OR post / post fields.');
  135. }
  136. $getfields = preg_replace('/^\?/', '', explode('&', $string));
  137. $params = array();
  138. foreach ($getfields as $field)
  139. {
  140. if ($field !== '')
  141. {
  142. list($key, $value) = explode('=', $field);
  143. $params[$key] = $value;
  144. }
  145. }
  146. $this->getfield = '?' . http_build_query($params, '', '&');
  147. return $this;
  148. }
  149. /**
  150. * Get getfield string (simple getter)
  151. *
  152. * @return string $this->getfields
  153. */
  154. public function getGetfield()
  155. {
  156. return $this->getfield;
  157. }
  158. /**
  159. * Get postfields array (simple getter)
  160. *
  161. * @return array $this->postfields
  162. */
  163. public function getPostfields()
  164. {
  165. return $this->postfields;
  166. }
  167. /**
  168. * Build the Oauth object using params set in construct and additionals
  169. * passed to this method. For v1.1, see: https://dev.twitter.com/docs/api/1.1
  170. *
  171. * @param string $url The API url to use. Example: https://api.twitter.com/1.1/search/tweets.json
  172. * @param string $requestMethod Either POST or GET
  173. *
  174. * @throws \Exception
  175. *
  176. * @return \TwitterAPIExchange Instance of self for method chaining
  177. */
  178. public function buildOauth($url, $requestMethod)
  179. {
  180. if (!in_array(strtolower($requestMethod), array('post', 'get', 'put', 'delete')))
  181. {
  182. throw new Exception('Request method must be either POST, GET or PUT or DELETE');
  183. }
  184. $consumer_key = $this->consumer_key;
  185. $consumer_secret = $this->consumer_secret;
  186. $oauth_access_token = $this->oauth_access_token;
  187. $oauth_access_token_secret = $this->oauth_access_token_secret;
  188. $oauth = array(
  189. 'oauth_consumer_key' => $consumer_key,
  190. 'oauth_nonce' => time(),
  191. 'oauth_signature_method' => 'HMAC-SHA1',
  192. 'oauth_token' => $oauth_access_token,
  193. 'oauth_timestamp' => time(),
  194. 'oauth_version' => '1.0'
  195. );
  196. $getfield = $this->getGetfield();
  197. if (!is_null($getfield))
  198. {
  199. $getfields = str_replace('?', '', explode('&', $getfield));
  200. foreach ($getfields as $g)
  201. {
  202. $split = explode('=', $g);
  203. /** In case a null is passed through **/
  204. if (isset($split[1]))
  205. {
  206. $oauth[$split[0]] = urldecode($split[1]);
  207. }
  208. }
  209. }
  210. $postfields = $this->getPostfields();
  211. if (!is_null($postfields)) {
  212. foreach ($postfields as $key => $value) {
  213. $oauth[$key] = $value;
  214. }
  215. }
  216. $base_info = $this->buildBaseString($url, $requestMethod, $oauth);
  217. $composite_key = rawurlencode($consumer_secret) . '&' . rawurlencode($oauth_access_token_secret);
  218. $oauth_signature = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true));
  219. $oauth['oauth_signature'] = $oauth_signature;
  220. $this->url = $url;
  221. $this->requestMethod = $requestMethod;
  222. $this->oauth = $oauth;
  223. return $this;
  224. }
  225. /**
  226. * Perform the actual data retrieval from the API
  227. *
  228. * @param boolean $return If true, returns data. This is left in for backward compatibility reasons
  229. * @param array $curlOptions Additional Curl options for this request
  230. *
  231. * @throws \Exception
  232. *
  233. * @return string json If $return param is true, returns json data.
  234. */
  235. public function performRequest($return = true, $curlOptions = array())
  236. {
  237. if (!is_bool($return))
  238. {
  239. throw new Exception('performRequest parameter must be true or false');
  240. }
  241. $header = array($this->buildAuthorizationHeader($this->oauth), 'Expect:');
  242. $getfield = $this->getGetfield();
  243. $postfields = $this->getPostfields();
  244. if (in_array(strtolower($this->requestMethod), array('put', 'delete')))
  245. {
  246. $curlOptions[CURLOPT_CUSTOMREQUEST] = $this->requestMethod;
  247. }
  248. $options = $curlOptions + array(
  249. CURLOPT_HTTPHEADER => $header,
  250. CURLOPT_HEADER => false,
  251. CURLOPT_URL => $this->url,
  252. CURLOPT_RETURNTRANSFER => true,
  253. CURLOPT_TIMEOUT => 10,
  254. );
  255. if (!is_null($postfields))
  256. {
  257. $options[CURLOPT_POSTFIELDS] = http_build_query($postfields, '', '&');
  258. }
  259. else
  260. {
  261. if ($getfield !== '')
  262. {
  263. $options[CURLOPT_URL] .= $getfield;
  264. }
  265. }
  266. $feed = curl_init();
  267. curl_setopt_array($feed, $options);
  268. $json = curl_exec($feed);
  269. $this->httpStatusCode = curl_getinfo($feed, CURLINFO_HTTP_CODE);
  270. if (($error = curl_error($feed)) !== '')
  271. {
  272. curl_close($feed);
  273. throw new \Exception($error);
  274. }
  275. curl_close($feed);
  276. return $json;
  277. }
  278. /**
  279. * Private method to generate the base string used by cURL
  280. *
  281. * @param string $baseURI
  282. * @param string $method
  283. * @param array $params
  284. *
  285. * @return string Built base string
  286. */
  287. private function buildBaseString($baseURI, $method, $params)
  288. {
  289. $return = array();
  290. ksort($params);
  291. foreach($params as $key => $value)
  292. {
  293. $return[] = rawurlencode($key) . '=' . rawurlencode($value);
  294. }
  295. return $method . "&" . rawurlencode($baseURI) . '&' . rawurlencode(implode('&', $return));
  296. }
  297. /**
  298. * Private method to generate authorization header used by cURL
  299. *
  300. * @param array $oauth Array of oauth data generated by buildOauth()
  301. *
  302. * @return string $return Header used by cURL for request
  303. */
  304. private function buildAuthorizationHeader(array $oauth)
  305. {
  306. $return = 'Authorization: OAuth ';
  307. $values = array();
  308. foreach($oauth as $key => $value)
  309. {
  310. if (in_array($key, array('oauth_consumer_key', 'oauth_nonce', 'oauth_signature',
  311. 'oauth_signature_method', 'oauth_timestamp', 'oauth_token', 'oauth_version'))) {
  312. $values[] = "$key=\"" . rawurlencode($value) . "\"";
  313. }
  314. }
  315. $return .= implode(', ', $values);
  316. return $return;
  317. }
  318. /**
  319. * Helper method to perform our request
  320. *
  321. * @param string $url
  322. * @param string $method
  323. * @param string $data
  324. * @param array $curlOptions
  325. *
  326. * @throws \Exception
  327. *
  328. * @return string The json response from the server
  329. */
  330. public function request($url, $method = 'get', $data = null, $curlOptions = array())
  331. {
  332. if (strtolower($method) === 'get')
  333. {
  334. $this->setGetfield($data);
  335. }
  336. else
  337. {
  338. $this->setPostfields($data);
  339. }
  340. return $this->buildOauth($url, $method)->performRequest(true, $curlOptions);
  341. }
  342. /**
  343. * Get the HTTP status code for the previous request
  344. *
  345. * @return integer
  346. */
  347. public function getHttpStatusCode()
  348. {
  349. return $this->httpStatusCode;
  350. }
  351. }