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