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.

Request::noFollow()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 *
4
 * @author  [email protected] [email protected]
5
 * Date: 2017/6/16
6
 * Time: 10:02
7
 * @version $Id: $
8
 * @since 1.0
9
 * @copyright Sina Corp.
10
 */
11
12
namespace MultiHttp;
13
14
use MultiHttp\Exception\InvalidArgumentException;
15
use MultiHttp\Exception\InvalidOperationException;
16
use MultiHttp\Handler\Form;
17
use MultiHttp\Handler\IHandler;
18
19
/**
20
 * Class Request
21
 * @package MultiHttp
22
 */
23
class Request extends Http
24
{
25
    /**
26
     * you can implement more traits
27
     */
28
    protected static $curlAlias = array(
29
        'url' => 'CURLOPT_URL',
30
        'uri' => 'CURLOPT_URL',
31
        'debug' => 'CURLOPT_VERBOSE',//for debug verbose
32
        'method' => 'CURLOPT_CUSTOMREQUEST',
33
        'data' => 'CURLOPT_POSTFIELDS', // array or string , file begin with '@'
34
        'ua' => 'CURLOPT_USERAGENT',
35
        'timeout' => 'CURLOPT_TIMEOUT', // (secs) 0 means indefinitely
36
        'connect_timeout' => 'CURLOPT_CONNECTTIMEOUT',
37
        'referer' => 'CURLOPT_REFERER',
38
        'binary' => 'CURLOPT_BINARYTRANSFER',
39
        'port' => 'CURLOPT_PORT',
40
        'header' => 'CURLOPT_HEADER', // TRUE:include header
41
        'headers' => 'CURLOPT_HTTPHEADER', // array
42
        'download' => 'CURLOPT_FILE', // writing file stream (using fopen()), default is STDOUT
43
        'upload' => 'CURLOPT_INFILE', // reading file stream
44
        'transfer' => 'CURLOPT_RETURNTRANSFER', // TRUE:return string; FALSE:output directly (curl_exec)
45
        'follow_location' => 'CURLOPT_FOLLOWLOCATION',
46
        'timeout_ms' => 'CURLOPT_TIMEOUT_MS', // milliseconds,  libcurl version > 7.36.0 ,
47
        'expects_mime' => null, //expected mime
48
        'send_mime' => null, //send mime
49
        'ip' => null,//specify ip to send request
50
        'callback' => null,//callback on end
51
52
    );
53
    protected static $loggerHandler;
54
    public
55
        $curlHandle,
56
        $uri,
57
        $sendMime,
58
        $expectsMime,
59
        $timeout,
60
        $maxRedirects,
61
        $encoding,
62
        $payload,
63
        $retryTimes,
64
        /**
65
         * @var int seconds
66
         */
67
        $retryDuration,
68
        $followRedirects;
69
70
    protected
71
        $body,
72
        $endCallback,
73
        $withURIQuery,
74
        $hasInitialized = false,
75
        /**
76
         * @var array
77
         */
78
        $options = array(
79
            'CURLOPT_MAXREDIRS' => 10,
80
            'CURLOPT_SSL_VERIFYPEER' => false,//for https
81
            'CURLOPT_SSL_VERIFYHOST' => 0,//for https
82
            'CURLOPT_IPRESOLVE' => CURL_IPRESOLVE_V4,//ipv4 first
83
            'CURLOPT_SAFE_UPLOAD' => false,// compatible with PHP 5.6.0
84
            'CURLOPT_USERAGENT' => 'Mozilla/5.0 (compatible;)',
85
            'header' => true,
86
            'method' => self::GET,
87
            'transfer' => true,
88
            'headers' => array(),
89
            'follow_location' => true,
90
            'timeout' => 0,
91
            //        'ip' => null, //host, in string, .e.g: 172.16.1.1:888
92
            'retry_times' => 1,//redo task when failed
93
            'retry_duration' => 0,//in seconds
94
            'send_mime' => 'form',//in seconds
95
        );
96
97
98
    /**
99
     * Request constructor.
100
     */
101 7
    protected function __construct()
102
    {
103 7
    }
104
105
    /**
106
     * @return Request
107
     */
108 7
    public static function create()
109
    {
110 7
        return new self;
111
    }
112
113
    /**
114
     * @param callable $handler
115
     */
116 1
    public static function setLogHandler(callable $handler)
117
    {
118 1
        self::$loggerHandler = $handler;
119 1
    }
120
    /**
121
     * Specify   timeout
122
     * @param float|int $timeout seconds to timeout the HTTP call
123
     * @return Request
124
     */
125
    public function timeout($timeout)
126
    {
127
        $this->timeout = $timeout;
128
        return $this;
129
    }
130
131
    /**
132
     * @return Request
133
     */
134
    public function noFollow()
135
    {
136
        return $this->follow(0);
137
    }
138
139
    /**
140
     * If the response is a 301 or 302 redirect, automatically
141
     * send off another request to that location
142
     * @param int $follow follow or not to follow or maximal number of redirects
143
     * @return Request
144
     */
145
    public function follow($follow)
146
    {
147
        $this->maxRedirects = abs($follow);
148
        $this->followRedirects = $follow > 0;
149
        return $this;
150
    }
151
152
    /**
153
     * @param $parsedComponents
154
     * @return string
155
     */
156 7
    private static function combineUrl($parsedComponents)
157
    {
158 7
        $scheme = isset($parsedComponents['scheme']) ? $parsedComponents['scheme'] . '://' : '';
159 7
        $host = isset($parsedComponents['host']) ? $parsedComponents['host'] : '';
160 7
        $port = isset($parsedComponents['port']) ? ':' . $parsedComponents['port'] : '';
161 7
        $user = isset($parsedComponents['user']) ? $parsedComponents['user'] : '';
162 7
        $pass = isset($parsedComponents['pass']) ? ':' . $parsedComponents['pass'] : '';
163 7
        $pass = ($user || $pass) ? "$pass@" : '';
164 7
        $path = isset($parsedComponents['path']) ? $parsedComponents['path'] : '';
165 7
        $query = isset($parsedComponents['query']) ? '?' . $parsedComponents['query'] : '';
166 7
        $fragment = isset($parsedComponents['fragment']) ? '#' . $parsedComponents['fragment'] : '';
167 7
        return "$scheme$user$pass$host$port$path$query$fragment";
168
    }
169
170
    /**
171
     * @param string $mime
172
     * @return $this
173
     */
174 1
    public function expectsMime($mime = 'json')
175
    {
176 1
        $this->expectsMime = $mime;
177 1
        $this->options['expects_mime'] = $mime;
178 1
        return $this;
179
    }
180
181
    /**
182
     * @param string $mime
183
     * @return Request
184
     */
185 2
    public function sendMime($mime = 'json')
186
    {
187 2
        $this->sendMime = $mime;
188 2
        $this->options['send_mime'] = $mime;
189
//        $this->addHeader('Content-type', Mime::getFullMime($mime));
190 2
        return $this;
191
    }
192
193
    /**
194
     * @param $headerName
195
     * @param $value , can be rawurlencode
196
     * @return $this
197
     */
198 1
    public function addHeader($headerName, $value)
199
    {
200 1
        $this->options['headers'][] = $headerName . ': ' . $value;
201 1
        return $this;
202
    }
203
204
    /**
205
     * @param $uri
206
     * @return $this
207
     */
208
    public function uri($uri)
209
    {
210
        $this->uri = $uri;
211
        return $this;
212
    }
213
214
215
216
    /**
217
     * @param array $headers
218
     * @return $this
219
     */
220 1
    public function addHeaders(array $headers)
221
    {
222 1
        foreach ($headers as $header => $value) {
223 1
            $this->addHeader($header, $value);
224 1
        }
225 1
        return $this;
226
    }
227
    /**
228
     * @return mixed
229
     */
230
    public function endCallback()
231
    {
232
        return $this->endCallback;
233
    }
234
235
    /**
236
     * @return bool
237
     */
238 2
    public function hasEndCallback()
239
    {
240 2
        return isset($this->endCallback);
241
    }
242
243
    /**
244
     * @param $field alias or field name
245
     * @return bool|mixed
246
     */
247 2
    public function getIni($field = null)
248
    {
249 2
        if(!$field) return $this->options;
250 2
        $full = self::fullOption($field);
251 2
        return isset($this->options[$full]) ? $this->options[$full] : false;
252
    }
253
254
    /**
255
     * @param $key
256
     * @return mixed
257
     */
258 7
    protected static function fullOption($key)
259
    {
260 7
        $full = false;
261 7
        if (isset(self::$curlAlias[$key])) {
262 7
            $full = self::$curlAlias[$key];
263 7
        } elseif ((substr($key, 0, strlen('CURLOPT_')) == 'CURLOPT_') && defined($key)) {
264 7
            $full = $key;
265 7
        }
266 7
        return $full;
267
    }
268
269
    /**
270
     * @param $queryData
271
     * @return $this
272
     */
273 1
    public function addQuery($queryData)
274
    {
275 1
        if (!empty($queryData)) {
276 1
            if (is_array($queryData)) {
277 1
                $this->withURIQuery = http_build_query($queryData);
278 1
            } else if (is_string($queryData)) {
279 1
                $this->withURIQuery = $queryData;
280 1
            } else {
281
                throw new InvalidArgumentException('data must be array or string');
282
            }
283 1
        }
284 1
        return $this;
285
    }
286
    /**
287
     * @param $uri
288
     * @param null $payload
289
     * @param array $options
290
     * @return Request
291
     */
292 3
    public function post($uri, $payload = null, array $options = array())
293
    {
294 3
        return $this->ini(Http::POST, $uri, $payload, $options);
295
    }
296
297
    /**
298
     * @param $uri
299
     * @param null $payload
300
     * @param array $options
301
     * @return Request
302
     */
303 1
    public function upload($uri, $payload = null, array $options = array())
304
    {
305 1
        return $this->ini(Http::POST, $uri, $payload, $options)->sendMime('upload');
306
    }
307
308
    /**
309
     * @param $uri
310
     * @param null $payload
311
     * @param array $options
312
     * @param null $response
313
     * @return string
314
     */
315 2
    public function quickPost($uri, $payload = null, array $options = array(), &$response = null)
316
    {
317 2
        $response = $this->post($uri, $payload, $options)->send();
318 2
        return $response->body;
319
    }
320
321
322
    /**
323
     * @param $method
324
     * @param $url
325
     * @param $data
326
     * @param array $options
327
     * @return $this
328
     */
329 6 View Code Duplication
    protected function ini($method, $url, $data, array $options = array())
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...
330
    {
331 6
        $options = array('url' => $url, 'method' => $method, 'data' => $data) + $options;
332 6
        $this->addOptions($options);
333
334 6
        return $this;
335
    }
336
337
    /**
338
     * @param array $options
339
     * @return $this
340
     */
341 7
    public function addOptions(array $options = array())
342
    {
343 7
        $this->options = $options + $this->options;
344 7
        $this->uri = $this->options['url'];
345 7
        return $this;
346
    }
347
348
    /**
349
     * @param $uri
350
     * @param null $payload
351
     * @param array $options
352
     * @return Request
353
     */
354 1
    function put($uri, $payload = null, array $options = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
355
    {
356 1
        return $this->ini(Http::PUT, $uri, $payload, $options);
357
    }
358
359
    /**
360
     * @param $uri
361
     * @param null $payload
362
     * @param array $options
363
     * @return Request
364
     */
365 1
    function patch($uri, $payload = null, array $options = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
366
    {
367 1
        return $this->ini(Http::PATCH, $uri, $payload, $options);
368
    }
369
370
    /**
371
     * @param $uri
372
     * @param array $options
373
     * @return Request
374
     */
375 3
    public function get($uri, array $options = array())
376
    {
377 3
        return $this->ini(Http::GET, $uri, array(), $options);
378
    }
379
380
381
    /**
382
     * @param $uri
383
     * @param array $options
384
     * @param null $response
385
     * @return string
386
     */
387 2
    public function quickGet($uri, array $options = array(), &$response = null)
388
    {
389 2
        $response = $this->get($uri, $options)->send();
390 2
        return $response->body;
391
    }
392
393
    /**
394
     * @param $uri
395
     * @param array $options
396
     * @return Request
397
     */
398 1
    function options($uri, array $options = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
399
    {
400 1
        return $this->ini(Http::OPTIONS, $uri, array(), $options);
401
    }
402
403
    /**
404
     * @param $uri
405
     * @param array $options
406
     * @return Request
407
     */
408 1
    function head($uri, array $options = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
409
    {
410 1
        return $this->ini(Http::HEAD, $uri, array('CURLOPT_NOBODY' => true), $options);
411
    }
412
413
    /**
414
     * @param $uri
415
     * @param array $options
416
     * @return Request
417
     */
418 1
    function delete($uri, array $options = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
419
    {
420 1
        return $this->ini(Http::DELETE, $uri, array(), $options);
421
    }
422
423
    /**
424
     * @param $uri
425
     * @param array $options
426
     * @return Request
427
     */
428 2
    function trace($uri, array $options = array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
429
    {
430 2
        return $this->ini(Http::TRACE, $uri, array(), $options);
431
    }
432
433
    /**
434
     * @param bool $isMultiCurl
435
     * @return Response
436
     */
437 7
    public function send($isMultiCurl = false)
438
    {
439
        try {
440 7
            if (!$this->hasInitialized)
441 7
                $this->applyOptions();
442 7
            $response = $this->makeResponse($isMultiCurl);
443 7
            $response->parse();
444 7
        } catch (\Exception $e) {
445
            if(!isset($response)) $response = Response::create($this, null, null, null, null);
446
            $response->error = $e->getMessage();
447
            $response->errorCode = 999;
448
        }
449
450 7
        if (self::$loggerHandler) {
451 3
            call_user_func(self::$loggerHandler, $response);
452 3
        }
453 7
        if ($this->endCallback) {
454 5
            call_user_func($this->endCallback, $response);
455 5
        }
456
457 7
        return $response;
458
    }
459
460
    /**
461
     * @return $this
462
     */
463 7
    public function applyOptions()
464
    {
465 7
        $curl = curl_init();
466 7
        $this->curlHandle = $curl;
467 7
        $this->prepare();
468 7
        $this->hasInitialized = true;
469 7
        return $this;
470
    }
471
472
    /**
473
     * @return $this
474
     */
475 7
    protected function prepare()
476
    {
477 7
        $this->options['url'] = trim($this->options['url']);
478 7
        if (empty($this->options['url'])) {
479
            throw new InvalidArgumentException('url can not empty');
480
        }
481
482 7
        if (isset($this->options['retry_times'])) {
483 7
            $this->retryTimes = abs($this->options['retry_times']);
484 7
        }
485
486 7
        if (isset($this->options['retry_duration'])) {
487 7
            $this->retryDuration = abs($this->options['retry_duration']);
488 7
        }
489
490 7
        if(isset($this->options['expects_mime'])){
491 2
            $this->expectsMime = $this->options['expects_mime'];
492 2
        }
493
494 7
        if(isset($this->options['send_mime'])){
495 7
            $this->sendMime = $this->options['send_mime'];
496 7
        }
497
498
//        if(!empty($this->options['data']) && !Http::hasBody($this->options['method'])){
499
//            $this->withURIQuery =  is_array($this->options['data']) ? http_build_query($this->options['data']) : $this->options['data'];
500
//        }
501 7
        if (isset($this->withURIQuery)) {
502 1
            $this->options['url'] .= strpos($this->options['url'], '?') === FALSE ? '?' : '&';
503 1
            $this->options['url'] .= $this->withURIQuery;
504 1
        }
505
506 7
        $this->serializeBody();
507
508
        //try fix url
509 7
        if (strpos($this->options['url'], '://') === FALSE) $this->options['url'] = 'http://' . $this->options['url'];
510 7
        $components = parse_url($this->options['url']);
511 7
        if(FALSE === $components) throw new InvalidArgumentException('formatting url occurs error: '. $this->options['url']);
512 7
        if($this->withURIQuery){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->withURIQuery of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
513 1
            if(isset($components['query'])) $components['query'] .= '&'. trim($this->withURIQuery);
514 1
            else $components['query'] = trim($this->withURIQuery);
515 1
        }
516 7
        $this->options['url'] = self::combineUrl($components);
517
518 7
        if (isset($this->options['callback'])) {
519 5
            $this->onEnd($this->options['callback']);
520 5
        }
521
        //swap ip and host
522 7
        if (!empty($this->options['ip'])) {
523 2
            $matches = array();
524 2
            preg_match('/\/\/([^\/]+)/', $this->options['url'], $matches);
525 2
            $host = $matches[1];
526 2
            if (empty($this->options['headers']) || !is_array($this->options['headers'])) {
527 2
                $this->options['headers'] = array('Host: ' . $host);
528 2
            } else {
529
                $this->options['headers'][] = 'Host: ' . $host;
530
            }
531 2
            $this->options['url'] = str_replace("//{$host}", '//' . $this->options['ip'], $this->options['url']);
532 2
            unset($host);
533 2
        }
534
        //process version
535 7
        if (!empty($this->options['http_version'])) {
536
            $version = $this->options['http_version'];
537
            if ($version == '1.0') {
538
                $this->options['CURLOPT_HTTP_VERSION'] = CURLOPT_HTTP_VERSION_1_0;
539
            } elseif ($version == '1.1') {
540
                $this->options['CURLOPT_HTTP_VERSION'] = CURLOPT_HTTP_VERSION_1_1;
541
            }
542
543
            unset($version);
544
        }
545
546
        //convert secs to milliseconds
547 7
        if (defined('CURLOPT_TIMEOUT_MS')) {
548 7
            if (!isset($this->options['timeout_ms'])) {
549 7
                $this->options['timeout_ms'] = intval($this->options['timeout'] * 1000);
550 7
            } else {
551 1
                $this->options['timeout_ms'] = intval($this->options['timeout_ms']);
552
            }
553 7
        }
554
555 7
        $cURLOptions = self::filterAndRaw($this->options);
556 7
        if(isset($this->body))$cURLOptions[CURLOPT_POSTFIELDS] = $this->body;//use serialized body not raw data
557 7
        curl_setopt_array($this->curlHandle, $cURLOptions);
558
559 7
        return $this;
560
    }
561
562 7
    public function serializeBody()
563
    {
564
        //Passing an array to CURLOPT_POSTFIELDS will encode the data as multipart/form-data, while passing a URL-encoded string will encode the data as application/x-www-form-urlencoded.
565 7
        if (isset($this->options['data'])) {
566 7
            $this->options[CURLOPT_POST] = true;
567 7
            $clz = '\\MultiHttp\\Handler\\'.ucfirst($this->sendMime);
568 7
            $inst = new $clz;
569 7
            if (!($inst instanceof Handler\IHandler)) throw new InvalidOperationException($clz . ' is not implement of  IHandler');
570 7
            $this->body = $inst->encode($this->options['data']);
571 7
        }
572 7
    }
573
574
    /**
575
     * @param callable $callback
576
     * @return $this
577
     */
578 5
    public function onEnd(callable $callback)
579
    {
580 5
        if (!is_callable($callback)) {
581
            throw new InvalidArgumentException('callback not is callable :' . print_r($callback, 1));
582
        }
583
584 5
        $this->endCallback = $callback;
585 5
        return $this;
586
    }
587
588
    /**
589
     * @param array $options
590
     * @return array
591
     */
592 7
    protected static function filterAndRaw(array &$options)
593
    {
594 7
        $opts = $fullsOpts = array();
595 7
        foreach ($options as $key => $val) {
596 7
            $fullOption = self::fullOption($key);
597
598 7
            if ($fullOption) {
599 7
                $fullsOpts[$fullOption] = $val;
600 7
                $opts[constant($fullOption)] = $val;
601 7
            }
602 7
            unset($options[$key]);
603 7
        }
604 7
        $options = $fullsOpts;
605 7
        return $opts;
606
    }
607
608
    /**
609
     * @param bool $isMultiCurl
610
     * @return Response
611
     * @throws \Exception
612
     */
613 7
    public function makeResponse($isMultiCurl = false)
614
    {
615 7
        $handle = $this->curlHandle;
616 7
        $body = $errno = null;
617 7
        Helper::retry($this->retryTimes, function()use(&$body, &$errno, $isMultiCurl, $handle){
618 7
            $body = $isMultiCurl ? curl_multi_getcontent($handle) : curl_exec($handle);
619 7
            $errno = curl_errno($handle);
620 7
            return 0 == $errno;
621 7
        }, $this->retryDuration);
622
623 7
        $info = curl_getinfo($this->curlHandle);
624 7
        $errorCode = curl_errno($this->curlHandle);
625 7
        $error = curl_error($this->curlHandle);
626 7
        $response = Response::create($this, $body, $info, $errorCode, $error);
627 7
        return $response;
628
    }
629
630
631
}
632