GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( ba570a...c6f9e6 )
by Abraham
03:27 queued 02:15
created

TwitterOAuth::useCAFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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
22
    /** @var Response details about the result of the last request */
23
    private $response;
24
    /** @var string|null Application bearer token */
25
    private $bearer;
26
    /** @var Consumer Twitter application details */
27
    private $consumer;
28
    /** @var Token|null User access token details */
29
    private $token;
30
    /** @var HmacSha1 OAuth 1 signature type used by Twitter */
31
    private $signatureMethod;
32
33
    /**
34
     * Constructor
35
     *
36
     * @param string      $consumerKey      The Application Consumer Key
37
     * @param string      $consumerSecret   The Application Consumer Secret
38
     * @param string|null $oauthToken       The Client Token (optional)
39
     * @param string|null $oauthTokenSecret The Client Token Secret (optional)
40
     */
41
    public function __construct($consumerKey, $consumerSecret, $oauthToken = null, $oauthTokenSecret = null)
42
    {
43
        $this->resetLastResponse();
44
        $this->signatureMethod = new HmacSha1();
45
        $this->consumer = new Consumer($consumerKey, $consumerSecret);
46
        if (!empty($oauthToken) && !empty($oauthTokenSecret)) {
47
            $this->token = new Token($oauthToken, $oauthTokenSecret);
48
        }
49
        if (empty($oauthToken) && !empty($oauthTokenSecret)) {
50
            $this->bearer = $oauthTokenSecret;
51
        }
52
    }
53
54
    /**
55
     * @param string $oauthToken
56
     * @param string $oauthTokenSecret
57
     */
58
    public function setOauthToken($oauthToken, $oauthTokenSecret)
59
    {
60
        $this->token = new Token($oauthToken, $oauthTokenSecret);
61
    }
62
63
    /**
64
     * @return string|null
65
     */
66
    public function getLastApiPath()
67
    {
68
        return $this->response->getApiPath();
69
    }
70
71
    /**
72
     * @return int
73
     */
74
    public function getLastHttpCode()
75
    {
76
        return $this->response->getHttpCode();
77
    }
78
79
    /**
80
     * @return array
81
     */
82
    public function getLastXHeaders()
83
    {
84
        return $this->response->getXHeaders();
85
    }
86
87
    /**
88
     * @return array|object|null
89
     */
90
    public function getLastBody()
91
    {
92
        return $this->response->getBody();
93
    }
94
95
    /**
96
     * Resets the last response cache.
97
     */
98
    public function resetLastResponse()
99
    {
100
        $this->response = new Response();
101
    }
102
103
    /**
104
     * Make URLs for user browser navigation.
105
     *
106
     * @param string $path
107
     * @param array  $parameters
108
     *
109
     * @return string
110
     */
111
    public function url($path, array $parameters)
112
    {
113
        $this->resetLastResponse();
114
        $this->response->setApiPath($path);
115
        $query = http_build_query($parameters);
116
        return sprintf('%s/%s?%s', self::API_HOST, $path, $query);
117
    }
118
119
    /**
120
     * Make /oauth/* requests to the API.
121
     *
122
     * @param string $path
123
     * @param array  $parameters
124
     *
125
     * @return array
126
     * @throws TwitterOAuthException
127
     */
128
    public function oauth($path, array $parameters = [])
129
    {
130
        $response = [];
131
        $this->resetLastResponse();
132
        $this->response->setApiPath($path);
133
        $url = sprintf('%s/%s', self::API_HOST, $path);
134
        $result = $this->oAuthRequest($url, 'POST', $parameters);
135
136
        if ($this->getLastHttpCode() != 200) {
137
            throw new TwitterOAuthException($result);
138
        }
139
140
        parse_str($result, $response);
141
        $this->response->setBody($response);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type null; however, Abraham\TwitterOAuth\Response::setBody() does only seem to accept array|object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
142
143
        return $response;
144
    }
145
146
    /**
147
     * Make /oauth2/* requests to the API.
148
     *
149
     * @param string $path
150
     * @param array  $parameters
151
     *
152
     * @return array|object
153
     */
154
    public function oauth2($path, array $parameters = [])
155
    {
156
        $method = 'POST';
157
        $this->resetLastResponse();
158
        $this->response->setApiPath($path);
159
        $url = sprintf('%s/%s', self::API_HOST, $path);
160
        $request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
161
        $authorization = 'Authorization: Basic ' . $this->encodeAppAuthorization($this->consumer);
162
        $result = $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters);
163
        $response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
164
        $this->response->setBody($response);
165
        return $response;
166
    }
167
168
    /**
169
     * Make GET requests to the API.
170
     *
171
     * @param string $path
172
     * @param array  $parameters
173
     *
174
     * @return array|object
175
     */
176
    public function get($path, array $parameters = [])
177
    {
178
        return $this->http('GET', self::API_HOST, $path, $parameters);
179
    }
180
181
    /**
182
     * Make POST requests to the API.
183
     *
184
     * @param string $path
185
     * @param array  $parameters
186
     *
187
     * @return array|object
188
     */
189
    public function post($path, array $parameters = [])
190
    {
191
        return $this->http('POST', self::API_HOST, $path, $parameters);
192
    }
193
194
    /**
195
     * Make DELETE requests to the API.
196
     *
197
     * @param string $path
198
     * @param array  $parameters
199
     *
200
     * @return array|object
201
     */
202
    public function delete($path, array $parameters = [])
203
    {
204
        return $this->http('DELETE', self::API_HOST, $path, $parameters);
205
    }
206
207
    /**
208
     * Make PUT requests to the API.
209
     *
210
     * @param string $path
211
     * @param array  $parameters
212
     *
213
     * @return array|object
214
     */
215
    public function put($path, array $parameters = [])
216
    {
217
        return $this->http('PUT', self::API_HOST, $path, $parameters);
218
    }
219
220
    /**
221
     * Upload media to upload.twitter.com.
222
     *
223
     * @param string $path
224
     * @param array  $parameters
225
     * @param boolean  $chunked
226
     *
227
     * @return array|object
228
     */
229
    public function upload($path, array $parameters = [], $chunked = false)
230
    {
231
        if ($chunked) {
232
            return $this->uploadMediaChunked($path, $parameters);
233
        } else {
234
            return $this->uploadMediaNotChunked($path, $parameters);
235
        }
236
    }
237
238
    /**
239
     * Private method to upload media (not chunked) to upload.twitter.com.
240
     *
241
     * @param string $path
242
     * @param array  $parameters
243
     *
244
     * @return array|object
245
     */
246
    private function uploadMediaNotChunked($path, array $parameters)
247
    {
248
        $file = file_get_contents($parameters['media']);
249
        $base = base64_encode($file);
250
        $parameters['media'] = $base;
251
        return $this->http('POST', self::UPLOAD_HOST, $path, $parameters);
252
    }
253
254
    /**
255
     * Private method to upload media (chunked) to upload.twitter.com.
256
     *
257
     * @param string $path
258
     * @param array  $parameters
259
     *
260
     * @return array|object
261
     */
262
    private function uploadMediaChunked($path, array $parameters)
263
    {
264
        $init = $this->http('POST', self::UPLOAD_HOST, $path, $this->mediaInitParameters($parameters));
265
        // Append
266
        $segmentIndex = 0;
267
        $media = fopen($parameters['media'], 'rb');
268
        while (!feof($media))
269
        {
270
            $this->http('POST', self::UPLOAD_HOST, 'media/upload', [
271
                'command' => 'APPEND',
272
                'media_id' => $init->media_id_string,
273
                'segment_index' => $segmentIndex++,
274
                'media_data' => base64_encode(fread($media, $this->chunkSize))
275
            ]);
276
        }
277
        fclose($media);
278
        // Finalize
279
        $finalize = $this->http('POST', self::UPLOAD_HOST, 'media/upload', [
280
            'command' => 'FINALIZE',
281
            'media_id' => $init->media_id_string
282
        ]);
283
        return $finalize;
284
    }
285
286
    /**
287
     * Private method to get params for upload media chunked init.
288
     * Twitter docs: https://dev.twitter.com/rest/reference/post/media/upload-init.html
289
     *
290
     * @param array  $parameters
291
     *
292
     * @return array
293
     */
294
    private function mediaInitParameters(array $parameters)
295
    {
296
        $return = [
297
            'command' => 'INIT',
298
            'media_type' => $parameters['media_type'],
299
            'total_bytes' => filesize($parameters['media'])
300
        ];
301
        if (isset($parameters['additional_owners'])) {
302
            $return['additional_owners'] = $parameters['additional_owners'];
303
        }
304
        if (isset($parameters['media_category'])) {
305
            $return['media_category'] = $parameters['media_category'];
306
        }
307
        return $return;
308
    }
309
310
    /**
311
     * @param string $method
312
     * @param string $host
313
     * @param string $path
314
     * @param array  $parameters
315
     *
316
     * @return array|object
317
     */
318
    private function http($method, $host, $path, array $parameters)
319
    {
320
        $this->resetLastResponse();
321
        $url = sprintf('%s/%s/%s.json', $host, self::API_VERSION, $path);
322
        $this->response->setApiPath($path);
323
        $result = $this->oAuthRequest($url, $method, $parameters);
324
        $response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
325
        $this->response->setBody($response);
326
        return $response;
327
    }
328
329
    /**
330
     * Format and sign an OAuth / API request
331
     *
332
     * @param string $url
333
     * @param string $method
334
     * @param array  $parameters
335
     *
336
     * @return string
337
     * @throws TwitterOAuthException
338
     */
339
    private function oAuthRequest($url, $method, array $parameters)
340
    {
341
        $request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
342
        if (array_key_exists('oauth_callback', $parameters)) {
343
            // Twitter doesn't like oauth_callback as a parameter.
344
            unset($parameters['oauth_callback']);
345
        }
346
        if ($this->bearer === null) {
347
            $request->signRequest($this->signatureMethod, $this->consumer, $this->token);
348
            $authorization = $request->toHeader();
349
            if (array_key_exists('oauth_verifier', $parameters)) {
350
                // Twitter doesn't always work with oauth in the body and in the header
351
                // and it's already included in the $authorization header
352
                unset($parameters['oauth_verifier']);
353
            }
354
        } else {
355
            $authorization = 'Authorization: Bearer ' . $this->bearer;
356
        }
357
        return $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters);
358
    }
359
360
    /**
361
     * Set Curl options.
362
     *
363
     * @return array
364
     */
365
    private function curlOptions()
366
    {
367
        $options = [
368
            // CURLOPT_VERBOSE => true,
369
            CURLOPT_CONNECTTIMEOUT => $this->connectionTimeout,
370
            CURLOPT_HEADER => true,
371
            CURLOPT_RETURNTRANSFER => true,
372
            CURLOPT_SSL_VERIFYHOST => 2,
373
            CURLOPT_SSL_VERIFYPEER => true,
374
            CURLOPT_TIMEOUT => $this->timeout,
375
            CURLOPT_USERAGENT => $this->userAgent,
376
        ];
377
378
        if ($this->useCAFile()) {
379
            $options[CURLOPT_CAINFO] = __DIR__ . DIRECTORY_SEPARATOR . 'cacert.pem';
380
        }
381
382
        if($this->gzipEncoding) {
383
            $options[CURLOPT_ENCODING] = 'gzip';
384
        }
385
386
        if (!empty($this->proxy)) {
387
            $options[CURLOPT_PROXY] = $this->proxy['CURLOPT_PROXY'];
388
            $options[CURLOPT_PROXYUSERPWD] = $this->proxy['CURLOPT_PROXYUSERPWD'];
389
            $options[CURLOPT_PROXYPORT] = $this->proxy['CURLOPT_PROXYPORT'];
390
            $options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC;
391
            $options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
392
        }
393
394
        return $options;
395
    }
396
397
    /**
398
     * Make an HTTP request
399
     *
400
     * @param string $url
401
     * @param string $method
402
     * @param string $authorization
403
     * @param array $postfields
404
     *
405
     * @return string
406
     * @throws TwitterOAuthException
407
     */
408
    private function request($url, $method, $authorization, array $postfields)
409
    {
410
        $options = $this->curlOptions($url, $authorization);
0 ignored issues
show
Unused Code introduced by
The call to TwitterOAuth::curlOptions() has too many arguments starting with $url.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
411
        $options[CURLOPT_URL] = $url;
412
        $options[CURLOPT_HTTPHEADER] = ['Accept: application/json', $authorization, 'Expect:'];
413
414
        switch ($method) {
415
            case 'GET':
416
                break;
417
            case 'POST':
418
                $options[CURLOPT_POST] = true;
419
                $options[CURLOPT_POSTFIELDS] = Util::buildHttpQuery($postfields);
420
                break;
421
            case 'DELETE':
422
                $options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
423
                break;
424
            case 'PUT':
425
                $options[CURLOPT_CUSTOMREQUEST] = 'PUT';
426
                break;
427
        }
428
429
        if (in_array($method, ['GET', 'PUT', 'DELETE']) && !empty($postfields)) {
430
            $options[CURLOPT_URL] .= '?' . Util::buildHttpQuery($postfields);
431
        }
432
433
434
        $curlHandle = curl_init();
435
        curl_setopt_array($curlHandle, $options);
436
        $response = curl_exec($curlHandle);
437
438
        // Throw exceptions on cURL errors.
439
        if (curl_errno($curlHandle) > 0) {
440
            throw new TwitterOAuthException(curl_error($curlHandle), curl_errno($curlHandle));
441
        }
442
443
        $this->response->setHttpCode(curl_getinfo($curlHandle, CURLINFO_HTTP_CODE));
444
        $parts = explode("\r\n\r\n", $response);
445
        $responseBody = array_pop($parts);
446
        $responseHeader = array_pop($parts);
447
        $this->response->setHeaders($this->parseHeaders($responseHeader));
448
449
        curl_close($curlHandle);
450
451
        return $responseBody;
452
    }
453
454
    /**
455
     * Get the header info to store.
456
     *
457
     * @param string $header
458
     *
459
     * @return array
460
     */
461
    private function parseHeaders($header)
462
    {
463
        $headers = [];
464
        foreach (explode("\r\n", $header) as $line) {
465
            if (strpos($line, ':') !== false) {
466
                list ($key, $value) = explode(': ', $line);
467
                $key = str_replace('-', '_', strtolower($key));
468
                $headers[$key] = trim($value);
469
            }
470
        }
471
        return $headers;
472
    }
473
474
    /**
475
     * Encode application authorization header with base64.
476
     *
477
     * @param Consumer $consumer
478
     *
479
     * @return string
480
     */
481
    private function encodeAppAuthorization(Consumer $consumer)
482
    {
483
        $key = rawurlencode($consumer->key);
484
        $secret = rawurlencode($consumer->secret);
485
        return base64_encode($key . ':' . $secret);
486
    }
487
488
    /**
489
     * Is the code running from a Phar module.
490
     *
491
     * @return boolean
492
     */
493
    private function pharRunning()
494
    {
495
        return class_exists('Phar') && \Phar::running(false) !== '';
496
    }
497
498
    /**
499
     * Use included CA file instead of OS provided list.
500
     *
501
     * @return boolean
502
     */
503
    private function useCAFile()
504
    {
505
        /* Use CACert file when not in a PHAR file. */
506
        return !$this->pharRunning();
507
    }
508
}
509