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.

Gateway::exec()   F
last analyzed

Complexity

Conditions 28
Paths 185

Size

Total Lines 159
Code Lines 98

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 98
c 0
b 0
f 0
nc 185
nop 1
dl 0
loc 159
rs 3.4583

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @package toolkit
5
 */
6
/**
7
 * The Gateway class provides a standard way to interact with other pages.
8
 * By default it is essentially a wrapper for CURL, but if that is not available
9
 * it falls back to use sockets.
10
 * @example
11
 *  `
12
 * $ch = new Gateway;
13
 * $ch->init('http://www.example.com/');
14
 * $ch->setopt('POST', 1);
15
 * $ch->setopt('POSTFIELDS', array('fred' => 1, 'happy' => 'yes'));
16
 * print $ch->exec();
17
 * `
18
 */
19
class Gateway
20
{
21
    /**
22
     * Constant used to explicitly bypass CURL and use Sockets to
23
     * complete the request.
24
     * @var string
25
     */
26
    const FORCE_SOCKET = 'socket';
27
28
    /**
29
     * An associative array of some common ports for HTTP, HTTPS
30
     * and FTP. Port cannot be null when using Sockets
31
     * @var array
32
     */
33
    private static $ports = array(
34
        'http' => 80,
35
        'https' => 443,
36
        'ftp' => 21
37
    );
38
39
    /**
40
     * The URL for the request, as string. This may be a full URL including
41
     * any basic authentication. It will be parsed and applied to CURL using
42
     * the correct options.
43
     * @var string
44
     */
45
    private $_url = null;
46
47
    /**
48
     * The hostname of the request, as parsed by parse_url
49
     *
50
     * @link http://php.net/manual/en/function.parse-url.php
51
     * @var string
52
     */
53
    private $_host = null;
54
55
    /**
56
     * The protocol of the URL in the request, as parsed by parse_url
57
     * Defaults to http://
58
     *
59
     * @link http://php.net/manual/en/function.parse-url.php
60
     * @var string
61
     */
62
    private $_scheme = 'http://';
63
64
    /**
65
     * The port of the URL in the request, as parsed by parse_url
66
     *
67
     * @link http://php.net/manual/en/function.parse-url.php
68
     * @var integer
69
     */
70
    private $_port = null;
71
72
    /**
73
     * The path of the URL in the request, as parsed by parse_url
74
     *
75
     * @link http://php.net/manual/en/function.parse-url.php
76
     * @var string
77
     */
78
    private $_path = null;
79
80
    /**
81
     * The method to request the URL. By default, this is GET
82
     * @var string
83
     */
84
    private $_method = 'GET';
85
86
    /**
87
     * The content-type of the request, defaults to application/x-www-form-urlencoded
88
     * @var string
89
     */
90
    private $_content_type = 'application/x-www-form-urlencoded; charset=utf-8';
91
92
    /**
93
     * The user agent for the request, defaults to Symphony.
94
     * @var string
95
     */
96
    private $_agent = 'Symphony';
97
98
    /**
99
     * A URL encoded string of the `$_POST` fields, as built by
100
     * http_build_query()
101
     *
102
     * @link http://php.net/manual/en/function.http-build-query.php
103
     * @var string
104
     */
105
    private $_postfields = '';
106
107
    /**
108
     * Whether to the return the Header with the result of the request
109
     * @var boolean
110
     */
111
    private $_returnHeaders = false;
112
113
    /**
114
     * The timeout in seconds for the request, defaults to 4
115
     * @var integer
116
     */
117
    private $_timeout = 4;
118
119
    /**
120
     * An array of custom headers to pass with the request
121
     * @var array
122
     */
123
    private $_headers = array();
124
125
    /**
126
     * An array of custom options for the CURL request, this
127
     * can be any option as listed on the PHP manual
128
     *
129
     * @link http://php.net/manual/en/function.curl-setopt.php
130
     * @var array
131
     */
132
    private $_custom_opt = array();
133
134
    /**
135
     * An array of information about the request after it has
136
     * been executed. At minimum, regardless of if CURL or Sockets
137
     * are used, the HTTP Code, URL and Content Type will be returned
138
     *
139
     * @link http://php.net/manual/en/function.curl-getinfo.php
140
     */
141
    private $_info_last = array();
142
143
    /**
144
     * Mimics curl_init in that a URL can be provided
145
     *
146
     * @param string $url
147
     * A full URL string to use for the request, this can include
148
     * basic authentication which will automatically set the
149
     * correct options for the CURL request. Defaults to null
150
     */
151
    public function init($url = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$url" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$url"; expected 0 but found 1
Loading history...
152
    {
153
        if (!is_null($url)) {
154
            $this->setopt('URL', $url);
155
        }
156
    }
157
158
    /**
159
     * Checks to the see if CURL is available, if it isn't, false will
160
     * be returned, and sockets will be used
161
     *
162
     * @return boolean
163
     */
164
    public static function isCurlAvailable()
165
    {
166
        return function_exists('curl_init');
167
    }
168
169
    /**
170
     * Resets `$this->_postfields` variable to an empty string
171
     */
172
    public function flush()
173
    {
174
        $this->_postfields = '';
175
    }
176
177
    /**
178
     * A basic wrapper that simulates the curl_setopt function. Any
179
     * options that are not recognised by Symphony will fallback to
180
     * being added to the `$custom_opt` array. Any options in `$custom_opt`
181
     * will be applied on executed using curl_setopt. Custom options are not
182
     * available for Socket requests. The benefit of using this function is for
183
     * convienience as it performs some basic preprocessing for some options
184
     * such as 'URL', which will take a full formatted URL string and set any
185
     * authentication or SSL curl options automatically
186
     *
187
     * @link http://php.net/manual/en/function.curl-setopt.php
188
     * @param string $opt
189
     *  A string representing a CURL constant. Symphony will intercept the
190
     *  following, URL, POST, POSTFIELDS, USERAGENT, HTTPHEADER,
191
     *  RETURNHEADERS, CONTENTTYPE and TIMEOUT. Any other values
192
     *  will be saved in the `$custom_opt` array.
193
     * @param mixed $value
194
     *  The value of the option, usually boolean or a string. Consult the
195
     *  setopt documentation for more information.
196
     */
197
    public function setopt($opt, $value)
198
    {
199
        switch ($opt) {
200
            case 'URL':
201
                $this->_url = $value;
202
                $url_parsed = parse_url($value);
203
                $this->_host = $url_parsed['host'];
204
205
                if (isset($url_parsed['scheme']) && strlen(trim($url_parsed['scheme'])) > 0) {
206
                    $this->_scheme = $url_parsed['scheme'];
207
                }
208
209
                if (isset($url_parsed['port'])) {
210
                    $this->_port = $url_parsed['port'];
211
                }
212
213
                if (isset($url_parsed['path'])) {
214
                    $this->_path = $url_parsed['path'];
215
                }
216
217
                if (isset($url_parsed['query'])) {
218
                    $this->_path .= '?' . $url_parsed['query'];
219
                }
220
221
                // Allow basic HTTP authentiction
222
                if (isset($url_parsed['user']) && isset($url_parsed['pass'])) {
223
                    $this->setopt(CURLOPT_USERPWD, sprintf('%s:%s', $url_parsed['user'], $url_parsed['pass']));
224
                    $this->setopt(CURLOPT_HTTPAUTH, CURLAUTH_ANY);
225
                }
226
227
                // Better support for HTTPS requests
228
                if ($url_parsed['scheme'] == 'https') {
229
                    $this->setopt(CURLOPT_SSL_VERIFYPEER, false);
230
                }
231
                break;
232
            case 'POST':
233
            case 'GET':
234
            case 'PUT':
235
            case 'DELETE':
236
                $this->_method = ($value == 1 ? $opt : 'GET');
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
237
                break;
238
            case 'POSTFIELDS':
239
                if (is_array($value) && !empty($value)) {
240
                    $this->_postfields = http_build_query($value);
241
                } else {
242
                    $this->_postfields = $value;
243
                }
244
                break;
245
            case 'USERAGENT':
246
                $this->_agent = $value;
247
                break;
248
            case 'HTTPHEADER':
249
                // merge the values, so multiple calls won't erase other values
250
                if (is_array($value)) {
251
                    $this->_headers = array_merge($this->_headers, $value);
252
                } else {
253
                    $this->_headers[] = $value;
254
                }
255
                break;
256
            case 'RETURNHEADERS':
257
                $this->_returnHeaders = (intval($value) == 1 ? true : false);
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
258
                break;
259
            case 'CONTENTTYPE':
260
                $this->_content_type = $value;
261
                break;
262
            case 'TIMEOUT':
263
                $this->_timeout = max(1, intval($value));
264
                break;
265
            default:
266
                $this->_custom_opt[$opt] = $value;
267
                break;
268
        }
269
    }
270
271
    /**
272
     * Executes the request using Curl unless it is not available
273
     * or this function has explicitly been told not by providing
274
     * the `Gateway::FORCE_SOCKET` constant as a parameter. The function
275
     * will apply all the options set using `curl_setopt` before
276
     * executing the request. Information about the transfer is
277
     * available using the `getInfoLast()` function. Should Curl not be
278
     * available, this function will fallback to using Sockets with `fsockopen`
279
     *
280
     * @see toolkit.Gateway#getInfoLast()
281
     * @param string $force_connection_method
282
     *  Only one valid parameter, `Gateway::FORCE_SOCKET`
283
     * @return string|boolean
284
     *  The result of the transfer as a string. If any errors occur during
285
     *  a socket request, false will be returned.
286
     */
287
    public function exec($force_connection_method = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$force_connection_method" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$force_connection_method"; expected 0 but found 1
Loading history...
288
    {
289
        if ($force_connection_method !== self::FORCE_SOCKET && self::isCurlAvailable()) {
290
            $ch = curl_init();
291
292
            curl_setopt($ch, CURLOPT_URL, sprintf(
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

292
            curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_URL, sprintf(
Loading history...
293
                "%s://%s%s%s",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal %s://%s%s%s does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
294
                $this->_scheme,
295
                $this->_host,
296
                (!is_null($this->_port) ? ':' . $this->_port : null),
0 ignored issues
show
introduced by
The condition is_null($this->_port) is always false.
Loading history...
297
                $this->_path
298
            ));
299
            curl_setopt($ch, CURLOPT_HEADER, $this->_returnHeaders);
300
            curl_setopt($ch, CURLOPT_USERAGENT, $this->_agent);
301
            curl_setopt($ch, CURLOPT_PORT, $this->_port);
302
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
303
            curl_setopt($ch, CURLOPT_TIMEOUT, $this->_timeout);
304
305
            if (ini_get('safe_mode') == 0 && ini_get('open_basedir') == '') {
306
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
307
            }
308
309
            switch ($this->_method) {
310
                case 'POST':
311
                    curl_setopt($ch, CURLOPT_POST, 1);
312
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_postfields);
313
                    break;
314
                case 'PUT':
315
                    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
316
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_postfields);
317
                    $this->setopt('HTTPHEADER', array('Content-Length:' => strlen($this->_postfields)));
318
                    break;
319
                case 'DELETE':
320
                    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
321
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_postfields);
322
                    break;
323
            }
324
325
            if (is_array($this->_headers) && !empty($this->_headers)) {
326
                curl_setopt($ch, CURLOPT_HTTPHEADER, $this->_headers);
327
            }
328
329
            if (is_array($this->_custom_opt) && !empty($this->_custom_opt)) {
330
                foreach ($this->_custom_opt as $opt => $value) {
331
                    curl_setopt($ch, $opt, $value);
332
                }
333
            }
334
335
            // Grab the result
336
            $result = curl_exec($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

336
            $result = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
337
338
            $this->_info_last = curl_getinfo($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_getinfo() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

338
            $this->_info_last = curl_getinfo(/** @scrutinizer ignore-type */ $ch);
Loading history...
339
            $this->_info_last['curl_error'] = curl_errno($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_errno() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

339
            $this->_info_last['curl_error'] = curl_errno(/** @scrutinizer ignore-type */ $ch);
Loading history...
340
341
            // Close the connection
342
            curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

342
            curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
343
344
            return $result;
345
        }
346
347
        $start = precision_timer();
348
349
        if (is_null($this->_port)) {
0 ignored issues
show
introduced by
The condition is_null($this->_port) is always false.
Loading history...
350
            $this->_port = (!is_null($this->_scheme) ? self::$ports[$this->_scheme] : 80);
351
        }
352
353
        // No CURL is available, use attempt to use normal sockets
354
        $handle = @fsockopen($this->_host, $this->_port, $errno, $errstr, $this->_timeout);
355
356
        if ($handle === false) {
357
            return false;
358
        }
359
360
        $query = $this->_method . ' ' . $this->_path . ' HTTP/1.1' . PHP_EOL;
361
        $query .= 'Host: '.$this->_host . PHP_EOL;
362
        $query .= 'Content-type: '.$this->_content_type . PHP_EOL;
363
        $query .= 'User-Agent: '.$this->_agent . PHP_EOL;
364
        $query .= @implode(PHP_EOL, $this->_headers);
365
        $query .= 'Content-length: ' . strlen($this->_postfields) . PHP_EOL;
366
        $query .= 'Connection: close' . PHP_EOL . PHP_EOL;
367
368
        if (in_array($this->_method, array('PUT', 'POST', 'DELETE'))) {
369
            $query .= $this->_postfields;
370
        }
371
372
        // send request
373
        if (!@fwrite($handle, $query)) {
374
            return false;
375
        }
376
377
        stream_set_blocking($handle, false);
378
        stream_set_timeout($handle, $this->_timeout);
379
380
        $status = stream_get_meta_data($handle);
381
        $response = $dechunked = '';
382
383
        // get header
384
        while (!preg_match('/\\r\\n\\r\\n$/', $header) && !$status['timed_out']) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $header seems to be never defined.
Loading history...
385
            $header .= @fread($handle, 1);
386
            $status = stream_get_meta_data($handle);
387
        }
388
389
        $status = socket_get_status($handle);
390
391
        // Get rest of the page data
392
        while (!feof($handle) && !$status['timed_out']) {
393
            $response .= fread($handle, 4096);
394
            $status = stream_get_meta_data($handle);
395
        }
396
397
        @fclose($handle);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

397
        /** @scrutinizer ignore-unhandled */ @fclose($handle);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
398
399
        $end = precision_timer('stop', $start);
400
401
        if (preg_match('/Transfer\\-Encoding:\\s+chunked\\r\\n/', $header)) {
402
            $fp = 0;
403
404
            do {
405
                $byte = '';
406
                $chunk_size = '';
407
408
                do {
409
                    $chunk_size .= $byte;
410
                    $byte = substr($response, $fp, 1);
411
                    $fp++;
412
                } while ($byte !== "\r" && $byte !== "\\r");
413
414
                $chunk_size = hexdec($chunk_size); // convert to real number
415
416
                if ($chunk_size == 0) {
417
                    break(1);
418
                }
419
420
                $fp++;
421
422
                $dechunked .= substr($response, $fp, $chunk_size);
0 ignored issues
show
Bug introduced by
It seems like $chunk_size can also be of type double; however, parameter $length of substr() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

422
                $dechunked .= substr($response, $fp, /** @scrutinizer ignore-type */ $chunk_size);
Loading history...
423
                $fp += $chunk_size;
424
425
                $fp += 2;
426
            } while (true);
427
428
            $response = $dechunked;
429
        }
430
431
        // Following code emulates part of the function curl_getinfo()
432
        preg_match('/Content-Type:\s*([^\r\n]+)/i', $header, $match);
433
        $content_type = $match[1];
434
435
        preg_match('/HTTP\/\d+.\d+\s+(\d+)/i', $header, $match);
436
        $status = $match[1];
437
438
        $this->_info_last = array(
439
            'url' => $this->_url,
440
            'content_type' => $content_type,
441
            'http_code' => (int)$status,
442
            'total_time' => $end
443
        );
444
445
        return ($this->_returnHeaders ? $header : null) . $response;
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
446
    }
447
448
449
    /**
450
     * Returns some information about the last transfer, this
451
     * the same output array as expected when calling the
452
     * `curl_getinfo()` function. If Sockets were used to complete
453
     * the request instead of CURL, the resulting array will be
454
     * the HTTP Code, Content Type, URL and Total Time of the resulting
455
     * request
456
     *
457
     * @link http://php.net/manual/en/function.curl-getinfo.php
458
     * @return array
459
     */
460
    public function getInfoLast()
461
    {
462
        return $this->_info_last;
463
    }
464
}
465