Completed
Pull Request — master (#28)
by
unknown
05:08
created

TwitterAds::upload()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 5
nc 2
nop 2
1
<?php
2
/**
3
 * A Twitter supported and maintained Ads API SDK for PHP.
4
 *
5
 * @license MIT
6
 */
7
namespace Hborras\TwitterAdsSDK;
8
9
use Exception;
10
use Hborras\TwitterAdsSDK\TwitterAds\Account;
11
use Hborras\TwitterAdsSDK;
12
use Hborras\TwitterAdsSDK\TwitterAds\Cursor;
13
use Hborras\TwitterAdsSDK\TwitterAds\Errors\BadRequest;
14
use Hborras\TwitterAdsSDK\TwitterAds\Errors\Forbidden;
15
use Hborras\TwitterAdsSDK\TwitterAds\Errors\NotAuthorized;
16
use Hborras\TwitterAdsSDK\TwitterAds\Errors\NotFound;
17
use Hborras\TwitterAdsSDK\TwitterAds\Errors\RateLimit;
18
use Hborras\TwitterAdsSDK\TwitterAds\Errors\ServerError;
19
use Hborras\TwitterAdsSDK\TwitterAds\Errors\ServiceUnavailable;
20
use Hborras\TwitterAdsSDK\Util\JsonDecoder;
21
use GuzzleHttp;
22
23
/**
24
 * TwitterAds class for interacting with the Twitter API.
25
 *
26
 * @author Hector Borras <[email protected]>
27
 */
28
class TwitterAds extends Config
29
{
30
    const API_VERSION      = '1';
31
    const API_REST_VERSION = '1.1';
32
    const API_HOST         = 'https://ads-api.twitter.com';
33
    const API_HOST_SANDBOX = 'https://ads-api-sandbox.twitter.com';
34
    const API_HOST_OAUTH   = 'https://api.twitter.com';
35
    const UPLOAD_HOST      = 'https://upload.twitter.com';
36
    const UPLOAD_PATH      = 'media/upload.json';
37
    const UPLOAD_CHUNK     = 40960; // 1024 * 40
38
39
    /** @var  string Method used for the request */
40
    private $method;
41
    /** @var  string Resource used for the request */
42
    private $resource;
43
    /** @var Response details about the result of the last request */
44
    private $response;
45
    /** @var string|null Application bearer token */
46
    private $bearer;
47
    /** @var Consumer Twitter application details */
48
    private $consumer;
49
    /** @var Token|null User access token details */
50
    private $token;
51
    /** @var HmacSha1 OAuth 1 signature type used by Twitter */
52
    private $signatureMethod;
53
    /** @var  bool Sandbox allows to make requests thought sandbox environment */
54
    private $sandbox;
55
56
    /**
57
     * Constructor.
58
     *
59
     * @param string $consumerKey The Application Consumer Key
60
     * @param string $consumerSecret The Application Consumer Secret
61
     * @param string|null $oauthToken The Client Token (optional)
62
     * @param string|null $oauthTokenSecret The Client Token Secret (optional)
63
     * @param bool $sandbox The Sandbox environment (optional)
64
     */
65
    public function __construct($consumerKey, $consumerSecret, $oauthToken = null, $oauthTokenSecret = null, $sandbox = false)
66
    {
67
        $this->resetLastResponse();
68
        $this->signatureMethod = new HmacSha1();
69
        $this->consumer = new Consumer($consumerKey, $consumerSecret);
70
        if (!empty($oauthToken) && !empty($oauthTokenSecret)) {
71
            $this->token = new Token($oauthToken, $oauthTokenSecret);
72
        }
73
        if (empty($oauthToken) && !empty($oauthTokenSecret)) {
74
            $this->bearer = $oauthTokenSecret;
75
        }
76
        $this->sandbox = $sandbox;
77
    }
78
79
    /**
80
     * Set API URLS.
81
     */
82
    public function accessTokenURL()
83
    {
84
        return 'https://api.twitter.com/oauth/access_token';
85
    }
86
87
    public function authenticateURL()
88
    {
89
        return 'https://api.twitter.com/oauth/authenticate';
90
    }
91
92
    public function authorizeURL()
93
    {
94
        return 'https://api.twitter.com/oauth/authorize';
95
    }
96
97
    public function requestTokenURL()
98
    {
99
        return 'https://api.twitter.com/oauth/request_token';
100
    }
101
102
    /**
103
     * @param string $accountId
104
     *
105
     * @return Account|Cursor
106
     */
107
    public function getAccounts($accountId = '')
108
    {
109
        $account = new Account($this);
110
111
        return $accountId ? $account->load($accountId) : $account->all();
112
    }
113
114
    /**
115
     * @param string $oauthToken
116
     * @param string $oauthTokenSecret
117
     */
118
    public function setOauthToken($oauthToken, $oauthTokenSecret)
119
    {
120
        $this->token = new Token($oauthToken, $oauthTokenSecret);
121
    }
122
123
    /**
124
     * @return string|null
125
     */
126
    public function getLastApiPath()
127
    {
128
        return $this->response->getApiPath();
129
    }
130
131
    /**
132
     * @return int
133
     */
134
    public function getLastHttpCode()
135
    {
136
        return $this->response->getHttpCode();
137
    }
138
139
    /**
140
     * @return array
141
     */
142
    public function getLastXHeaders()
143
    {
144
        return $this->response->getXHeaders();
145
    }
146
147
    /**
148
     * @return array|object|null
149
     */
150
    public function getLastBody()
151
    {
152
        return $this->response->getBody();
153
    }
154
155
    /**
156
     * Resets the last response cache.
157
     */
158
    public function resetLastResponse()
159
    {
160
        $this->response = new Response();
161
    }
162
163
    /**
164
     * Make URLs for user browser navigation.
165
     *
166
     * @param string $path
167
     * @param array $parameters
168
     *
169
     * @return string
170
     */
171
    public function url($path, array $parameters)
172
    {
173
        $this->resetLastResponse();
174
        $this->response->setApiPath($path);
175
        $query = http_build_query($parameters);
176
177
        return sprintf('%s/%s?%s', self::API_HOST_OAUTH, $path, $query);
178
    }
179
180
    /**
181
     * Make /oauth/* requests to the API.
182
     *
183
     * @param string $path
184
     * @param array $parameters
185
     * @return array
186
     * @throws Exception
187
     */
188
    public function oauth($path, array $parameters = [])
189
    {
190
        $response = [];
191
        $this->resetLastResponse();
192
        $this->response->setApiPath($path);
193
        $url = sprintf('%s/%s', self::API_HOST_OAUTH, $path);
194
        $result = $this->oAuthRequest($url, 'POST', $parameters);
195
196
        if ($this->getLastHttpCode() != 200) {
197
            throw new TwitterAdsException($result, 500, null, $result);
198
        }
199
200
        parse_str($result, $response);
201
        $this->response->setBody($response);
202
203
        return $response;
204
    }
205
206
    /**
207
     * Make /oauth2/* requests to the API.
208
     *
209
     * @param string $path
210
     * @param array $parameters
211
     *
212
     * @return array|object
213
     */
214
    public function oauth2($path, array $parameters = [])
215
    {
216
        $method = 'POST';
217
        $this->resetLastResponse();
218
        $this->response->setApiPath($path);
219
        $url = sprintf('%s/%s', self::API_HOST_OAUTH, $path);
220
        $request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
221
        $authorization = 'Authorization: Basic '.$this->encodeAppAuthorization($this->consumer);
222
        $result = $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters);
223
        $response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
224
        $this->response->setBody($response);
225
226
        return $response;
227
    }
228
229
    public function verifyCredentials($parameters = [])
230
    {
231
        return $this->http('GET', self::API_HOST_OAUTH, 'account/verify_credentials', $parameters);
232
    }
233
234
    /**
235
     * Make GET requests to the API.
236
     *
237
     * @param string $path
238
     * @param array $parameters
239
     *
240
     * @return Response
241
     */
242
    public function get($path, array $parameters = [])
243
    {
244
        return $this->http('GET', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters);
245
    }
246
247
    /**
248
     * Make POST requests to the API.
249
     *
250
     * @param string $path
251
     * @param array $parameters
252
     *
253
     * @param array $headers
254
     * @return Response
255
     */
256
    public function post($path, array $parameters = [], array $headers = [])
257
    {
258
        return $this->http('POST', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters, $headers);
259
    }
260
261
    /**
262
     * Make DELETE requests to the API.
263
     *
264
     * @param string $path
265
     * @param array $parameters
266
     *
267
     * @return Response
268
     */
269
    public function delete($path, array $parameters = [])
270
    {
271
        return $this->http('DELETE', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters);
272
    }
273
274
    /**
275
     * Make PUT requests to the API.
276
     *
277
     * @param string $path
278
     * @param array $parameters
279
     *
280
     * @return Response
281
     */
282
    public function put($path, array $parameters = [], array $headers = [])
283
    {
284
        return $this->http('PUT', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters, $headers);
285
    }
286
287
    /**
288
     * Upload media to upload.twitter.com.
289
     *
290
     * @param array $parameters
291
     * @param bool $chunked
292
     *
293
     * @return array|object
294
     */
295
    public function upload(array $parameters = [], $chunked = false)
296
    {
297
        if ($chunked) {
298
            return $this->uploadMediaChunked(self::UPLOAD_PATH, $parameters);
299
        } else {
300
            return $this->uploadMediaNotChunked(self::UPLOAD_PATH, $parameters)->getBody();
301
        }
302
    }
303
304
    /**
305
     * Private method to upload media (not chunked) to upload.twitter.com.
306
     *
307
     * @param string $path
308
     * @param array $parameters
309
     *
310
     * @return array|object
311
     */
312
    private function uploadMediaNotChunked($path, $parameters)
313
    {
314
        $file = file_get_contents($parameters['media']);
315
        $base = base64_encode($file);
316
        $parameters['media'] = $base;
317
318
        return $this->http('POST', self::UPLOAD_HOST, $path, $parameters);
319
    }
320
321
    /**
322
     * Private method to upload media (chunked) to upload.twitter.com.
323
     *
324
     * @param string $path
325
     * @param array $parameters
326
     *
327
     * @return array|object
328
     */
329
    private function uploadMediaChunked($path, $parameters)
330
    {
331
        if ($parameters['media_type'] == 'video/mp4') {
332
            $parameters['media_category'] = "amplify_video";
333
        } elseif($parameters['media_type'] == 'image/gif'){
334
            $parameters['media_category'] = 'tweet_gif';
335
        }
336
337
        // Init
338
        $init = $this->http(
339
            'POST',
340
            self::UPLOAD_HOST,
341
            $path,
342
            [
343
                'command' => 'INIT',
344
                'media_type' => $parameters['media_type'],
345
                'media_category' => $parameters['media_category'],
346
                'total_bytes' => filesize($parameters['media'])
347
            ]
348
        )->getBody();
349
350
        // Append
351
        $segment_index = 0;
352
        $media = fopen($parameters['media'], 'rb');
353
        while (!feof($media)) {
354
            $this->http(
0 ignored issues
show
Unused Code introduced by
The call to the method Hborras\TwitterAdsSDK\Response::getBody() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
355
                'POST',
356
                self::UPLOAD_HOST,
357
                $path,
358
                [
359
                    'command' => 'APPEND',
360
                    'media_id' => $init->media_id_string,
361
                    'segment_index' => $segment_index++,
362
                    'media_data' => base64_encode(fread($media, self::UPLOAD_CHUNK)),
363
                ]
364
            )->getBody();
365
        }
366
        fclose($media);
367
        // Finalize
368
        $finalize = $this->http(
369
            'POST',
370
            self::UPLOAD_HOST,
371
            $path,
372
            [
373
                'command' => 'FINALIZE',
374
                'media_id' => $init->media_id_string,
375
            ]
376
        )->getBody();
377
378
        while (isset($finalize->processing_info)) {
379
            if (isset($finalize->processing_info->check_after_secs)) {
380
                sleep($finalize->processing_info->check_after_secs);
381
                $data = array(
382
                    'command' => 'STATUS',
383
                    'media_id' => $finalize->media_id
384
                );
385
                $finalize = $this->http('GET', self::UPLOAD_HOST, $path, $data)->getBody();
386
            } else {
387
                if (isset($finalize->processing_info->state) && $finalize->processing_info->state == 'succeeded') {
388
                    break;
389
                }
390
            }
391
        }
392
393
        return $finalize;
394
    }
395
396
    /**
397
     * @param string $method
398
     * @param string $host
399
     * @param string $path
400
     * @param array $parameters
401
     * @param array $headers
402
     * @return Response
403
     * @throws BadRequest
404
     * @throws Forbidden
405
     * @throws NotAuthorized
406
     * @throws NotFound
407
     * @throws RateLimit
408
     * @throws ServerError
409
     * @throws ServiceUnavailable
410
     */
411
    private function http($method, $host, $path, array $parameters, $headers = [])
412
    {
413
        $this->method = $method;
414
        $this->resource = $path;
415
        $this->resetLastResponse();
416
        if(strpos($path, TONUpload::DEFAULT_DOMAIN) === 0) {
417
            $url = $path;
418
        } else {
419
            if($host == self::UPLOAD_HOST){
420
                $url = sprintf('%s/%s/%s', $host, self::API_REST_VERSION, $path);
421
            } else {
422
                $url = sprintf('%s/%s/%s', $host, self::API_VERSION, $path);
423
            }
424
        }
425
426
        $this->response->setApiPath($path);
427
        $result = $this->oAuthRequest($url, $method, $parameters, $headers);
428
        $response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
429
        $this->response->setBody($response);
430
        if ($this->getLastHttpCode() > 399) {
431
            $this->manageErrors($response);
432
        }
433
        return $this->response;
434
    }
435
436
    /**
437
     * @param $response
438
     * @throws BadRequest
439
     * @throws Forbidden
440
     * @throws NotAuthorized
441
     * @throws NotFound
442
     * @throws RateLimit
443
     * @throws ServerError
444
     * @throws ServiceUnavailable
445
     */
446
    public function manageErrors($response){
447
        switch ($this->getLastHttpCode()) {
448
            case 400:
449
                throw new BadRequest(TwitterAdsException::BAD_REQUEST, 400, null, $response->errors);
450
            case 401:
451
                throw new NotAuthorized(TwitterAdsException::NOT_AUTHORIZED, 401, null, $response->errors);
452
            case 403:
453
                throw new Forbidden(TwitterAdsException::FORBIDDEN, 403, null, $response->errors);
454
            case 404:
455
                throw new NotFound(TwitterAdsException::NOT_FOUND, 404, null, $response->errors);
456
            case 429:
457
                throw new RateLimit(TwitterAdsException::RATE_LIMIT, 429, null, $response->errors, $this->response->getsHeaders());
458
            case 500:
459
                throw new ServerError(TwitterAdsException::SERVER_ERROR, 500, null, $response->errors);
460
            case 503:
461
                throw new ServiceUnavailable(TwitterAdsException::SERVICE_UNAVAILABLE, 503, null, $response->errors, $this->response->getsHeaders());
462
            default:
463
                throw new ServerError(TwitterAdsException::SERVER_ERROR, 500, null, $response->errors);
464
        }
465
    }
466
467
    /**
468
     * Format and sign an OAuth / API request.
469
     *
470
     * @param string $url
471
     * @param string $method
472
     * @param array $parameters
473
     *
474
     * @param array $headers
475
     * @return string
476
     * @throws TwitterAdsException
477
     * @throws TwitterOAuthException
478
     */
479
    private function oAuthRequest($url, $method, array $parameters, $headers = [])
480
    {
481
        $request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
482
        if (array_key_exists('oauth_callback', $parameters)) {
483
            // Twitter doesn't like oauth_callback as a parameter.
484
            unset($parameters['oauth_callback']);
485
        }
486
        if ($this->bearer === null) {
487
            $request->signRequest($this->signatureMethod, $this->consumer, $this->token);
488
            $authorization = $request->toHeader();
489
        } else {
490
            $authorization = 'Authorization: Bearer '.$this->bearer;
491
        }
492
        if(strpos($url, TONUpload::DEFAULT_DOMAIN) === 0) {
493
            return $this->request($url, $method, $authorization, $parameters, $headers);
494
        } else {
495
            return $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters, $headers);
496
        }
497
498
    }
499
500
    /**
501
     * Make an HTTP request.
502
     *
503
     * @param string $url
504
     * @param string $method
505
     * @param string $authorization
506
     * @param array $postfields
507
     *
508
     * @param array $headers
509
     * @return string
510
     * @throws TwitterAdsException
511
     */
512
    private function request($url, $method, $authorization, $postfields, $headers = [])
513
    {
514
        /* Curl settings */
515
        $options = [
516
            CURLOPT_VERBOSE => false,
517
            CURLOPT_CAINFO => __DIR__.DIRECTORY_SEPARATOR.'cacert.pem',
518
            CURLOPT_CONNECTTIMEOUT => $this->connectionTimeout,
519
            CURLOPT_HEADER => true,
520
            CURLOPT_HTTPHEADER => array_merge(['Accept: */*', $authorization, 'Expect:'],$headers,['Connection: close']),
521
            CURLOPT_RETURNTRANSFER => true,
522
            CURLOPT_SSL_VERIFYHOST => 2,
523
            CURLOPT_SSL_VERIFYPEER => true,
524
            CURLOPT_TIMEOUT => $this->timeout,
525
            CURLOPT_URL => $url,
526
            CURLOPT_USERAGENT => $this->userAgent,
527
            CURLOPT_ENCODING => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
528
        ];
529
530
        if (!empty($this->proxy)) {
531
            $options[CURLOPT_PROXY] = $this->proxy['CURLOPT_PROXY'];
532
            $options[CURLOPT_PROXYUSERPWD] = $this->proxy['CURLOPT_PROXYUSERPWD'];
533
            $options[CURLOPT_PROXYPORT] = $this->proxy['CURLOPT_PROXYPORT'];
534
            $options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC;
535
            $options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
536
        }
537
538
        switch ($method) {
539
            case 'GET':
540
                break;
541
            case 'POST':
542
                $options[CURLOPT_POST] = true;
543
                if(isset($postfields['raw'])){
544
                    $options[CURLOPT_POSTFIELDS] = $postfields['raw'];
545
                } else {
546
                    $options[CURLOPT_POSTFIELDS] = Util::buildHttpQuery($postfields);
547
                }
548
549
                break;
550
            case 'DELETE':
551
                $options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
552
                break;
553
            case 'PUT':
554
                $options[CURLOPT_CUSTOMREQUEST] = 'PUT';
555
                if(isset($postfields['raw'])){
556
                    $options[CURLOPT_POSTFIELDS] = $postfields['raw'];
557
                }
558
                break;
559
        }
560
561
        if (in_array($method, ['GET', 'PUT', 'DELETE']) && !empty($postfields) && !isset($postfields['raw'])) {
562
            $options[CURLOPT_URL] .= '?'.Util::buildHttpQuery($postfields);
563
        }
564
565
        $curlHandle = curl_init();
566
        curl_setopt_array($curlHandle, $options);
567
        $response = curl_exec($curlHandle);
568
        // Throw exceptions on cURL errors.
569
        if (curl_errno($curlHandle) > 0) {
570
            throw new TwitterAdsException(curl_error($curlHandle), curl_errno($curlHandle), null, null);
571
        }
572
        $this->response->setHttpCode(curl_getinfo($curlHandle, CURLINFO_HTTP_CODE));
573
        $parts = explode("\r\n\r\n", $response);
574
        $responseBody = array_pop($parts);
575
        $responseHeader = array_pop($parts);
576
        $this->response->setHeaders($this->parseHeaders($responseHeader));
577
        curl_close($curlHandle);
578
        return $responseBody;
579
    }
580
581
    /**
582
     * Get the header info to store.
583
     *
584
     * @param string $header
585
     *
586
     * @return array
587
     */
588
    private function parseHeaders($header)
589
    {
590
        $headers = [];
591
        foreach (explode("\r\n", $header) as $line) {
592
            if (strpos($line, ':') !== false) {
593
                list($key, $value) = explode(': ', $line);
594
                $key = str_replace('-', '_', strtolower($key));
595
                $headers[$key] = trim($value);
596
            }
597
        }
598
599
        return $headers;
600
    }
601
602
    /**
603
     * Encode application authorization header with base64.
604
     *
605
     * @param Consumer $consumer
606
     *
607
     * @return string
608
     */
609
    private function encodeAppAuthorization($consumer)
610
    {
611
        // TODO: key and secret should be rfc 1738 encoded
612
        $key = $consumer->key;
613
        $secret = $consumer->secret;
614
615
        return base64_encode($key.':'.$secret);
616
    }
617
618 View Code Duplication
    public function getRequestToken($oauth_callback)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
619
    {
620
        $parameters = array();
621
        $parameters['oauth_callback'] = $oauth_callback;
622
        $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
623
        $token = self::parse_parameters($request);
624
625
        return $token;
626
    }
627
628
    /**
629
     * Exchange request token and secret for an access token and
630
     * secret, to sign API calls.
631
     *
632
     * @param $oauth_verifier
633
     * @return array ("oauth_token" => "the-access-token",
634
     *                "oauth_token_secret" => "the-access-secret",
635
     * "user_id" => "9436992",
636
     * "screen_name" => "abraham")
637
     */
638 View Code Duplication
    public function getAccessToken($oauth_verifier)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
639
    {
640
        $parameters = array();
641
        $parameters['oauth_verifier'] = $oauth_verifier;
642
        $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters);
643
        $token = self::parse_parameters($request);
644
645
        return $token;
646
    }
647
648
    // This function takes a input like a=b&a=c&d=e and returns the parsed
649
    // parameters like this
650
    // array('a' => array('b','c'), 'd' => 'e')
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
651 View Code Duplication
    public static function parse_parameters($input)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
652
    {
653
        if (!isset($input) || !$input) {
654
            return array();
655
        }
656
657
        $pairs = explode('&', $input);
658
659
        $parsed_parameters = array();
660
        foreach ($pairs as $pair) {
661
            $split = explode('=', $pair, 2);
662
            $parameter = self::urldecode_rfc3986($split[0]);
663
            $value = isset($split[1]) ? self::urldecode_rfc3986($split[1]) : '';
664
665
            if (isset($parsed_parameters[$parameter])) {
666
                // We have already recieved parameter(s) with this name, so add to the list
667
                // of parameters with this name
668
669
                if (is_scalar($parsed_parameters[$parameter])) {
670
                    // This is the first duplicate, so transform scalar (string) into an array
671
                    // so we can add the duplicates
672
                    $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
673
                }
674
675
                $parsed_parameters[$parameter][] = $value;
676
            } else {
677
                $parsed_parameters[$parameter] = $value;
678
            }
679
        }
680
681
        return $parsed_parameters;
682
    }
683
684
    public static function urlencode_rfc3986($input)
685
    {
686
        if (is_array($input)) {
687
            return array_map(array(__CLASS__, 'urlencode_rfc3986'), $input);
688
        } elseif (is_scalar($input)) {
689
            return str_replace(
690
                '+',
691
                ' ',
692
                str_replace('%7E', '~', rawurlencode($input))
693
            );
694
        } else {
695
            return '';
696
        }
697
    }
698
699
    // This decode function isn't taking into consideration the above
700
    // modifications to the encoding process. However, this method doesn't
701
    // seem to be used anywhere so leaving it as is.
702
    public static function urldecode_rfc3986($string)
703
    {
704
        return urldecode($string);
705
    }
706
707
    /**
708
     * Get the authorize URL.
709
     *
710
     * @param $token
711
     * @param bool $sign_in_with_twitter
712
     * @return string
713
     */
714
    public function getAuthorizeURL($token, $sign_in_with_twitter = true)
715
    {
716
        if (is_array($token)) {
717
            $token = $token['oauth_token'];
718
        }
719
        if (empty($sign_in_with_twitter)) {
720
            return $this->authorizeURL() . "?oauth_token={$token}";
721
        } else {
722
            return $this->authenticateURL() . "?oauth_token={$token}";
723
        }
724
    }
725
726
    /**
727
     * Return current response. Allows inheritance.
728
     *
729
     * @return Response
730
     */
731
    public function getResponse()
732
    {
733
        return $this->response;
734
    }
735
736
    /**
737
     * @return string
738
     */
739
    public function getMethod()
740
    {
741
        return $this->method;
742
    }
743
744
    /**
745
     * @return string
746
     */
747
    public function getResource()
748
    {
749
        return $this->resource;
750
    }
751
}
752