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 |