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.
Test Failed
Push — master ( 881fff...e7adbb )
by sunsky
05:09
created

Request::addQuery()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

Changes 0
Metric Value
dl 0
loc 13
c 0
b 0
f 0
ccs 9
cts 10
cp 0.9
rs 9.2
nc 4
cc 4
eloc 9
nop 1
crap 4.016
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
17
/**
18
 * Class Request
19
 * @package MultiHttp
20
 */
21
class Request extends Http
22
{
23
    /**
24
     * you can implement more traits
25
     */
26
    use JsonTrait;
27
    /**
28
     *
29
     */
30
    const MAX_REDIRECTS_DEFAULT = 10;
31
    protected static $curlAlias = array(
32
        'url' => 'CURLOPT_URL',
33
        'uri' => 'CURLOPT_URL',
34
        'debug' => 'CURLOPT_VERBOSE',//for debug verbose
35
        'method' => 'CURLOPT_CUSTOMREQUEST',
36
        'data' => 'CURLOPT_POSTFIELDS', // array or string , file begin with '@'
37
        'ua' => 'CURLOPT_USERAGENT',
38
        'timeout' => 'CURLOPT_TIMEOUT', // (secs) 0 means indefinitely
39
        'connect_timeout' => 'CURLOPT_CONNECTTIMEOUT',
40
        'referer' => 'CURLOPT_REFERER',
41
        'binary' => 'CURLOPT_BINARYTRANSFER',
42
        'port' => 'CURLOPT_PORT',
43
        'header' => 'CURLOPT_HEADER', // TRUE:include header
44
        'headers' => 'CURLOPT_HTTPHEADER', // array
45
        'download' => 'CURLOPT_FILE', // writing file stream (using fopen()), default is STDOUT
46
        'upload' => 'CURLOPT_INFILE', // reading file stream
47
        'transfer' => 'CURLOPT_RETURNTRANSFER', // TRUE:return string; FALSE:output directly (curl_exec)
48
        'follow_location' => 'CURLOPT_FOLLOWLOCATION',
49
        'timeout_ms' => 'CURLOPT_TIMEOUT_MS', // milliseconds,  libcurl version > 7.36.0 ,
50
        /**
51
         * private properties
52
         */
53
        'expectsMime' => null, //expected mime
54
        'sendMime' => null, //send mime
55
        'ip' => null,//specify ip to send request
56
        'callback' => null,//callback on end
57
58
    );
59
    protected static $loggerHandler;
60
    public
61
        $curlHandle,
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
Coding Style introduced by
The visibility should be declared for property $curlHandle.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
62
        $uri,
63
        $sendMime,
64
        $expectedMime;
65
    protected $options = array(
66
        'CURLOPT_MAXREDIRS' => 10,
67
        'CURLOPT_SSL_VERIFYPEER' => false,//for https
68
        'CURLOPT_SSL_VERIFYHOST' => 0,//for https
69
        'CURLOPT_IPRESOLVE' => CURL_IPRESOLVE_V4,//ipv4 first
70
        'header' => true,
71
        'method' => self::GET,
72
        'transfer' => true,
73
        'headers' => array(),
74
        'follow_location' => true,
75
        'timeout' => 0);
76
    protected $endCallback;
77
    protected $withURIQuery;
78
    protected $hasInitialized = false;
79
80
    /**
81
     * Request constructor.
82
     */
83 2
    protected function __construct()
84
    {
85 2
    }
86
87
    /**
88
     * @return Request
89
     */
90 2
    public static function create()
91
    {
92 2
        return new self;
93
    }
94
95
    /**
96
     * @param callable $handler
97
     */
98 1
    public static function setLogHandler(callable $handler)
99
    {
100 1
        self::$loggerHandler = $handler;
101 1
    }
102
103
    /**
104
     * @param $parsedComponents
105
     * @return string
106
     */
107 2
    private static function combineUrl($parsedComponents)
108
    {
109 2
        $scheme = isset($parsedComponents['scheme']) ? $parsedComponents['scheme'] . '://' : '';
110 2
        $host = isset($parsedComponents['host']) ? $parsedComponents['host'] : '';
111 2
        $port = isset($parsedComponents['port']) ? ':' . $parsedComponents['port'] : '';
112 2
        $user = isset($parsedComponents['user']) ? $parsedComponents['user'] : '';
113 2
        $pass = isset($parsedComponents['pass']) ? ':' . $parsedComponents['pass'] : '';
114 2
        $pass = ($user || $pass) ? "$pass@" : '';
115 2
        $path = isset($parsedComponents['path']) ? $parsedComponents['path'] : '';
116 2
        $query = isset($parsedComponents['query']) ? '?' . $parsedComponents['query'] : '';
117 2
        $fragment = isset($parsedComponents['fragment']) ? '#' . $parsedComponents['fragment'] : '';
118 2
        return "$scheme$user$pass$host$port$path$query$fragment";
119
    }
120
121
    /**
122
     * @param string $mime
123
     * @return $this
124
     */
125 2
    public function expectsMime($mime = 'json')
126
    {
127 2
        $this->expectedMime = $mime;
128 2
        return $this;
129
    }
130
131
    /**
132
     * @param string $mime
133
     * @return Request
134
     */
135 2
    public function sendMime($mime = 'json')
136
    {
137 2
        $this->sendMime = $mime;
138 2
        $this->addHeader('Content-type', Mime::getFullMime($mime));
139 2
        return $this;
140
    }
141
142
    /**
143
     * @param $headerName
144
     * @param $value , can be rawurlencode
145
     * @return $this
146
     */
147 2
    public function addHeader($headerName, $value)
148
    {
149 2
        $this->options['headers'][] = $headerName . ': ' . $value;
150 2
        return $this;
151
    }
152
153
    /**
154
     * @param $uri
155
     * @return $this
156
     */
157
    public function uri($uri)
158
    {
159
        $this->uri = $uri;
160
        return $this;
161
    }
162
163
    /**
164
     * @param $timeout seconds, can be float
165
     * @return $this
166
     */
167
    public function timeout($timeout)
168
    {
169
        $this->options['timeout'] = $timeout;
170
        return $this;
171
    }
172
173
    /**
174
     * @param array $headers
175
     * @return $this
176
     */
177 1
    public function addHeaders(array $headers)
178
    {
179 1
        foreach ($headers as $header => $value) {
180 1
            $this->addHeader($header, $value);
181 1
        }
182 1
        return $this;
183
    }
184
185
    /**
186
     * @return mixed
187
     */
188
    public function endCallback()
189
    {
190
        return $this->endCallback;
191
    }
192
193
    /**
194
     * @return bool
195
     */
196 2
    public function hasEndCallback()
197
    {
198 2
        return isset($this->endCallback);
199
    }
200
201
    /**
202
     * @param $field alias or field name
203
     * @return bool|mixed
204
     */
205 2
    public function getIni($field = null)
206
    {
207 2
        if(!$field) return $this->options;
208 2
        $full = self::fullOption($field);
209 2
        return isset($this->options[$full]) ? $this->options[$full] : false;
210
    }
211
212
    /**
213
     * @param $key
214
     * @return mixed
215
     */
216 2
    protected static function fullOption($key)
217
    {
218 2
        $full = false;
219 2
        if (isset(self::$curlAlias[$key])) {
220 2
            $full = self::$curlAlias[$key];
221 2
        } elseif ((substr($key, 0, strlen('CURLOPT_')) == 'CURLOPT_') && defined($key)) {
222 2
            $full = $key;
223 2
        }
224 2
        return $full;
225
    }
226
227
    /**
228
     * @param $data
229
     * @return $this
230
     */
231 1
    public function addQuery($data)
232
    {
233 1
        if (!empty($data)) {
234 1
            if (is_array($data)) {
235 1
                $this->withURIQuery = http_build_query($data);
236 1
            } else if (is_string($data)) {
237 1
                $this->withURIQuery = $data;
238 1
            } else {
239
                throw new InvalidArgumentException('data must be array or string');
240
            }
241 1
        }
242 1
        return $this;
243
    }
244
245
    /**
246
     * @param $uri
247
     * @param null $payload
248
     * @param array $options
249
     * @return Request
250
     */
251 1
    public function post($uri, $payload = null, array $options = array())
252
    {
253 1
        return $this->ini(Http::POST, $uri, $payload, $options);
254
    }
255
256
    /**
257
     * @param $method
258
     * @param $url
259
     * @param $data
260
     * @param array $options
261
     * @return $this
262
     */
263 2 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...
264
    {
265 2
        $options = array('url' => $url, 'method' => $method, 'data' => $data) + $options;
266 2
        $this->addOptions($options);
267
268 2
        return $this;
269
    }
270
271
    /**
272
     * @param array $options
273
     * @return $this
274
     */
275 2
    public function addOptions(array $options = array())
276
    {
277 2
        $this->options = $options + $this->options;
278 2
        $this->uri = $this->options['url'];
279 2
        return $this;
280
    }
281
282
    /**
283
     * @param $uri
284
     * @param null $payload
285
     * @param array $options
286
     * @return Request
287
     */
288 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...
289
    {
290 1
        return $this->ini(Http::PUT, $uri, $payload, $options);
291
    }
292
293
    /**
294
     * @param $uri
295
     * @param null $payload
296
     * @param array $options
297
     * @return Request
298
     */
299 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...
300
    {
301 1
        return $this->ini(Http::PATCH, $uri, $payload, $options);
302
    }
303
304
    /**
305
     * @param $uri
306
     * @param array $options
307
     * @return Request
308
     */
309 1
    public function get($uri, array $options = array())
310
    {
311 1
        return $this->ini(Http::GET, $uri, array(), $options);
312
    }
313
314
    /**
315
     * @param $uri
316
     * @param array $options
317
     * @return Request
318
     */
319 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...
320
    {
321 1
        return $this->ini(Http::OPTIONS, $uri, array(), $options);
322
    }
323
324
    /**
325
     * @param $uri
326
     * @param array $options
327
     * @return Request
328
     */
329 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...
330
    {
331 1
        return $this->ini(Http::HEAD, $uri, array('CURLOPT_NOBODY' => true), $options);
332
    }
333
334
    /**
335
     * @param $uri
336
     * @param array $options
337
     * @return Request
338
     */
339 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...
340
    {
341 1
        return $this->ini(Http::DELETE, $uri, array(), $options);
342
    }
343
344
    /**
345
     * @param $uri
346
     * @param array $options
347
     * @return Request
348
     */
349 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...
350
    {
351 2
        return $this->ini(Http::TRACE, $uri, array(), $options);
352
    }
353
354
    /**
355
     * @param bool $isMultiCurl
356
     * @return Response
357
     */
358 2
    public function send($isMultiCurl = false)
359
    {
360
        try {
361 2
            if (!$this->hasInitialized)
362 2
                $this->applyOptions();
363 2
            $response = $this->makeResponse($isMultiCurl);
364 2
            $response->parse();
365 2
        } catch (\Exception $e) {
366
            if(!isset($response)) $response = Response::create($this, null, null, null, null);
367
            $response->error = $e->getMessage();
368
            $response->errorCode = 999;
369
        }
370
371 2
        if (self::$loggerHandler) {
372 1
            call_user_func(self::$loggerHandler, $response);
373 1
        }
374 2
        if ($this->endCallback) {
375 2
            call_user_func($this->endCallback, $response);
376 2
        }
377
378 2
        return $response;
379
    }
380
381
    /**
382
     * @return $this
383
     */
384 2
    public function applyOptions()
385
    {
386 2
        $curl = curl_init();
387 2
        $this->curlHandle = $curl;
388 2
        $this->prepare();
389 2
        $this->hasInitialized = true;
390 2
        return $this;
391
    }
392
393
    /**
394
     * @return $this
395
     */
396 2
    protected function prepare()
397
    {
398 2
        $this->options['url'] = trim($this->options['url']);
399 2
        if (empty($this->options['url'])) {
400
            throw new InvalidArgumentException('url can not empty');
401
        }
402
403 2
        if(isset($this->options['expectsMime'])){
404 1
            $this->expectsMime($this->options['expectsMime']);
405
//            unset($this->options['expectsMime']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% 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...
406 1
        }
407
408 2
        if(isset($this->options['sendMime'])){
409 1
            $this->sendMime($this->options['sendMime']);
410
//            unset($this->options['sendMime']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% 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...
411 1
        }
412
413 2
        $this->serializeBody();
414
415
        //try fix url
416 2
        if (strpos($this->options['url'], '://') === FALSE) $this->options['url'] = 'http://' . $this->options['url'];
417 2
        $components = parse_url($this->options['url']);
418 2
        if(FALSE === $components) throw new InvalidArgumentException('formatting url occurs error: '. $this->options['url']);
419 2
        if($this->withURIQuery){
420 1
            if(isset($components['query'])) $components['query'] .= '&'. trim($this->withURIQuery);
421 1
            else $components['query'] = trim($this->withURIQuery);
422 1
        }
423 2
        $this->options['url'] = self::combineUrl($components);
424
425 2
        if (isset($this->options['callback'])) {
426 2
            $this->onEnd($this->options['callback']);
427
//            unset($this->options['callback']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% 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...
428 2
        }
429
        //swap ip and host
430 2
        if (!empty($this->options['ip'])) {
431 2
            $matches = array();
432 2
            preg_match('/\/\/([^\/]+)/', $this->options['url'], $matches);
433 2
            $host = $matches[1];
434 2
            if (empty($this->options['headers']) || !is_array($this->options['headers'])) {
435 2
                $this->options['headers'] = array('Host: ' . $host);
436 2
            } else {
437
                $this->options['headers'][] = 'Host: ' . $host;
438
            }
439 2
            $this->options['url'] = preg_replace('/\/\/([^\/]+)/', '//' . $this->options['ip'], $this->options['url']);
440
//            unset($this->options['ip']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% 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...
441 2
            unset($host);
442 2
        }
443
        //process version
444 2
        if (!empty($this->options['http_version'])) {
445
            $version = $this->options['http_version'];
446
            if ($version == '1.0') {
447
                $this->options['CURLOPT_HTTP_VERSION'] = CURLOPT_HTTP_VERSION_1_0;
448
            } elseif ($version == '1.1') {
449
                $this->options['CURLOPT_HTTP_VERSION'] = CURLOPT_HTTP_VERSION_1_1;
450
            }
451
452
            unset($version);
453
        }
454
455
        //convert secs to milliseconds
456 2
        if (defined('CURLOPT_TIMEOUT_MS')) {
457 2
            if (!isset($this->options['timeout_ms'])) {
458 2
                $this->options['timeout_ms'] = intval($this->options['timeout'] * 1000);
459 2
            } else {
460 1
                $this->options['timeout_ms'] = intval($this->options['timeout_ms']);
461
            }
462 2
        }
463
464
465 2
        $cURLOptions = self::filterAndRaw($this->options);
466 2
        curl_setopt_array($this->curlHandle, $cURLOptions);
467
468 2
        return $this;
469
    }
470
471 2
    public function serializeBody()
472
    {
473 2
        if (isset($this->options['data'])) {
474 2
            if (isset($this->sendMime)) {
475 2
                $method = $this->sendMime;
476 2
                if (!method_exists($this, $method)) throw new InvalidOperationException($method . ' is not exists in ' . __CLASS__);
477 2
                $this->options['data'] = $this->$method($this->options['data']);
478 2
            } else {
479 2
                $this->options['data'] = is_array($this->options['data']) ? http_build_query($this->options['data']) : $this->options['data'];//for better compatibility
480
            }
481 2
        }
482 2
    }
483
484
    /**
485
     * @param callable $callback
486
     * @return $this
487
     */
488 2
    public function onEnd(callable $callback)
489
    {
490 2
        if (!is_callable($callback)) {
491
            throw new InvalidArgumentException('callback not is callable :' . print_r($callback, 1));
492
        }
493
494 2
        $this->endCallback = $callback;
495 2
        return $this;
496
    }
497
498
    /**
499
     * @param array $options
500
     * @return array
501
     */
502 2
    protected static function filterAndRaw(array &$options)
503
    {
504 2
        $opts = $fullsOpts = array();
505 2
        foreach ($options as $key => $val) {
506 2
            $fullOption = self::fullOption($key);
507
508 2
            if ($fullOption) {
509 2
                $fullsOpts[$fullOption] = $val;
510 2
                $opts[constant($fullOption)] = $val;
511 2
            }
512 2
            unset($options[$key]);
513 2
        }
514 2
        $options = $fullsOpts;
515 2
        return $opts;
516
    }
517
518
    /**
519
     * @param bool $isMultiCurl
520
     * @return Response
521
     * @throws \Exception
522
     */
523 2
    public function makeResponse($isMultiCurl = false)
524
    {
525 2
        $body = $isMultiCurl ? curl_multi_getcontent($this->curlHandle) : curl_exec($this->curlHandle);
526 2
        $info = curl_getinfo($this->curlHandle);
527 2
        $errorCode = curl_errno($this->curlHandle);
528 2
        $error = curl_error($this->curlHandle);
529 2
        $response = Response::create($this, $body, $info, $errorCode, $error);
530 2
        return $response;
531
    }
532
533
534
}
535