Mercurial > hg > ywww
comparison twitter/EpiTwitter.php @ 6:077b0a0a3e6d
remaining originals according to dependency walk
| author | Robert Boland <robert@markup.co.uk> |
|---|---|
| date | Thu, 16 Feb 2017 22:29:02 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 5:55445b456ad0 | 6:077b0a0a3e6d |
|---|---|
| 1 <?php | |
| 2 /* | |
| 3 * Class to integrate with Twitter's API. | |
| 4 * Authenticated calls are done using OAuth and require access tokens for a user. | |
| 5 * API calls which do not require authentication do not require tokens (i.e. search/trends) | |
| 6 * | |
| 7 * Full documentation available on github | |
| 8 * http://wiki.github.com/jmathai/twitter-async | |
| 9 * | |
| 10 * @author Jaisen Mathai <jaisen@jmathai.com> | |
| 11 */ | |
| 12 class EpiTwitter extends EpiOAuth | |
| 13 { | |
| 14 const EPITWITTER_SIGNATURE_METHOD = 'HMAC-SHA1'; | |
| 15 const EPITWITTER_AUTH_OAUTH = 'oauth'; | |
| 16 const EPITWITTER_AUTH_BASIC = 'basic'; | |
| 17 protected $requestTokenUrl= 'http://twitter.com/oauth/request_token'; | |
| 18 protected $accessTokenUrl = 'http://twitter.com/oauth/access_token'; | |
| 19 protected $authorizeUrl = 'http://twitter.com/oauth/authorize'; | |
| 20 protected $authenticateUrl= 'http://twitter.com/oauth/authenticate'; | |
| 21 protected $apiUrl = 'http://twitter.com'; | |
| 22 protected $apiVersionedUrl= 'http://api.twitter.com'; | |
| 23 protected $searchUrl = 'http://search.twitter.com'; | |
| 24 protected $userAgent = 'EpiTwitter (http://github.com/jmathai/twitter-async/tree/)'; | |
| 25 protected $apiVersion = '1'; | |
| 26 protected $isAsynchronous = false; | |
| 27 | |
| 28 /* OAuth methods */ | |
| 29 public function delete($endpoint, $params = null) | |
| 30 { | |
| 31 return $this->request('DELETE', $endpoint, $params); | |
| 32 } | |
| 33 | |
| 34 public function get($endpoint, $params = null) | |
| 35 { | |
| 36 return $this->request('GET', $endpoint, $params); | |
| 37 } | |
| 38 | |
| 39 public function post($endpoint, $params = null) | |
| 40 { | |
| 41 return $this->request('POST', $endpoint, $params); | |
| 42 } | |
| 43 | |
| 44 /* Basic auth methods */ | |
| 45 public function delete_basic($endpoint, $params = null, $username = null, $password = null) | |
| 46 { | |
| 47 return $this->request_basic('DELETE', $endpoint, $params, $username, $password); | |
| 48 } | |
| 49 | |
| 50 public function get_basic($endpoint, $params = null, $username = null, $password = null) | |
| 51 { | |
| 52 return $this->request_basic('GET', $endpoint, $params, $username, $password); | |
| 53 } | |
| 54 | |
| 55 public function post_basic($endpoint, $params = null, $username = null, $password = null) | |
| 56 { | |
| 57 return $this->request_basic('POST', $endpoint, $params, $username, $password); | |
| 58 } | |
| 59 | |
| 60 public function useApiVersion($version = null) | |
| 61 { | |
| 62 $this->apiVersion = $version; | |
| 63 } | |
| 64 | |
| 65 public function useAsynchronous($async = true) | |
| 66 { | |
| 67 $this->isAsynchronous = (bool)$async; | |
| 68 } | |
| 69 | |
| 70 public function __construct($consumerKey = null, $consumerSecret = null, $oauthToken = null, $oauthTokenSecret = null) | |
| 71 { | |
| 72 parent::__construct($consumerKey, $consumerSecret, self::EPITWITTER_SIGNATURE_METHOD); | |
| 73 $this->setToken($oauthToken, $oauthTokenSecret); | |
| 74 } | |
| 75 | |
| 76 public function __call($name, $params = null/*, $username, $password*/) | |
| 77 { | |
| 78 $parts = explode('_', $name); | |
| 79 $method = strtoupper(array_shift($parts)); | |
| 80 $parts = implode('_', $parts); | |
| 81 $endpoint = '/' . preg_replace('/[A-Z]|[0-9]+/e', "'/'.strtolower('\\0')", $parts) . '.json'; | |
| 82 /* HACK: this is required for list support that starts with a user id */ | |
| 83 $endpoint = str_replace('//','/',$endpoint); | |
| 84 $args = !empty($params) ? array_shift($params) : null; | |
| 85 | |
| 86 // calls which do not have a consumerKey are assumed to not require authentication | |
| 87 if($this->consumerKey === null) | |
| 88 { | |
| 89 $username = null; | |
| 90 $password = null; | |
| 91 | |
| 92 if(!empty($params)) | |
| 93 { | |
| 94 $username = array_shift($params); | |
| 95 $password = !empty($params) ? array_shift($params) : null; | |
| 96 } | |
| 97 | |
| 98 return $this->request_basic($method, $endpoint, $args, $username, $password); | |
| 99 } | |
| 100 | |
| 101 return $this->request($method, $endpoint, $args); | |
| 102 } | |
| 103 | |
| 104 private function getApiUrl($endpoint) | |
| 105 { | |
| 106 if(preg_match('@^/(trends|search)[./]?(?=(json|daily|current|weekly))@', $endpoint)) | |
| 107 return "{$this->searchUrl}{$endpoint}"; | |
| 108 elseif(!empty($this->apiVersion)) | |
| 109 return "{$this->apiVersionedUrl}/{$this->apiVersion}{$endpoint}"; | |
| 110 else | |
| 111 return "{$this->apiUrl}{$endpoint}"; | |
| 112 } | |
| 113 | |
| 114 private function request($method, $endpoint, $params = null) | |
| 115 { | |
| 116 $url = $this->getUrl($this->getApiUrl($endpoint)); | |
| 117 $resp= new EpiTwitterJson(call_user_func(array($this, 'httpRequest'), $method, $url, $params, $this->isMultipart($params)), $this->debug); | |
| 118 if(!$this->isAsynchronous) | |
| 119 $resp->responseText; | |
| 120 | |
| 121 return $resp; | |
| 122 } | |
| 123 | |
| 124 private function request_basic($method, $endpoint, $params = null, $username = null, $password = null) | |
| 125 { | |
| 126 $url = $this->getApiUrl($endpoint); | |
| 127 if($method === 'GET') | |
| 128 $url .= is_null($params) ? '' : '?'.http_build_query($params, '', '&'); | |
| 129 $ch = curl_init($url); | |
| 130 curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); | |
| 131 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
| 132 curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout); | |
| 133 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); | |
| 134 if($method === 'POST' && $params !== null) | |
| 135 { | |
| 136 if($this->isMultipart($params)) | |
| 137 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); | |
| 138 else | |
| 139 curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildHttpQueryRaw($params)); | |
| 140 } | |
| 141 if(!empty($username) && !empty($password)) | |
| 142 curl_setopt($ch, CURLOPT_USERPWD, "{$username}:{$password}"); | |
| 143 | |
| 144 $resp = new EpiTwitterJson(EpiCurl::getInstance()->addCurl($ch), $this->debug); | |
| 145 if(!$this->isAsynchronous) | |
| 146 $resp->responseText; | |
| 147 | |
| 148 return $resp; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 class EpiTwitterJson implements ArrayAccess, Countable, IteratorAggregate | |
| 153 { | |
| 154 private $debug; | |
| 155 private $__resp; | |
| 156 public function __construct($response, $debug = false) | |
| 157 { | |
| 158 $this->__resp = $response; | |
| 159 $this->debug = $debug; | |
| 160 } | |
| 161 | |
| 162 // ensure that calls complete by blocking for results, NOOP if already returned | |
| 163 public function __destruct() | |
| 164 { | |
| 165 $this->responseText; | |
| 166 } | |
| 167 | |
| 168 // Implementation of the IteratorAggregate::getIterator() to support foreach ($this as $...) | |
| 169 public function getIterator () | |
| 170 { | |
| 171 if ($this->__obj) { | |
| 172 return new ArrayIterator($this->__obj); | |
| 173 } else { | |
| 174 return new ArrayIterator($this->response); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 // Implementation of Countable::count() to support count($this) | |
| 179 public function count () | |
| 180 { | |
| 181 return count($this->response); | |
| 182 } | |
| 183 | |
| 184 // Next four functions are to support ArrayAccess interface | |
| 185 // 1 | |
| 186 public function offsetSet($offset, $value) | |
| 187 { | |
| 188 $this->response[$offset] = $value; | |
| 189 } | |
| 190 | |
| 191 // 2 | |
| 192 public function offsetExists($offset) | |
| 193 { | |
| 194 return isset($this->response[$offset]); | |
| 195 } | |
| 196 | |
| 197 // 3 | |
| 198 public function offsetUnset($offset) | |
| 199 { | |
| 200 unset($this->response[$offset]); | |
| 201 } | |
| 202 | |
| 203 // 4 | |
| 204 public function offsetGet($offset) | |
| 205 { | |
| 206 return isset($this->response[$offset]) ? $this->response[$offset] : null; | |
| 207 } | |
| 208 | |
| 209 public function __get($name) | |
| 210 { | |
| 211 $accessible = array('responseText'=>1,'headers'=>1,'code'=>1); | |
| 212 $this->responseText = $this->__resp->data; | |
| 213 $this->headers = $this->__resp->headers; | |
| 214 $this->code = $this->__resp->code; | |
| 215 if(isset($accessible[$name]) && $accessible[$name]) | |
| 216 return $this->$name; | |
| 217 elseif(($this->code < 200 || $this->code >= 400) && !isset($accessible[$name])) | |
| 218 EpiTwitterException::raise($this->__resp, $this->debug); | |
| 219 | |
| 220 // Call appears ok so we can fill in the response | |
| 221 $this->response = json_decode($this->responseText, 1); | |
| 222 $this->__obj = json_decode($this->responseText); | |
| 223 | |
| 224 if(gettype($this->__obj) === 'object') | |
| 225 { | |
| 226 foreach($this->__obj as $k => $v) | |
| 227 { | |
| 228 $this->$k = $v; | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 if (property_exists($this, $name)) { | |
| 233 return $this->$name; | |
| 234 } | |
| 235 return null; | |
| 236 } | |
| 237 | |
| 238 public function __isset($name) | |
| 239 { | |
| 240 $value = self::__get($name); | |
| 241 return !empty($name); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 class EpiTwitterException extends Exception | |
| 246 { | |
| 247 public static function raise($response, $debug) | |
| 248 { | |
| 249 $message = $response->data; | |
| 250 | |
| 251 switch($response->code) | |
| 252 { | |
| 253 case 400: | |
| 254 throw new EpiTwitterBadRequestException($message, $response->code); | |
| 255 case 401: | |
| 256 throw new EpiTwitterNotAuthorizedException($message, $response->code); | |
| 257 case 403: | |
| 258 throw new EpiTwitterForbiddenException($message, $response->code); | |
| 259 case 404: | |
| 260 throw new EpiTwitterNotFoundException($message, $response->code); | |
| 261 case 406: | |
| 262 throw new EpiTwitterNotAcceptableException($message, $response->code); | |
| 263 case 420: | |
| 264 throw new EpiTwitterEnhanceYourCalmException($message, $response->code); | |
| 265 case 500: | |
| 266 throw new EpiTwitterInternalServerException($message, $response->code); | |
| 267 case 502: | |
| 268 throw new EpiTwitterBadGatewayException($message, $response->code); | |
| 269 case 503: | |
| 270 throw new EpiTwitterServiceUnavailableException($message, $response->code); | |
| 271 default: | |
| 272 throw new EpiTwitterException($message, $response->code); | |
| 273 } | |
| 274 } | |
| 275 } | |
| 276 class EpiTwitterBadRequestException extends EpiTwitterException{} | |
| 277 class EpiTwitterNotAuthorizedException extends EpiTwitterException{} | |
| 278 class EpiTwitterForbiddenException extends EpiTwitterException{} | |
| 279 class EpiTwitterNotFoundException extends EpiTwitterException{} | |
| 280 class EpiTwitterNotAcceptableException extends EpiTwitterException{} | |
| 281 class EpiTwitterEnhanceYourCalmException extends EpiTwitterException{} | |
| 282 class EpiTwitterInternalServerException extends EpiTwitterException{} | |
| 283 class EpiTwitterBadGatewayException extends EpiTwitterException{} | |
| 284 class EpiTwitterServiceUnavailableException extends EpiTwitterException{} | |
| 285 |
