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
Pull Request — master (#463)
by
unknown
03:48
created

TwitterOAuth::getLastApiPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
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
    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);
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...
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
     * get image from DM.
184
     *
185
     * @param string $url
186
     *
187
     * @return image data
188
     */
189
    public function getimg($url) {
190
        return $this->http('GET', $url, "", $parameters);
0 ignored issues
show
Bug introduced by
The variable $parameters does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug Best Practice introduced by
The return type of return $this->http('GET', $url, '', $parameters); (array|object|string) is incompatible with the return type documented by Abraham\TwitterOAuth\TwitterOAuth::getimg of type Abraham\TwitterOAuth\image.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
191
    }
192
193
    /**
194
     * Make POST requests to the API.
195
     *
196
     * @param string $path
197
     * @param array  $parameters
198
     *
199
     * @return array|object
200
     */
201
    public function post($path, array $parameters = [])
202
    {
203
        return $this->http('POST', self::API_HOST, $path, $parameters);
204
    }
205
206
    /**
207
     * Make DELETE requests to the API.
208
     *
209
     * @param string $path
210
     * @param array  $parameters
211
     *
212
     * @return array|object
213
     */
214
    public function delete($path, array $parameters = [])
215
    {
216
        return $this->http('DELETE', self::API_HOST, $path, $parameters);
217
    }
218
219
    /**
220
     * Make PUT requests to the API.
221
     *
222
     * @param string $path
223
     * @param array  $parameters
224
     *
225
     * @return array|object
226
     */
227
    public function put($path, array $parameters = [])
228
    {
229
        return $this->http('PUT', self::API_HOST, $path, $parameters);
230
    }
231
232
    /**
233
     * Upload media to upload.twitter.com.
234
     *
235
     * @param string $path
236
     * @param array  $parameters
237
     * @param boolean  $chunked
238
     *
239
     * @return array|object
240
     */
241
    public function upload($path, array $parameters = [], $chunked = false)
242
    {
243
        if ($chunked) {
244
            return $this->uploadMediaChunked($path, $parameters);
245
        } else {
246
            return $this->uploadMediaNotChunked($path, $parameters);
247
        }
248
    }
249
250
    /**
251
     * Private method to upload media (not chunked) to upload.twitter.com.
252
     *
253
     * @param string $path
254
     * @param array  $parameters
255
     *
256
     * @return array|object
257
     */
258
    private function uploadMediaNotChunked($path, $parameters)
259
    {
260
        $file = file_get_contents($parameters['media']);
261
        $base = base64_encode($file);
262
        $parameters['media'] = $base;
263
        return $this->http('POST', self::UPLOAD_HOST, $path, $parameters);
264
    }
265
266
    /**
267
     * Private method to upload media (chunked) to upload.twitter.com.
268
     *
269
     * @param string $path
270
     * @param array  $parameters
271
     *
272
     * @return array|object
273
     */
274
    private function uploadMediaChunked($path, $parameters)
275
    {
276
        // Init
277
        $init = $this->http('POST', self::UPLOAD_HOST, $path, [
278
            'command' => 'INIT',
279
            'media_type' => $parameters['media_type'],
280
            'total_bytes' => filesize($parameters['media'])
281
        ]);
282
        // Append
283
        $segment_index = 0;
284
        $media = fopen($parameters['media'], 'rb');
285
        while (!feof($media))
286
        {
287
            $this->http('POST', self::UPLOAD_HOST, 'media/upload', [
288
                'command' => 'APPEND',
289
                'media_id' => $init->media_id_string,
290
                'segment_index' => $segment_index++,
291
                'media_data' => base64_encode(fread($media, self::UPLOAD_CHUNK))
292
            ]);
293
        }
294
        fclose($media);
295
        // Finalize
296
        $finalize = $this->http('POST', self::UPLOAD_HOST, 'media/upload', [
297
            'command' => 'FINALIZE',
298
            'media_id' => $init->media_id_string
299
        ]);
300
        return $finalize;
301
    }
302
303
    /**
304
     * @param string $method
305
     * @param string $host
306
     * @param string $path
307
     * @param array  $parameters
308
     *
309
     * @return array|object
310
     */
311
    private function http($method, $host, $path, array $parameters)
312
    {
313
        $this->resetLastResponse();
314
        $img = false;
315
        if(strlen($path)==0) {
316
            $url = $host;
317
            $img = true;
318
        } else {
319
            $url = sprintf('%s/%s/%s.json', $host, self::API_VERSION, $path);
320
        }
321
        $this->response->setApiPath($path);
322
        $result = $this->oAuthRequest($url, $method, $parameters);
323
        if($img) {
324
            $response = $result;
325
        } else {
326
            $response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
327
        }
328
        $this->response->setBody($response);
0 ignored issues
show
Bug introduced by
It seems like $response defined by $result on line 324 can also be of type string; 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...
329
        return $response;
330
    }
331
332
    /**
333
     * Format and sign an OAuth / API request
334
     *
335
     * @param string $url
336
     * @param string $method
337
     * @param array  $parameters
338
     *
339
     * @return string
340
     * @throws TwitterOAuthException
341
     */
342
    private function oAuthRequest($url, $method, array $parameters)
343
    {
344
        $request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
345
        if (array_key_exists('oauth_callback', $parameters)) {
346
            // Twitter doesn't like oauth_callback as a parameter.
347
            unset($parameters['oauth_callback']);
348
        }
349
        if ($this->bearer === null) {
350
            $request->signRequest($this->signatureMethod, $this->consumer, $this->token);
351
            $authorization = $request->toHeader();
352
        } else {
353
            $authorization = 'Authorization: Bearer ' . $this->bearer;
354
        }
355
        return $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters);
356
    }
357
358
    /**
359
     * Make an HTTP request
360
     *
361
     * @param string $url
362
     * @param string $method
363
     * @param string $authorization
364
     * @param array $postfields
365
     *
366
     * @return string
367
     * @throws TwitterOAuthException
368
     */
369
    private function request($url, $method, $authorization, $postfields)
370
    {
371
        /* Curl settings */
372
        $options = [
373
            // CURLOPT_VERBOSE => true,
374
            CURLOPT_CAINFO => __DIR__ . DIRECTORY_SEPARATOR . 'cacert.pem',
375
            CURLOPT_CONNECTTIMEOUT => $this->connectionTimeout,
376
            CURLOPT_HEADER => true,
377
            CURLOPT_HTTPHEADER => ['Accept: application/json', $authorization, 'Expect:'],
378
            CURLOPT_RETURNTRANSFER => true,
379
            CURLOPT_SSL_VERIFYHOST => 2,
380
            CURLOPT_SSL_VERIFYPEER => true,
381
            CURLOPT_TIMEOUT => $this->timeout,
382
            CURLOPT_URL => $url,
383
            CURLOPT_USERAGENT => $this->userAgent,
384
        ];
385
386
        if($this->gzipEncoding) {
387
            $options[CURLOPT_ENCODING] = 'gzip';
388
        }
389
390
        if (!empty($this->proxy)) {
391
            $options[CURLOPT_PROXY] = $this->proxy['CURLOPT_PROXY'];
392
            $options[CURLOPT_PROXYUSERPWD] = $this->proxy['CURLOPT_PROXYUSERPWD'];
393
            $options[CURLOPT_PROXYPORT] = $this->proxy['CURLOPT_PROXYPORT'];
394
            $options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC;
395
            $options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
396
        }
397
398
        switch ($method) {
399
            case 'GET':
400
                break;
401
            case 'POST':
402
                $options[CURLOPT_POST] = true;
403
                $options[CURLOPT_POSTFIELDS] = Util::buildHttpQuery($postfields);
404
                break;
405
            case 'DELETE':
406
                $options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
407
                break;
408
            case 'PUT':
409
                $options[CURLOPT_CUSTOMREQUEST] = 'PUT';
410
                break;
411
        }
412
413
        if (in_array($method, ['GET', 'PUT', 'DELETE']) && !empty($postfields)) {
414
            $options[CURLOPT_URL] .= '?' . Util::buildHttpQuery($postfields);
415
        }
416
417
418
        $curlHandle = curl_init();
419
        curl_setopt_array($curlHandle, $options);
420
        $response = curl_exec($curlHandle);
421
422
        // Throw exceptions on cURL errors.
423
        if (curl_errno($curlHandle) > 0) {
424
            throw new TwitterOAuthException(curl_error($curlHandle), curl_errno($curlHandle));
425
        }
426
427
        $this->response->setHttpCode(curl_getinfo($curlHandle, CURLINFO_HTTP_CODE));
428
        $parts = explode("\r\n\r\n", $response);
429
        $responseBody = array_pop($parts);
430
        $responseHeader = array_pop($parts);
431
        $this->response->setHeaders($this->parseHeaders($responseHeader));
432
433
        curl_close($curlHandle);
434
435
        return $responseBody;
436
    }
437
438
    /**
439
     * Get the header info to store.
440
     *
441
     * @param string $header
442
     *
443
     * @return array
444
     */
445
    private function parseHeaders($header)
446
    {
447
        $headers = [];
448
        foreach (explode("\r\n", $header) as $line) {
449
            if (strpos($line, ':') !== false) {
450
                list ($key, $value) = explode(': ', $line);
451
                $key = str_replace('-', '_', strtolower($key));
452
                $headers[$key] = trim($value);
453
            }
454
        }
455
        return $headers;
456
    }
457
458
    /**
459
     * Encode application authorization header with base64.
460
     *
461
     * @param Consumer $consumer
462
     *
463
     * @return string
464
     */
465
    private function encodeAppAuthorization($consumer)
466
    {
467
        // TODO: key and secret should be rfc 1738 encoded
468
        $key = $consumer->key;
469
        $secret = $consumer->secret;
470
        return base64_encode($key . ':' . $secret);
471
    }
472
}
473