Completed
Push — release-2.1 ( 15f485...ae1be0 )
by Mathias
08:27
created

Subs-Db-mysql.php ➔ smf_db_replacement__callback()   F

Complexity

Conditions 47
Paths 336

Size

Total Lines 153
Code Lines 104

Duplication

Lines 26
Ratio 16.99 %

Importance

Changes 0
Metric Value
cc 47
eloc 104
nc 336
nop 1
dl 26
loc 153
rs 3.3333
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
/**
4
 * This file has all the main functions in it that relate to the database.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 3
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 *  Maps the implementations in this file (smf_db_function_name)
21
 *  to the $smcFunc['db_function_name'] variable.
22
 *
23
 * @param string $db_server The database server
24
 * @param string $db_name The name of the database
25
 * @param string $db_user The database username
26
 * @param string $db_passwd The database password
27
 * @param string $db_prefix The table prefix
28
 * @param array $db_options An array of database options
29
 * @return null|resource Returns null on failure if $db_options['non_fatal'] is true or a MySQL connection resource handle if the connection was successful.
30
 */
31
function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options = array())
0 ignored issues
show
Unused Code introduced by
The parameter $db_prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
32
{
33
	global $smcFunc, $mysql_set_mode;
34
35
	// Map some database specific functions, only do this once.
36 View Code Duplication
	if (!isset($smcFunc['db_fetch_assoc']))
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...
37
		$smcFunc += array(
38
			'db_query'                  => 'smf_db_query',
39
			'db_quote'                  => 'smf_db_quote',
40
			'db_fetch_assoc'            => 'mysqli_fetch_assoc',
41
			'db_fetch_row'              => 'mysqli_fetch_row',
42
			'db_free_result'            => 'mysqli_free_result',
43
			'db_insert'                 => 'smf_db_insert',
44
			'db_insert_id'              => 'smf_db_insert_id',
45
			'db_num_rows'               => 'mysqli_num_rows',
46
			'db_data_seek'              => 'mysqli_data_seek',
47
			'db_num_fields'             => 'mysqli_num_fields',
48
			'db_escape_string'          => 'addslashes',
49
			'db_unescape_string'        => 'stripslashes',
50
			'db_server_info'            => 'smf_db_get_server_info',
51
			'db_affected_rows'          => 'smf_db_affected_rows',
52
			'db_transaction'            => 'smf_db_transaction',
53
			'db_error'                  => 'mysqli_error',
54
			'db_select_db'              => 'smf_db_select',
55
			'db_title'                  => 'MySQLi',
56
			'db_sybase'                 => false,
57
			'db_case_sensitive'         => false,
58
			'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string',
59
			'db_is_resource'            => 'smf_is_resource',
60
			'db_mb4'                    => false,
61
			'db_ping'                   => 'mysqli_ping',
62
		);
63
64
	if (!empty($db_options['persist']))
65
		$db_server = 'p:' . $db_server;
66
67
	$connection = mysqli_init();
68
69
	$flags = MYSQLI_CLIENT_FOUND_ROWS;
70
71
	$success = false;
72
73
	if ($connection) {
74
		if (!empty($db_options['port']))
75
			$success = mysqli_real_connect($connection, $db_server, $db_user, $db_passwd, '', $db_options['port'], null, $flags);
76
		else
77
			$success = mysqli_real_connect($connection, $db_server, $db_user, $db_passwd, '', 0, null, $flags);
78
	}
79
80
	// Something's wrong, show an error if its fatal (which we assume it is)
81
	if ($success === false)
82
	{
83
		if (!empty($db_options['non_fatal']))
84
			return null;
85
		else
86
			display_db_error();
87
	}
88
89
	// Select the database, unless told not to
90
	if (empty($db_options['dont_select_db']) && !@mysqli_select_db($connection, $db_name) && empty($db_options['non_fatal']))
91
		display_db_error();
92
93
	// This makes it possible to have SMF automatically change the sql_mode and autocommit if needed.
94
	if (isset($mysql_set_mode) && $mysql_set_mode === true)
95
		$smcFunc['db_query']('', 'SET sql_mode = \'\', AUTOCOMMIT = 1',
96
		array(),
97
		false
98
	);
99
100
	return $connection;
101
}
102
103
/**
104
 * Extend the database functionality. It calls the respective file's init
105
 * to add the implementations in that file to $smcFunc array.
106
 *
107
 * @param string $type Indicates which additional file to load. ('extra', 'packages')
108
 */
109
function db_extend($type = 'extra')
110
{
111
	global $sourcedir;
112
113
	// we force the MySQL files as nothing syntactically changes with MySQLi
114
	require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-mysql.php');
115
	$initFunc = 'db_' . $type . '_init';
116
	$initFunc();
117
}
118
119
/**
120
 * Fix up the prefix so it doesn't require the database to be selected.
121
 *
122
 * @param string &$db_prefix The table prefix
123
 * @param string $db_name The database name
124
 */
125
function db_fix_prefix(&$db_prefix, $db_name)
0 ignored issues
show
Unused Code introduced by
The parameter $db_prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $db_name is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
126
{
127
	$db_prefix = is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix;
128
}
129
130
/**
131
 * Wrap mysqli_select_db so the connection does not need to be specified
132
 *
133
 * @param string &$database The database
134
 * @param object $connection The connection object (if null, $db_connection is used)
135
 * @return bool Whether the database was selected
136
 */
137
function smf_db_select($database, $connection = null)
138
{
139
	global $db_connection;
140
	return mysqli_select_db($connection === null ? $db_connection : $connection, $database);
141
}
142
143
/**
144
 * Wrap mysqli_get_server_info so the connection does not need to be specified
145
 *
146
 * @param object $connection The connection to use (if null, $db_connection is used)
147
 * @return string The server info
148
 */
149
function smf_db_get_server_info($connection = null)
150
{
151
	global $db_connection;
152
	return mysqli_get_server_info($connection === null ? $db_connection : $connection);
153
}
154
155
/**
156
 * Callback for preg_replace_callback on the query.
157
 * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board', etc), with
158
 * their current values from $user_info.
159
 * In addition, it performs checks and sanitization on the values sent to the database.
160
 *
161
 * @param array $matches The matches from preg_replace_callback
162
 * @return string The appropriate string depending on $matches[1]
163
 */
164
function smf_db_replacement__callback($matches)
165
{
166
	global $db_callback, $user_info, $db_prefix, $smcFunc;
167
168
	list ($values, $connection) = $db_callback;
169
	if (!is_object($connection))
170
		display_db_error();
171
172
	if ($matches[1] === 'db_prefix')
173
		return $db_prefix;
174
175 View Code Duplication
	if (!empty($user_info))
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...
176
	{
177
		foreach (array_keys($user_info) as $key)
178
			if (strpos($key, 'query_') !== false && $key === $matches[1])
179
				return $user_info[$matches[1]];
180
	}
181
182
	if ($matches[1] === 'empty')
183
		return '\'\'';
184
185
	if (!isset($matches[2]))
186
		smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__);
187
188
	if ($matches[1] === 'literal')
189
		return '\'' . mysqli_real_escape_string($connection, $matches[2]) . '\'';
190
191
	if (!isset($values[$matches[2]]))
192
		smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . (isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($matches[2]) : htmlspecialchars($matches[2])), '', E_USER_ERROR, __FILE__, __LINE__);
193
194
	$replacement = $values[$matches[2]];
195
196
	switch ($matches[1])
197
	{
198
		case 'int':
199
			if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement)
200
				smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
201
			return (string) (int) $replacement;
202
		break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
203
204
		case 'string':
205
		case 'text':
206
			return sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $replacement));
207
		break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
208
209
		case 'array_int':
210
			if (is_array($replacement))
211
			{
212
				if (empty($replacement))
213
					smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
214
215
				foreach ($replacement as $key => $value)
216
				{
217
					if (!is_numeric($value) || (string) $value !== (string) (int) $value)
218
						smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
219
220
					$replacement[$key] = (string) (int) $value;
221
				}
222
223
				return implode(', ', $replacement);
224
			}
225
			else
226
				smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
227
228
		break;
229
230
		case 'array_string':
231
			if (is_array($replacement))
232
			{
233
				if (empty($replacement))
234
					smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
235
236
				foreach ($replacement as $key => $value)
237
					$replacement[$key] = sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $value));
238
239
				return implode(', ', $replacement);
240
			}
241
			else
242
				smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
243
		break;
244
245 View Code Duplication
		case 'date':
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...
246
			if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1)
247
				return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]);
248
			else
249
				smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
250
		break;
251
252 View Code Duplication
		case 'time':
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...
253
			if (preg_match('~^([0-1]?\d|2[0-3]):([0-5]\d):([0-5]\d)$~', $replacement, $time_matches) === 1)
254
				return sprintf('\'%02d:%02d:%02d\'', $time_matches[1], $time_matches[2], $time_matches[3]);
255
			else
256
				smf_db_error_backtrace('Wrong value type sent to the database. Time expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
257
		break;
258
259 View Code Duplication
		case 'datetime':
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...
260
			if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d) ([0-1]?\d|2[0-3]):([0-5]\d):([0-5]\d)$~', $replacement, $datetime_matches) === 1)
261
				return 'str_to_date('.
262
					sprintf('\'%04d-%02d-%02d %02d:%02d:%02d\'', $datetime_matches[1], $datetime_matches[2], $datetime_matches[3], $datetime_matches[4], $datetime_matches[5] ,$datetime_matches[6]).
263
					',\'%Y-%m-%d %h:%i:%s\')';
264
			else
265
				smf_db_error_backtrace('Wrong value type sent to the database. Datetime expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
266
		break;
267
268
		case 'float':
269
			if (!is_numeric($replacement))
270
				smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
271
			return (string) (float) $replacement;
272
		break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
273
274
		case 'identifier':
275
			// Backticks inside identifiers are supported as of MySQL 4.1. We don't need them for SMF.
276
			return '`' . strtr($replacement, array('`' => '', '.' => '`.`')) . '`';
277
		break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
278
279
		case 'raw':
280
			return $replacement;
281
		break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
282
283
		case 'inet':
284
			if ($replacement == 'null' || $replacement == '')
285
				return 'null';
286
			if (!isValidIP($replacement))
287
				smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
288
			//we don't use the native support of mysql > 5.6.2
289
			return sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($replacement)));
290
291
		case 'array_inet':
292
			if (is_array($replacement))
293
			{
294
				if (empty($replacement))
295
					smf_db_error_backtrace('Database error, given array of IPv4 or IPv6 values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
296
297
				foreach ($replacement as $key => $value)
298
				{
299
					if ($replacement == 'null' || $replacement == '')
300
						$replacement[$key] = 'null';
301
					if (!isValidIP($value))
302
						smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
303
					$replacement[$key] = sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($value)));
304
				}
305
306
				return implode(', ', $replacement);
307
			}
308
			else
309
				smf_db_error_backtrace('Wrong value type sent to the database. Array of IPv4 or IPv6 expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
310
		break;
311
312
		default:
313
			smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__);
314
		break;
315
	}
316
}
317
318
/**
319
 * Just like the db_query, escape and quote a string, but not executing the query.
320
 *
321
 * @param string $db_string The database string
322
 * @param array $db_values An array of values to be injected into the string
323
 * @param resource $connection = null The connection to use (null to use $db_connection)
324
 * @return string The string with the values inserted
325
 */
326 View Code Duplication
function smf_db_quote($db_string, $db_values, $connection = null)
0 ignored issues
show
Duplication introduced by
This function 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...
327
{
328
	global $db_callback, $db_connection;
329
330
	// Only bother if there's something to replace.
331
	if (strpos($db_string, '{') !== false)
332
	{
333
		// This is needed by the callback function.
334
		$db_callback = array($db_values, $connection === null ? $db_connection : $connection);
335
336
		// Do the quoting and escaping
337
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
338
339
		// Clear this global variable.
340
		$db_callback = array();
341
	}
342
343
	return $db_string;
344
}
345
346
/**
347
 * Do a query.  Takes care of errors too.
348
 *
349
 * @param string $identifier An identifier. Only used in Postgres when we need to do things differently...
350
 * @param string $db_string The database string
351
 * @param array $db_values = array() The values to be inserted into the string
352
 * @param resource $connection = null The connection to use (null to use $db_connection)
353
 * @return resource|bool Returns a MySQL result resource (for SELECT queries), true (for UPDATE queries) or false if the query failed
354
 */
355
function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null)
0 ignored issues
show
Unused Code introduced by
The parameter $identifier is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
356
{
357
	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
358
	global $db_unbuffered, $db_callback, $modSettings;
359
360
	// Comments that are allowed in a query are preg_removed.
361
	static $allowed_comments_from = array(
362
		'~\s+~s',
363
		'~/\*!40001 SQL_NO_CACHE \*/~',
364
		'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
365
		'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
366
	);
367
	static $allowed_comments_to = array(
368
		' ',
369
		'',
370
		'',
371
		'',
372
	);
373
374
	// Decide which connection to use.
375
	$connection = $connection === null ? $db_connection : $connection;
376
377
	// Get a connection if we are shutting down, sometimes the link is closed before sessions are written
378
	if (!is_object($connection))
379
	{
380
		global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
381
382
		// Are we in SSI mode?  If so try that username and password first
383
		if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
384
		{
385
			if (empty($db_persist))
0 ignored issues
show
Bug introduced by
The variable $db_persist seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
386
				$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
387
			else
388
				$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
389
		}
390
		// Fall back to the regular username and password if need be
391
		if (!$db_connection)
392
		{
393
			if (empty($db_persist))
394
				$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
395
			else
396
				$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
397
		}
398
399
		if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
400
			$db_connection = false;
401
402
		$connection = $db_connection;
403
	}
404
405
	// One more query....
406
	$db_count = !isset($db_count) ? 1 : $db_count + 1;
407
408 View Code Duplication
	if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
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...
409
		smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
410
411
	// Use "ORDER BY null" to prevent Mysql doing filesorts for Group By clauses without an Order By
412
	if (strpos($db_string, 'GROUP BY') !== false && strpos($db_string, 'ORDER BY') === false && preg_match('~^\s+SELECT~i', $db_string))
413
	{
414
		// Add before LIMIT
415
		if ($pos = strpos($db_string, 'LIMIT '))
416
			$db_string = substr($db_string, 0, $pos) . "\t\t\tORDER BY null\n" . substr($db_string, $pos, strlen($db_string));
417
		else
418
			// Append it.
419
			$db_string .= "\n\t\t\tORDER BY null";
420
	}
421
422 View Code Duplication
	if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
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...
423
	{
424
		// Pass some values to the global space for use in the callback function.
425
		$db_callback = array($db_values, $connection);
426
427
		// Inject the values passed to this function.
428
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
429
430
		// This shouldn't be residing in global space any longer.
431
		$db_callback = array();
432
	}
433
434
	// Debugging.
435 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
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...
436
	{
437
		// Get the file and line number this function was called.
438
		list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
439
440
		// Initialize $db_cache if not already initialized.
441
		if (!isset($db_cache))
442
			$db_cache = array();
443
444
		if (!empty($_SESSION['debug_redirect']))
445
		{
446
			$db_cache = array_merge($_SESSION['debug_redirect'], $db_cache);
447
			$db_count = count($db_cache) + 1;
448
			$_SESSION['debug_redirect'] = array();
449
		}
450
451
		// Don't overload it.
452
		$st = microtime();
453
		$db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...';
454
		$db_cache[$db_count]['f'] = $file;
455
		$db_cache[$db_count]['l'] = $line;
456
		$db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start));
457
	}
458
459
	// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
460 View Code Duplication
	if (empty($modSettings['disableQueryCheck']))
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...
461
	{
462
		$clean = '';
463
		$old_pos = 0;
464
		$pos = -1;
465
		while (true)
466
		{
467
			$pos = strpos($db_string, '\'', $pos + 1);
468
			if ($pos === false)
469
				break;
470
			$clean .= substr($db_string, $old_pos, $pos - $old_pos);
471
472
			while (true)
473
			{
474
				$pos1 = strpos($db_string, '\'', $pos + 1);
475
				$pos2 = strpos($db_string, '\\', $pos + 1);
476
				if ($pos1 === false)
477
					break;
478
				elseif ($pos2 === false || $pos2 > $pos1)
479
				{
480
					$pos = $pos1;
481
					break;
482
				}
483
484
				$pos = $pos2 + 1;
485
			}
486
			$clean .= ' %s ';
487
488
			$old_pos = $pos + 1;
489
		}
490
		$clean .= substr($db_string, $old_pos);
491
		$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
492
493
		// Comments?  We don't use comments in our queries, we leave 'em outside!
494
		if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
495
			$fail = true;
496
		// Trying to change passwords, slow us down, or something?
497
		elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
498
			$fail = true;
499
		elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
500
			$fail = true;
501
502
		if (!empty($fail) && function_exists('log_error'))
503
			smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
504
	}
505
506
	if (empty($db_unbuffered))
507
		$ret = @mysqli_query($connection, $db_string);
508
	else
509
		$ret = @mysqli_query($connection, $db_string, MYSQLI_USE_RESULT);
510
511 View Code Duplication
	if ($ret === false && empty($db_values['db_error_skip']))
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...
512
		$ret = smf_db_error($db_string, $connection);
513
514
	// Debugging.
515 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
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...
516
		$db_cache[$db_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
0 ignored issues
show
Bug introduced by
The variable $st does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
517
518
	return $ret;
519
}
520
521
/**
522
 * affected_rows
523
 * @param resource $connection A connection to use (if null, $db_connection is used)
524
 * @return int The number of rows affected by the last query
525
 */
526
function smf_db_affected_rows($connection = null)
527
{
528
	global $db_connection;
529
530
	return mysqli_affected_rows($connection === null ? $db_connection : $connection);
531
}
532
533
/**
534
 * Gets the ID of the most recently inserted row.
535
 *
536
 * @param string $table The table (only used for Postgres)
537
 * @param string $field = null The specific field (not used here)
538
 * @param resource $connection = null The connection (if null, $db_connection is used)
539
 * @return int The ID of the most recently inserted row
540
 */
541
function smf_db_insert_id($table, $field = null, $connection = null)
0 ignored issues
show
Unused Code introduced by
The parameter $table is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $field is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $connection is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
542
{
543
	global $db_connection;
544
545
	// MySQL doesn't need the table or field information.
546
	return mysqli_insert_id($connection === null ? $db_connection : $connection);
547
}
548
549
/**
550
 * Do a transaction.
551
 *
552
 * @param string $type The step to perform (i.e. 'begin', 'commit', 'rollback')
553
 * @param resource $connection The connection to use (if null, $db_connection is used)
554
 * @return bool True if successful, false otherwise
555
 */
556 View Code Duplication
function smf_db_transaction($type = 'commit', $connection = null)
0 ignored issues
show
Duplication introduced by
This function 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...
557
{
558
	global $db_connection;
559
560
	// Decide which connection to use
561
	$connection = $connection === null ? $db_connection : $connection;
562
563
	if ($type == 'begin')
564
		return @mysqli_query($connection, 'BEGIN');
565
	elseif ($type == 'rollback')
566
		return @mysqli_query($connection, 'ROLLBACK');
567
	elseif ($type == 'commit')
568
		return @mysqli_query($connection, 'COMMIT');
569
570
	return false;
571
}
572
573
/**
574
 * Database error!
575
 * Backtrace, log, try to fix.
576
 *
577
 * @param string $db_string The DB string
578
 * @param object $connection The connection to use (if null, $db_connection is used)
579
 */
580
function smf_db_error($db_string, $connection = null)
581
{
582
	global $txt, $context, $sourcedir, $webmaster_email, $modSettings;
583
	global $db_connection, $db_last_error, $db_persist;
584
	global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
585
	global $smcFunc;
586
587
	// Get the file and line numbers.
588
	list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
589
590
	// Decide which connection to use
591
	$connection = $connection === null ? $db_connection : $connection;
592
593
	// This is the error message...
594
	$query_error = mysqli_error($connection);
595
	$query_errno = mysqli_errno($connection);
596
597
	// Error numbers:
598
	//    1016: Can't open file '....MYI'
599
	//    1030: Got error ??? from table handler.
600
	//    1034: Incorrect key file for table.
601
	//    1035: Old key file for table.
602
	//    1205: Lock wait timeout exceeded.
603
	//    1213: Deadlock found.
604
	//    2006: Server has gone away.
605
	//    2013: Lost connection to server during query.
606
607
	// Log the error.
608
	if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error'))
609
		log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line);
610
611
	// Database error auto fixing ;).
612
	if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1'))
613
	{
614
		// Force caching on, just for the error checking.
615
		$old_cache = @$modSettings['cache_enable'];
616
		$modSettings['cache_enable'] = '1';
617
618
		if (($temp = cache_get_data('db_last_error', 600)) !== null)
619
			$db_last_error = max(@$db_last_error, $temp);
620
621
		if (@$db_last_error < time() - 3600 * 24 * 3)
622
		{
623
			// We know there's a problem... but what?  Try to auto detect.
624
			if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false)
625
			{
626
				preg_match_all('~(?:[\n\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\n\r(]+?(?:, )?)*)~s', $db_string, $matches);
627
628
				$fix_tables = array();
629
				foreach ($matches[1] as $tables)
630
				{
631
					$tables = array_unique(explode(',', $tables));
632
					foreach ($tables as $table)
633
					{
634
						// Now, it's still theoretically possible this could be an injection.  So backtick it!
635
						if (trim($table) != '')
636
							$fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`';
637
					}
638
				}
639
640
				$fix_tables = array_unique($fix_tables);
641
			}
642
			// Table crashed.  Let's try to fix it.
643
			elseif ($query_errno == 1016)
644
			{
645
				if (preg_match('~\'([^\.\']+)~', $query_error, $match) != 0)
646
					$fix_tables = array('`' . $match[1] . '`');
647
			}
648
			// Indexes crashed.  Should be easy to fix!
649
			elseif ($query_errno == 1034 || $query_errno == 1035)
650
			{
651
				preg_match('~\'([^\']+?)\'~', $query_error, $match);
652
				$fix_tables = array('`' . $match[1] . '`');
653
			}
654
		}
655
656
		// Check for errors like 145... only fix it once every three days, and send an email. (can't use empty because it might not be set yet...)
657
		if (!empty($fix_tables))
658
		{
659
			// Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail().
660
			require_once($sourcedir . '/Subs-Admin.php');
661
			require_once($sourcedir . '/Subs-Post.php');
662
663
			// Make a note of the REPAIR...
664
			cache_put_data('db_last_error', time(), 600);
665
			if (($temp = cache_get_data('db_last_error', 600)) === null)
666
				updateSettingsFile(array('db_last_error' => time()));
667
668
			// Attempt to find and repair the broken table.
669
			foreach ($fix_tables as $table)
670
				$smcFunc['db_query']('', "
671
					REPAIR TABLE $table", false, false);
672
673
			// And send off an email!
674
			sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair'], null, 'dberror');
675
676
			$modSettings['cache_enable'] = $old_cache;
677
678
			// Try the query again...?
679
			$ret = $smcFunc['db_query']('', $db_string, false, false);
680
			if ($ret !== false)
681
				return $ret;
682
		}
683
		else
684
			$modSettings['cache_enable'] = $old_cache;
685
686
		// Check for the "lost connection" or "deadlock found" errors - and try it just one more time.
687
		if (in_array($query_errno, array(1205, 1213, 2006, 2013)))
688
		{
689
			if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection)
690
			{
691
				// Are we in SSI mode?  If so try that username and password first
692
				if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
693
				{
694
					if (empty($db_persist))
695
						$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
696
					else
697
						$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
698
				}
699
				// Fall back to the regular username and password if need be
700
				if (!$db_connection)
701
				{
702
					if (empty($db_persist))
703
						$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
704
					else
705
						$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
706
				}
707
708
				if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
709
					$db_connection = false;
710
			}
711
712
			if ($db_connection)
713
			{
714
				// Try a deadlock more than once more.
715
				for ($n = 0; $n < 4; $n++)
716
				{
717
					$ret = $smcFunc['db_query']('', $db_string, false, false);
718
719
					$new_errno = mysqli_errno($db_connection);
720
					if ($ret !== false || in_array($new_errno, array(1205, 1213)))
721
						break;
722
				}
723
724
				// If it failed again, shucks to be you... we're not trying it over and over.
725
				if ($ret !== false)
726
					return $ret;
0 ignored issues
show
Bug introduced by
The variable $ret does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
727
			}
728
		}
729
		// Are they out of space, perhaps?
730
		elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false))
731
		{
732
			if (!isset($txt))
733
				$query_error .= ' - check database storage space.';
734
			else
735
			{
736
				if (!isset($txt['mysql_error_space']))
737
					loadLanguage('Errors');
738
739
				$query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space'];
740
			}
741
		}
742
	}
743
744
	// Nothing's defined yet... just die with it.
745
	if (empty($context) || empty($txt))
746
		die($query_error);
747
748
	// Show an error message, if possible.
749
	$context['error_title'] = $txt['database_error'];
750
	if (allowedTo('admin_forum'))
751
		$context['error_message'] = nl2br($query_error) . '<br>' . $txt['file'] . ': ' . $file . '<br>' . $txt['line'] . ': ' . $line;
752
	else
753
		$context['error_message'] = $txt['try_again'];
754
755
	if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true)
756
	{
757
		$context['error_message'] .= '<br><br>' . nl2br($db_string);
758
	}
759
760
	// It's already been logged... don't log it again.
761
	fatal_error($context['error_message'], false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
762
}
763
764
/**
765
 * Inserts data into a table
766
 *
767
 * @param string $method The insert method - can be 'replace', 'ignore' or 'insert'
768
 * @param string $table The table we're inserting the data into
769
 * @param array $columns An array of the columns we're inserting the data into. Should contain 'column' => 'datatype' pairs
770
 * @param array $data The data to insert
771
 * @param array $keys The keys for the table
772
 * @param int $returnmode 0 = nothing(default), 1 = last row id, 2 = all rows id as array; every mode runs only with method = ''
773
 * @param object $connection The connection to use (if null, $db_connection is used)
774
 * @return mixed value of the first key, behavior based on returnmode. null if no data.
775
 */
776
function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $returnmode = 0, $connection = null)
777
{
778
	global $smcFunc, $db_connection, $db_prefix;
779
780
	$connection = $connection === null ? $db_connection : $connection;
781
782
	// With nothing to insert, simply return.
783
	if (empty($data))
784
		return;
785
786
	// Replace the prefix holder with the actual prefix.
787
	$table = str_replace('{db_prefix}', $db_prefix, $table);
788
789
	// Inserting data as a single row can be done as a single array.
790
	if (!is_array($data[array_rand($data)]))
791
		$data = array($data);
792
793
	// Create the mold for a single row insert.
794
	$insertData = '(';
795
	foreach ($columns as $columnName => $type)
796
	{
797
		// Are we restricting the length?
798
		if (strpos($type, 'string-') !== false)
799
			$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
800
		else
801
			$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
802
	}
803
	$insertData = substr($insertData, 0, -2) . ')';
804
805
	// Create an array consisting of only the columns.
806
	$indexed_columns = array_keys($columns);
807
808
	// Here's where the variables are injected to the query.
809
	$insertRows = array();
810
	foreach ($data as $dataRow)
811
		$insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection);
812
813
	// Determine the method of insertion.
814
	$queryTitle = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT');
815
816
	// Do the insert.
817
	$smcFunc['db_query']('', '
818
		' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
819
		VALUES
820
			' . implode(',
821
			', $insertRows),
822
		array(
823
			'security_override' => true,
824
			'db_error_skip' => $table === $db_prefix . 'log_errors',
825
		),
826
		$connection
827
	);
828
829
	if(!empty($keys) && (count($keys) > 0) && $method == '' && $returnmode > 0)
830
	{
831
		if ($returnmode == 1)
832
			$return_var = smf_db_insert_id($table, $keys[0]) + count($insertRows) - 1;
833
		else if ($returnmode == 2)
834
		{
835
			$return_var = array();
836
			$count = count($insertRows);
837
			$start = smf_db_insert_id($table, $keys[0]);
838
			for ($i = 0; $i < $count; $i++ )
839
				$return_var[] = $start + $i;
840
		}
841
		return $return_var;
0 ignored issues
show
Bug introduced by
The variable $return_var does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
842
	}
843
}
844
845
/**
846
 * This function tries to work out additional error information from a back trace.
847
 *
848
 * @param string $error_message The error message
849
 * @param string $log_message The message to log
850
 * @param string|bool $error_type What type of error this is
851
 * @param string $file The file the error occurred in
852
 * @param int $line What line of $file the code which generated the error is on
853
 * @return void|array Returns an array with the file and line if $error_type is 'return'
854
 */
855 View Code Duplication
function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null)
0 ignored issues
show
Duplication introduced by
This function 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...
856
{
857
	if (empty($log_message))
858
		$log_message = $error_message;
859
860
	foreach (debug_backtrace() as $step)
861
	{
862
		// Found it?
863
		if (strpos($step['function'], 'query') === false && !in_array(substr($step['function'], 0, 7), array('smf_db_', 'preg_re', 'db_erro', 'call_us')) && strpos($step['function'], '__') !== 0)
864
		{
865
			$log_message .= '<br>Function: ' . $step['function'];
866
			break;
867
		}
868
869
		if (isset($step['line']))
870
		{
871
			$file = $step['file'];
872
			$line = $step['line'];
873
		}
874
	}
875
876
	// A special case - we want the file and line numbers for debugging.
877
	if ($error_type == 'return')
878
		return array($file, $line);
879
880
	// Is always a critical error.
881
	if (function_exists('log_error'))
882
		log_error($log_message, 'critical', $file, $line);
883
884
	if (function_exists('fatal_error'))
885
	{
886
		fatal_error($error_message, false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
887
888
		// Cannot continue...
889
		exit;
890
	}
891
	elseif ($error_type)
892
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type);
893
	else
894
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
895
}
896
897
/**
898
 * Escape the LIKE wildcards so that they match the character and not the wildcard.
899
 *
900
 * @param string $string The string to escape
901
 * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards.
902
 * @return string The escaped string
903
 */
904 View Code Duplication
function smf_db_escape_wildcard_string($string, $translate_human_wildcards = false)
0 ignored issues
show
Duplication introduced by
This function 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...
905
{
906
	$replacements = array(
907
		'%' => '\%',
908
		'_' => '\_',
909
		'\\' => '\\\\',
910
	);
911
912
	if ($translate_human_wildcards)
913
		$replacements += array(
914
			'*' => '%',
915
		);
916
917
	return strtr($string, $replacements);
918
}
919
920
/**
921
 * Validates whether the resource is a valid mysqli instance.
922
 * Mysqli uses objects rather than resource. https://bugs.php.net/bug.php?id=42797
923
 *
924
 * @param mixed $result The string to test
925
 * @return bool True if it is, false otherwise
926
 */
927
function smf_is_resource($result)
928
{
929
	if ($result instanceof mysqli_result)
930
		return true;
931
932
	return false;
933
}
934
935
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...