comparison default/assets/vendors/theme-widgets/vendor/abraham/twitteroauth/src/TwitterOAuth.php @ 0:1d038bc9b3d2 default tip

Up:default
author Liny <dev@neowd.com>
date Sat, 31 May 2025 09:21:51 +0800
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1d038bc9b3d2
1 <?php
2 /**
3 * The most popular PHP library for use with the Twitter OAuth REST API.
4 *
5 * @license MIT
6 */
7 namespace Abraham\TwitterOAuth;
8
9 use Abraham\TwitterOAuth\Util\JsonDecoder;
10
11 /**
12 * TwitterOAuth class for interacting with the Twitter API.
13 *
14 * @author Abraham Williams <[email protected]>
15 */
16 class TwitterOAuth extends Config
17 {
18 const API_VERSION = '1.1';
19 const API_HOST = 'https://api.twitter.com';
20 const UPLOAD_HOST = 'https://upload.twitter.com';
21 const UPLOAD_CHUNK = 40960; // 1024 * 40
22
23 /** @var Response details about the result of the last request */
24 private $response;
25 /** @var string|null Application bearer token */
26 private $bearer;
27 /** @var Consumer Twitter application details */
28 private $consumer;
29 /** @var Token|null User access token details */
30 private $token;
31 /** @var HmacSha1 OAuth 1 signature type used by Twitter */
32 private $signatureMethod;
33
34 /**
35 * Constructor
36 *
37 * @param string $consumerKey The Application Consumer Key
38 * @param string $consumerSecret The Application Consumer Secret
39 * @param string|null $oauthToken The Client Token (optional)
40 * @param string|null $oauthTokenSecret The Client Token Secret (optional)
41 */
42 public function __construct($consumerKey, $consumerSecret, $oauthToken = null, $oauthTokenSecret = null)
43 {
44 $this->resetLastResponse();
45 $this->signatureMethod = new HmacSha1();
46 $this->consumer = new Consumer($consumerKey, $consumerSecret);
47 if (!empty($oauthToken) && !empty($oauthTokenSecret)) {
48 $this->token = new Token($oauthToken, $oauthTokenSecret);
49 }
50 if (empty($oauthToken) && !empty($oauthTokenSecret)) {
51 $this->bearer = $oauthTokenSecret;
52 }
53 }
54
55 /**
56 * @param string $oauthToken
57 * @param string $oauthTokenSecret
58 */
59 public function setOauthToken($oauthToken, $oauthTokenSecret)
60 {
61 $this->token = new Token($oauthToken, $oauthTokenSecret);
62 }
63
64 /**
65 * @return string|null
66 */
67 public function getLastApiPath()
68 {
69 return $this->response->getApiPath();
70 }
71
72 /**
73 * @return int
74 */
75 public function getLastHttpCode()
76 {
77 return $this->response->getHttpCode();
78 }
79
80 /**
81 * @return array
82 */
83 public function getLastXHeaders()
84 {
85 return $this->response->getXHeaders();
86 }
87
88 /**
89 * @return array|object|null
90 */
91 public function getLastBody()
92 {
93 return $this->response->getBody();
94 }
95
96 /**
97 * Resets the last response cache.
98 */
99 public function resetLastResponse()
100 {
101 $this->response = new Response();
102 }
103
104 /**
105 * Make URLs for user browser navigation.
106 *
107 * @param string $path
108 * @param array $parameters
109 *
110 * @return string
111 */
112 public function url($path, array $parameters)
113 {
114 $this->resetLastResponse();
115 $this->response->setApiPath($path);
116 $query = http_build_query($parameters);
117 return sprintf('%s/%s?%s', self::API_HOST, $path, $query);
118 }
119
120 /**
121 * Make /oauth/* requests to the API.
122 *
123 * @param string $path
124 * @param array $parameters
125 *
126 * @return array
127 * @throws TwitterOAuthException
128 */
129 public function oauth($path, array $parameters = [])
130 {
131 $response = [];
132 $this->resetLastResponse();
133 $this->response->setApiPath($path);
134 $url = sprintf('%s/%s', self::API_HOST, $path);
135 $result = $this->oAuthRequest($url, 'POST', $parameters);
136
137 if ($this->getLastHttpCode() != 200) {
138 throw new TwitterOAuthException($result);
139 }
140
141 parse_str($result, $response);
142 $this->response->setBody($response);
143
144 return $response;
145 }
146
147 /**
148 * Make /oauth2/* requests to the API.
149 *
150 * @param string $path
151 * @param array $parameters
152 *
153 * @return array|object
154 */
155 public function oauth2($path, array $parameters = [])
156 {
157 $method = 'POST';
158 $this->resetLastResponse();
159 $this->response->setApiPath($path);
160 $url = sprintf('%s/%s', self::API_HOST, $path);
161 $request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
162 $authorization = 'Authorization: Basic ' . $this->encodeAppAuthorization($this->consumer);
163 $result = $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters);
164 $response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
165 $this->response->setBody($response);
166 return $response;
167 }
168
169 /**
170 * Make GET requests to the API.
171 *
172 * @param string $path
173 * @param array $parameters
174 *
175 * @return array|object
176 */
177 public function get($path, array $parameters = [])
178 {
179 return $this->http('GET', self::API_HOST, $path, $parameters);
180 }
181
182 /**
183 * Make POST requests to the API.
184 *
185 * @param string $path
186 * @param array $parameters
187 *
188 * @return array|object
189 */
190 public function post($path, array $parameters = [])
191 {
192 return $this->http('POST', self::API_HOST, $path, $parameters);
193 }
194
195 /**
196 * Make DELETE requests to the API.
197 *
198 * @param string $path
199 * @param array $parameters
200 *
201 * @return array|object
202 */
203 public function delete($path, array $parameters = [])
204 {
205 return $this->http('DELETE', self::API_HOST, $path, $parameters);
206 }
207
208 /**
209 * Make PUT requests to the API.
210 *
211 * @param string $path
212 * @param array $parameters
213 *
214 * @return array|object
215 */
216 public function put($path, array $parameters = [])
217 {
218 return $this->http('PUT', self::API_HOST, $path, $parameters);
219 }
220
221 /**
222 * Upload media to upload.twitter.com.
223 *
224 * @param string $path
225 * @param array $parameters
226 * @param boolean $chunked
227 *
228 * @return array|object
229 */
230 public function upload($path, array $parameters = [], $chunked = false)
231 {
232 if ($chunked) {
233 return $this->uploadMediaChunked($path, $parameters);
234 } else {
235 return $this->uploadMediaNotChunked($path, $parameters);
236 }
237 }
238
239 /**
240 * Private method to upload media (not chunked) to upload.twitter.com.
241 *
242 * @param string $path
243 * @param array $parameters
244 *
245 * @return array|object
246 */
247 private function uploadMediaNotChunked($path, array $parameters)
248 {
249 $file = file_get_contents($parameters['media']);
250 $base = base64_encode($file);
251 $parameters['media'] = $base;
252 return $this->http('POST', self::UPLOAD_HOST, $path, $parameters);
253 }
254
255 /**
256 * Private method to upload media (chunked) to upload.twitter.com.
257 *
258 * @param string $path
259 * @param array $parameters
260 *
261 * @return array|object
262 */
263 private function uploadMediaChunked($path, array $parameters)
264 {
265 // Init
266 $init = $this->http('POST', self::UPLOAD_HOST, $path, [
267 'command' => 'INIT',
268 'media_type' => $parameters['media_type'],
269 'total_bytes' => filesize($parameters['media'])
270 ]);
271 // Append
272 $segment_index = 0;
273 $media = fopen($parameters['media'], 'rb');
274 while (!feof($media))
275 {
276 $this->http('POST', self::UPLOAD_HOST, 'media/upload', [
277 'command' => 'APPEND',
278 'media_id' => $init->media_id_string,
279 'segment_index' => $segment_index++,
280 'media_data' => base64_encode(fread($media, self::UPLOAD_CHUNK))
281 ]);
282 }
283 fclose($media);
284 // Finalize
285 $finalize = $this->http('POST', self::UPLOAD_HOST, 'media/upload', [
286 'command' => 'FINALIZE',
287 'media_id' => $init->media_id_string
288 ]);
289 return $finalize;
290 }
291
292 /**
293 * @param string $method
294 * @param string $host
295 * @param string $path
296 * @param array $parameters
297 *
298 * @return array|object
299 */
300 private function http($method, $host, $path, array $parameters)
301 {
302 $this->resetLastResponse();
303 $url = sprintf('%s/%s/%s.json', $host, self::API_VERSION, $path);
304 $this->response->setApiPath($path);
305 $result = $this->oAuthRequest($url, $method, $parameters);
306 $response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
307 $this->response->setBody($response);
308 return $response;
309 }
310
311 /**
312 * Format and sign an OAuth / API request
313 *
314 * @param string $url
315 * @param string $method
316 * @param array $parameters
317 *
318 * @return string
319 * @throws TwitterOAuthException
320 */
321 private function oAuthRequest($url, $method, array $parameters)
322 {
323 $request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
324 if (array_key_exists('oauth_callback', $parameters)) {
325 // Twitter doesn't like oauth_callback as a parameter.
326 unset($parameters['oauth_callback']);
327 }
328 if ($this->bearer === null) {
329 $request->signRequest($this->signatureMethod, $this->consumer, $this->token);
330 $authorization = $request->toHeader();
331 } else {
332 $authorization = 'Authorization: Bearer ' . $this->bearer;
333 }
334 return $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters);
335 }
336
337 /**
338 * Make an HTTP request
339 *
340 * @param string $url
341 * @param string $method
342 * @param string $authorization
343 * @param array $postfields
344 *
345 * @return string
346 * @throws TwitterOAuthException
347 */
348 private function request($url, $method, $authorization, array $postfields)
349 {
350 /* Curl settings */
351 $options = [
352 // CURLOPT_VERBOSE => true,
353 CURLOPT_CAINFO => __DIR__ . DIRECTORY_SEPARATOR . 'cacert.pem',
354 CURLOPT_CONNECTTIMEOUT => $this->connectionTimeout,
355 CURLOPT_HEADER => true,
356 CURLOPT_HTTPHEADER => ['Accept: application/json', $authorization, 'Expect:'],
357 CURLOPT_RETURNTRANSFER => true,
358 CURLOPT_SSL_VERIFYHOST => 2,
359 CURLOPT_SSL_VERIFYPEER => true,
360 CURLOPT_TIMEOUT => $this->timeout,
361 CURLOPT_URL => $url,
362 CURLOPT_USERAGENT => $this->userAgent,
363 ];
364
365 /* Remove CACert file when in a PHAR file. */
366 if ($this->pharRunning()) {
367 unset($options[CURLOPT_CAINFO]);
368 }
369
370 if($this->gzipEncoding) {
371 $options[CURLOPT_ENCODING] = 'gzip';
372 }
373
374 if (!empty($this->proxy)) {
375 $options[CURLOPT_PROXY] = $this->proxy['CURLOPT_PROXY'];
376 $options[CURLOPT_PROXYUSERPWD] = $this->proxy['CURLOPT_PROXYUSERPWD'];
377 $options[CURLOPT_PROXYPORT] = $this->proxy['CURLOPT_PROXYPORT'];
378 $options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC;
379 $options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
380 }
381
382 switch ($method) {
383 case 'GET':
384 break;
385 case 'POST':
386 $options[CURLOPT_POST] = true;
387 $options[CURLOPT_POSTFIELDS] = Util::buildHttpQuery($postfields);
388 break;
389 case 'DELETE':
390 $options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
391 break;
392 case 'PUT':
393 $options[CURLOPT_CUSTOMREQUEST] = 'PUT';
394 break;
395 }
396
397 if (in_array($method, ['GET', 'PUT', 'DELETE']) && !empty($postfields)) {
398 $options[CURLOPT_URL] .= '?' . Util::buildHttpQuery($postfields);
399 }
400
401
402 $curlHandle = curl_init();
403 curl_setopt_array($curlHandle, $options);
404 $response = curl_exec($curlHandle);
405
406 // Throw exceptions on cURL errors.
407 if (curl_errno($curlHandle) > 0) {
408 throw new TwitterOAuthException(curl_error($curlHandle), curl_errno($curlHandle));
409 }
410
411 $this->response->setHttpCode(curl_getinfo($curlHandle, CURLINFO_HTTP_CODE));
412 $parts = explode("\r\n\r\n", $response);
413 $responseBody = array_pop($parts);
414 $responseHeader = array_pop($parts);
415 $this->response->setHeaders($this->parseHeaders($responseHeader));
416
417 curl_close($curlHandle);
418
419 return $responseBody;
420 }
421
422 /**
423 * Get the header info to store.
424 *
425 * @param string $header
426 *
427 * @return array
428 */
429 private function parseHeaders($header)
430 {
431 $headers = [];
432 foreach (explode("\r\n", $header) as $line) {
433 if (strpos($line, ':') !== false) {
434 list ($key, $value) = explode(': ', $line);
435 $key = str_replace('-', '_', strtolower($key));
436 $headers[$key] = trim($value);
437 }
438 }
439 return $headers;
440 }
441
442 /**
443 * Encode application authorization header with base64.
444 *
445 * @param Consumer $consumer
446 *
447 * @return string
448 */
449 private function encodeAppAuthorization(Consumer $consumer)
450 {
451 $key = rawurlencode($consumer->key);
452 $secret = rawurlencode($consumer->secret);
453 return base64_encode($key . ':' . $secret);
454 }
455
456 /**
457 * Is the code running from a Phar module.
458 *
459 * @return boolean
460 */
461 private function pharRunning()
462 {
463 return class_exists('Phar') && \Phar::running(false) !== '';
464 }
465 }