GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( b5e3c2...7799bc )
by
unknown
15s
created

Socket::connect()   D

Complexity

Conditions 13
Paths 156

Size

Total Lines 86
Code Lines 32

Duplication

Lines 13
Ratio 15.12 %

Importance

Changes 0
Metric Value
cc 13
eloc 32
nc 156
nop 2
dl 13
loc 86
rs 4.6605
c 0
b 0
f 0

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
 * Part of the Joomla Framework Http Package
4
 *
5
 * @copyright  Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
6
 * @license    GNU General Public License version 2 or later; see LICENSE
7
 */
8
9
namespace Joomla\Http\Transport;
10
11
use Joomla\Http\Exception\InvalidResponseCodeException;
12
use Joomla\Http\TransportInterface;
13
use Joomla\Http\Response;
14
use Joomla\Uri\UriInterface;
15
use Joomla\Uri\Uri;
16
17
/**
18
 * HTTP transport class for using sockets directly.
19
 *
20
 * @since  1.0
21
 */
22
class Socket implements TransportInterface
23
{
24
	/**
25
	 * Reusable socket connections.
26
	 *
27
	 * @var    array
28
	 * @since  1.0
29
	 */
30
	protected $connections;
31
32
	/**
33
	 * The client options.
34
	 *
35
	 * @var    array|\ArrayAccess
36
	 * @since  1.0
37
	 */
38
	protected $options;
39
40
	/**
41
	 * Constructor.
42
	 *
43
	 * @param   array|\ArrayAccess  $options  Client options array.
44
	 *
45
	 * @since   1.0
46
	 * @throws  \RuntimeException
47
	 */
48
	public function __construct($options = array())
49
	{
50
		if (!self::isSupported())
51
		{
52
			throw new \RuntimeException('Cannot use a socket transport when fsockopen() is not available.');
53
		}
54
55
		if (!is_array($options) && !($options instanceof \ArrayAccess))
56
		{
57
			throw new \InvalidArgumentException(
58
				'The options param must be an array or implement the ArrayAccess interface.'
59
			);
60
		}
61
62
		$this->options = $options;
63
	}
64
65
	/**
66
	 * Send a request to the server and return a Response object with the response.
67
	 *
68
	 * @param   string        $method     The HTTP method for sending the request.
69
	 * @param   UriInterface  $uri        The URI to the resource to request.
70
	 * @param   mixed         $data       Either an associative array or a string to be sent with the request.
71
	 * @param   array         $headers    An array of request headers to send with the request.
72
	 * @param   integer       $timeout    Read timeout in seconds.
73
	 * @param   string        $userAgent  The optional user agent string to send with the request.
74
	 *
75
	 * @return  Response
76
	 *
77
	 * @since   1.0
78
	 * @throws  \RuntimeException
79
	 */
80
	public function request($method, UriInterface $uri, $data = null, array $headers = null, $timeout = null, $userAgent = null)
81
	{
82
		$connection = $this->connect($uri, $timeout);
83
84
		// Make sure the connection is alive and valid.
85
		if (is_resource($connection))
86
		{
87
			// Make sure the connection has not timed out.
88
			$meta = stream_get_meta_data($connection);
89
90
			if ($meta['timed_out'])
91
			{
92
				throw new \RuntimeException('Server connection timed out.');
93
			}
94
		}
95
		else
96
		{
97
			throw new \RuntimeException('Not connected to server.');
98
		}
99
100
		// Get the request path from the URI object.
101
		$path = $uri->toString(array('path', 'query'));
102
103
		// If we have data to send make sure our request is setup for it.
104
		if (!empty($data))
105
		{
106
			// If the data is not a scalar value encode it to be sent with the request.
107
			if (!is_scalar($data))
108
			{
109
				$data = http_build_query($data);
110
			}
111
112
			if (!isset($headers['Content-Type']))
113
			{
114
				$headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
115
			}
116
117
			// Add the relevant headers.
118
			$headers['Content-Length'] = strlen($data);
119
		}
120
121
		// Build the request payload.
122
		$request = array();
123
		$request[] = strtoupper($method) . ' ' . ((empty($path)) ? '/' : $path) . ' HTTP/1.0';
124
		$request[] = 'Host: ' . $uri->getHost();
125
126
		// If an explicit user agent is given use it.
127
		if (isset($userAgent))
128
		{
129
			$headers['User-Agent'] = $userAgent;
130
		}
131
132
		// If we have a username then we include basic authentication credentials.
133
		if ($uri->getUser())
134
		{
135
			$authString = $uri->getUser() . ':' . $uri->getPass();
136
			$headers['Authorization'] = 'Basic ' . base64_encode($authString);
137
		}
138
139
		// If there are custom headers to send add them to the request payload.
140
		if (is_array($headers))
141
		{
142
			foreach ($headers as $k => $v)
143
			{
144
				$request[] = $k . ': ' . $v;
145
			}
146
		}
147
148
		// Authentication, if needed
149 View Code Duplication
		if (isset($this->options['userauth']) && isset($this->options['passwordauth']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
150
		{
151
			$request[] = 'Authorization: Basic ' . base64_encode($this->options['userauth'] . ':' . $this->options['passwordauth']);
152
		}
153
154
		// Set any custom transport options
155
		if (isset($this->options['transport.socket']))
156
		{
157
			foreach ($this->options['transport.socket'] as $value)
158
			{
159
				$request[] = $value;
160
			}
161
		}
162
163
		// If we have data to send add it to the request payload.
164
		if (!empty($data))
165
		{
166
			$request[] = null;
167
			$request[] = $data;
168
		}
169
170
		// Send the request to the server.
171
		fwrite($connection, implode("\r\n", $request) . "\r\n\r\n");
172
173
		// Get the response data from the server.
174
		$content = '';
175
176
		while (!feof($connection))
177
		{
178
			$content .= fgets($connection, 4096);
179
		}
180
181
		$content = $this->getResponse($content);
182
183
		// Follow Http redirects
184
		if ($content->code >= 301 && $content->code < 400 && isset($content->headers['Location']))
185
		{
186
			return $this->request($method, new Uri($content->headers['Location']), $data, $headers, $timeout, $userAgent);
187
		}
188
189
		return $content;
190
	}
191
192
	/**
193
	 * Method to get a response object from a server response.
194
	 *
195
	 * @param   string  $content  The complete server response, including headers.
196
	 *
197
	 * @return  Response
198
	 *
199
	 * @since   1.0
200
	 * @throws  \UnexpectedValueException
201
	 * @throws  InvalidResponseCodeException
202
	 */
203
	protected function getResponse($content)
204
	{
205
		// Create the response object.
206
		$return = new Response;
207
208
		if (empty($content))
209
		{
210
			throw new \UnexpectedValueException('No content in response.');
211
		}
212
213
		// Split the response into headers and body.
214
		$response = explode("\r\n\r\n", $content, 2);
215
216
		// Get the response headers as an array.
217
		$headers = explode("\r\n", $response[0]);
218
219
		// Set the body for the response.
220
		$return->body = empty($response[1]) ? '' : $response[1];
221
222
		// Get the response code from the first offset of the response headers.
223
		preg_match('/[0-9]{3}/', array_shift($headers), $matches);
224
		$code = $matches[0];
225
226
		if (is_numeric($code))
227
		{
228
			$return->code = (int) $code;
229
		}
230
		else
231
		// No valid response code was detected.
232
		{
233
			throw new InvalidResponseCodeException('No HTTP response code found.');
234
		}
235
236
		// Add the response headers to the response object.
237 View Code Duplication
		foreach ($headers as $header)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
238
		{
239
			$pos = strpos($header, ':');
240
			$return->headers[trim(substr($header, 0, $pos))] = trim(substr($header, ($pos + 1)));
241
		}
242
243
		return $return;
244
	}
245
246
	/**
247
	 * Method to connect to a server and get the resource.
248
	 *
249
	 * @param   UriInterface  $uri      The URI to connect with.
250
	 * @param   integer       $timeout  Read timeout in seconds.
251
	 *
252
	 * @return  resource  Socket connection resource.
253
	 *
254
	 * @since   1.0
255
	 * @throws  \RuntimeException
256
	 */
257
	protected function connect(UriInterface $uri, $timeout = null)
258
	{
259
		$errno = null;
260
		$err = null;
261
262
		// Get the host from the uri.
263
		$host = ($uri->isSsl()) ? 'ssl://' . $uri->getHost() : $uri->getHost();
264
265
		// If the port is not explicitly set in the URI detect it.
266
		if (!$uri->getPort())
267
		{
268
			$port = ($uri->getScheme() == 'https') ? 443 : 80;
269
		}
270
271
		// Use the set port.
272
		else
273
		{
274
			$port = $uri->getPort();
275
		}
276
277
		// Build the connection key for resource memory caching.
278
		$key = md5($host . $port);
279
280
		// If the connection already exists, use it.
281
		if (!empty($this->connections[$key]) && is_resource($this->connections[$key]))
282
		{
283
			// Connection reached EOF, cannot be used anymore
284
			$meta = stream_get_meta_data($this->connections[$key]);
285
286
			if ($meta['eof'])
287
			{
288
				if (!fclose($this->connections[$key]))
289
				{
290
					throw new \RuntimeException('Cannot close connection');
291
				}
292
			}
293
294
			// Make sure the connection has not timed out.
295
			elseif (!$meta['timed_out'])
296
			{
297
				return $this->connections[$key];
298
			}
299
		}
300
301
		if (!is_numeric($timeout))
302
		{
303
			$timeout = ini_get('default_socket_timeout');
304
		}
305
306
		// Capture PHP errors
307
		$php_errormsg = '';
308
		$track_errors = ini_get('track_errors');
309
		ini_set('track_errors', true);
310
311
		// PHP sends a warning if the uri does not exists; we silence it and throw an exception instead.
312
		// Attempt to connect to the server
313
		$connection = @fsockopen($host, $port, $errno, $err, $timeout);
314
315 View Code Duplication
		if (!$connection)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
316
		{
317
			if (!$php_errormsg)
318
			{
319
				// Error but nothing from php? Create our own
320
				$php_errormsg = sprintf('Could not connect to resource: %s', $uri, $err, $errno);
321
			}
322
323
			// Restore error tracking to give control to the exception handler
324
			ini_set('track_errors', $track_errors);
325
326
			throw new \RuntimeException($php_errormsg);
327
		}
328
329
		// Restore error tracking to what it was before.
330
		ini_set('track_errors', $track_errors);
331
332
		// Since the connection was successful let's store it in case we need to use it later.
333
		$this->connections[$key] = $connection;
334
335
		// If an explicit timeout is set, set it.
336
		if (isset($timeout))
337
		{
338
			stream_set_timeout($this->connections[$key], (int) $timeout);
339
		}
340
341
		return $this->connections[$key];
342
	}
343
344
	/**
345
	 * Method to check if http transport socket available for use
346
	 *
347
	 * @return  boolean   True if available else false
348
	 *
349
	 * @since   1.0
350
	 */
351
	public static function isSupported()
352
	{
353
		return function_exists('fsockopen') && is_callable('fsockopen');
354
	}
355
}
356