Passed
Push — development ( b93807...dda237 )
by Emanuele
01:10 queued 23s
created

Server   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Test Coverage

Coverage 70.9%

Importance

Changes 3
Bugs 0 Features 1
Metric Value
eloc 69
dl 0
loc 272
ccs 39
cts 55
cp 0.709
rs 5.04
c 3
b 0
f 1
wmc 57

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 3
A setMemoryLimit() 0 23 3
A setTimeLimit() 0 25 5
A outPutCompressionEnabled() 0 3 2
A _isValidFQDN() 0 14 4
A _is_web_server() 0 3 2
B is() 0 22 10
B supportsSSL() 0 7 9
C getFQDN() 0 36 12
B supportRewrite() 0 4 7

How to fix   Complexity   

Complex Class

Complex classes like Server often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Server, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file has the functions "describing" the server.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
namespace ElkArte;
18
19
/**
20
 * Class Server
21
 *
22
 * Wrapper around many common server functions and server information
23
 */
24
class Server extends \ArrayObject
25
{
26
	/** @var mixed */
27
	public $SERVER_SOFTWARE;
28
29
	/** @var mixed */
30
	public $HTTPS;
31
32
	/** @var mixed */
33
	public $SERVER_PORT;
34
35
	/**
36
	 * Server constructor.
37
	 *
38
	 * @param null|array $server
39
	 */
40 1
	public function __construct($server = null)
41
	{
42 1
		if (!is_array($server))
43
		{
44
			$server = isset($_SERVER) ? $_SERVER : array();
45
		}
46
47 1
		parent::__construct($server, \ArrayObject::ARRAY_AS_PROPS);
48 1
	}
49
50
	/**
51
	 * Helper function to set the system memory to a needed value
52
	 *
53
	 * What it does:
54
	 *
55
	 * - If the needed memory is greater than current, will attempt to get more
56
	 * - If in_use is set to true, will also try to take the current memory usage in to account
57
	 *
58
	 * @param string $needed The amount of memory to request, if needed, like 256M
59
	 * @param bool $in_use Set to true to account for current memory usage of the script
60
	 *
61
	 * @return bool true if we have at least the needed memory
62
	 */
63 10
	public function setMemoryLimit($needed, $in_use = false)
64
	{
65
		// Everything in bytes
66 10
		$memory_current = memoryReturnBytes(ini_get('memory_limit'));
67 10
		$memory_needed = memoryReturnBytes($needed);
68
69
		// Should we account for how much is currently being used?
70 10
		if ($in_use)
71
		{
72
			$memory_needed += memory_get_usage();
73
		}
74
75
		// If more is needed, request it
76 10
		if ($memory_current < $memory_needed)
77
		{
78
			@ini_set('memory_limit', ceil($memory_needed / 1048576) . 'M');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). 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

78
			/** @scrutinizer ignore-unhandled */ @ini_set('memory_limit', ceil($memory_needed / 1048576) . 'M');

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...
79
			$memory_current = memoryReturnBytes(ini_get('memory_limit'));
80
		}
81
82 10
		$memory_current = max($memory_current, memoryReturnBytes(get_cfg_var('memory_limit')));
0 ignored issues
show
Bug introduced by
It seems like get_cfg_var('memory_limit') can also be of type array; however, parameter $val of memoryReturnBytes() does only seem to accept string, 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

82
		$memory_current = max($memory_current, memoryReturnBytes(/** @scrutinizer ignore-type */ get_cfg_var('memory_limit')));
Loading history...
83
84
		// Return success or not
85 10
		return (bool) ($memory_current >= $memory_needed);
86
	}
87
88
	/**
89
	 * Wrapper function for set_time_limit
90
	 *
91
	 * When called, attempts to restart the timeout counter from zero.
92
	 *
93
	 * This sets the maximum time in seconds a script is allowed to run before it is terminated by the parser.
94
	 * You can not change this setting with ini_set() when running in safe mode.
95
	 * Your web server can have other timeout configurations that may also interrupt PHP execution.
96
	 * Apache has a Timeout directive and IIS has a CGI timeout function.
97
	 * Security extension may also disable this function, such as Suhosin
98
	 * Hosts may add this to the disabled_functions list in php.ini
99
	 *
100
	 * If the current time limit is not unlimited it is possible to decrease the
101
	 * total time limit if the sum of the new time limit and the current time spent
102
	 * running the script is inferior to the original time limit. It is inherent to
103
	 * the way set_time_limit() works, it should rather be called with an
104
	 * appropriate value every time you need to allocate a certain amount of time
105
	 * to execute a task than only once at the beginning of the script.
106
	 *
107
	 * Before calling set_time_limit(), we check if this function is available
108
	 *
109
	 * @param int $time_limit The time limit
110
	 * @param bool $server_reset whether to reset the server timer or not
111
	 *
112
	 * @return string
113
	 */
114 21
	public function setTimeLimit($time_limit, $server_reset = true)
115
	{
116
		// Make sure the function exists, it may be in the ini disable_functions list
117 21
		if (function_exists('set_time_limit'))
118
		{
119 21
			$current = (int) ini_get('max_execution_time');
120
121
			// Do not set a limit if it is currently unlimited.
122 21
			if ($current !== 0)
123
			{
124
				// Set it to the maximum that we can, not more, not less
125
				$time_limit = min($current, max($time_limit, $current));
126
127
				// Still need error suppression as some security addons many prevent this action
128
				@set_time_limit($time_limit);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). 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

128
				/** @scrutinizer ignore-unhandled */ @set_time_limit($time_limit);

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...
129
			}
130
		}
131
132
		// Don't let apache close the connection
133 21
		if ($server_reset && function_exists('apache_reset_timeout'))
134
		{
135
			@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for apache_reset_timeout(). 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

135
			/** @scrutinizer ignore-unhandled */ @apache_reset_timeout();

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...
136
		}
137
138 21
		return ini_get('max_execution_time');
139
	}
140
141
	/**
142
	 * Checks the type of software the webserver is functioning under
143
	 *
144
	 * @param $server
145
	 *
146
	 * @return bool
147
	 */
148 230
	public function is($server)
149
	{
150 116
		switch ($server)
151
		{
152 230
			case 'apache':
153 1
				return $this->_is_web_server('Apache');
154 230
			case 'cgi':
155 1
				return isset($this->SERVER_SOFTWARE) && strpos(php_sapi_name(), 'cgi') !== false;
156 230
			case 'iis':
157 1
				return $this->_is_web_server('Microsoft-IIS');
158 230
			case 'iso_case_folding':
159 229
				return ord(strtolower(chr(138))) === 154;
160 6
			case 'lighttpd':
161 1
				return $this->_is_web_server('lighttpd');
162 6
			case 'litespeed':
163 1
				return $this->_is_web_server('LiteSpeed');
164 6
			case 'nginx':
165 1
				return $this->_is_web_server('nginx');
166 6
			case 'windows':
167 1
				return strpos(PHP_OS, 'WIN') === 0;
168 6
			default:
169 6
				return false;
170
		}
171
	}
172
173
	/**
174
	 * Search $_SERVER['SERVER_SOFTWARE'] for a give $type
175
	 *
176
	 * @param string $type
177
	 *
178
	 * @return bool
179
	 */
180
	private function _is_web_server($type)
181
	{
182 1
		return isset($this->SERVER_SOFTWARE) && strpos($this->SERVER_SOFTWARE, $type) !== false;
183
	}
184 1
185
	/**
186
	 * Checks if the webserver supports rewrite
187
	 *
188
	 * @return bool
189
	 */
190
	public function supportRewrite()
191
	{
192
		return (!$this->is('cgi') || ini_get('cgi.fix_pathinfo') == 1 || @get_cfg_var('cgi.fix_pathinfo') == 1)
193
			&& ($this->is('apache') || $this->is('nginx') || $this->is('lighttpd') || $this->is('litespeed'));
194
	}
195
196
	/**
197
	 * Returns if the system supports output compression
198
	 *
199
	 * @return bool
200
	 */
201
	public function outPutCompressionEnabled()
202
	{
203
		return ini_get('zlib.output_compression') >= 1 || ini_get('output_handler') == 'ob_gzhandler';
204
	}
205
206
	/**
207
	 * Returns if the system supports / is using https connections
208
	 *
209
	 * @return bool
210
	 */
211
	public function supportsSSL()
212
	{
213
		return
214
			(isset($this->HTTPS) && ($this->HTTPS === 'on' || $this->HTTPS == 1))
215
			|| (isset($this->REQUEST_SCHEME) && $this->REQUEST_SCHEME === 'https')
216
			|| (isset($this->SERVER_PORT) && $this->SERVER_PORT == 443)
217
			|| (isset($this->HTTP_X_FORWARDED_PROTO) && $this->HTTP_X_FORWARDED_PROTO === 'https');
218
	}
219
220
	/**
221
	 * Try to determine a FQDN for the server
222
	 *
223
	 * Many SMTP servers *require* a fully qualified domain name in the
224
	 * HELO/EHLO command.  This function tries to determine the fully qualified domain name
225
	 * from the OS which often just returns the current host name, like bob, rather than a FQDN.
226
	 *
227
	 * From the rfc:
228
	 * The SMTP client MUST, if possible, ensure that the domain parameter to the EHLO
229
	 * command is a valid principal host name (not a CNAME or MX name) for its host. If
230
	 * this is not possible (e.g., when the client's address is dynamically assigned and
231
	 * the client does not have an obvious name), an address literal SHOULD be substituted
232
	 * for the domain name and supplemental information provided that will assist in
233
	 * identifying the client.
234
	 *
235
	 * @param string $fallback the fallback to use when we fail
236
	 * @return string a FQDN
237
	 */
238
	public function getFQDN($fallback = '[127.0.0.1]')
239
	{
240
		// Try gethostname
241
		if (function_exists('gethostname') && $this->_isValidFQDN(gethostname()))
242
		{
243
			return gethostname();
244
		}
245
246
		// Failing, try php_uname
247
		if (function_exists('php_uname') && $this->_isValidFQDN(php_uname('n')))
248
		{
249
			return php_uname('n');
250
		}
251
252
		// This is likely a sitename vs host
253
		if (!empty($_SERVER['SERVER_NAME']) && $this->_isValidFQDN($_SERVER['SERVER_NAME']))
254
		{
255
			return $_SERVER['SERVER_NAME'];
256
		}
257
258
		// Try a reverse IP lookup on the server addr
259
		if (!empty($_SERVER['SERVER_ADDR']) && $this->_isValidFQDN((host_from_ip($_SERVER['SERVER_ADDR']))))
260
		{
261
			return host_from_ip($_SERVER['SERVER_ADDR']);
262
		}
263
264
		// Literal it is, but some SMTP servers may not accept this
265
		if (!empty($_SERVER['SERVER_ADDR']) && $fallback === '[127.0.0.1]')
266
		{
267
			// Set the address literal prefix
268
			$prefix = strpos($_SERVER['SERVER_ADDR'], ':' !== false) ? 'IPv6:' : '';
0 ignored issues
show
Bug introduced by
':' !== false of type boolean is incompatible with the type string expected by parameter $needle of strpos(). ( Ignorable by Annotation )

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

268
			$prefix = strpos($_SERVER['SERVER_ADDR'], /** @scrutinizer ignore-type */ ':' !== false) ? 'IPv6:' : '';
Loading history...
269
270
			return  '[' . $prefix . $_SERVER['SERVER_ADDR'] . ']';
271
		}
272
273
		return $fallback;
274
	}
275
276
	/**
277
	 * Checks if this is a valid FQDN first by basic syntax and then if it has domain records
278
	 *
279
	 * @param string $hostname
280
	 * @return bool
281
	 */
282
	private function _isValidFQDN($hostname)
283
	{
284
		if (empty($hostname) || strpos($hostname, '.') === false)
285
		{
286
			return false;
287
		}
288
289
		if (preg_match('~^(?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63}$~', $hostname) === 1)
290
		{
291
			// Check for ANY dns records for this name for simplicity although we really want A / AAAA
292
			return checkdnsrr($hostname, 'ANY');
293
		}
294
295
		return false;
296
	}
297
}
298