Completed
Push — release-2.1 ( aa21c4...7040ad )
by Mathias
09:20
created

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

Complexity

Conditions 45
Paths 169

Size

Total Lines 149
Code Lines 102

Duplication

Lines 20
Ratio 13.42 %

Importance

Changes 0
Metric Value
cc 45
eloc 102
nc 169
nop 1
dl 20
loc 149
rs 4
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 4
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;
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
	$smcFunc['db_query']('', 'SET SESSION sql_mode = \'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION\'',
94
		array(),
95
		false
96
	);
97
98
	return $connection;
99
}
100
101
/**
102
 * Extend the database functionality. It calls the respective file's init
103
 * to add the implementations in that file to $smcFunc array.
104
 *
105
 * @param string $type Indicates which additional file to load. ('extra', 'packages')
106
 */
107
function db_extend($type = 'extra')
108
{
109
	global $sourcedir;
110
111
	// we force the MySQL files as nothing syntactically changes with MySQLi
112
	require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-mysql.php');
113
	$initFunc = 'db_' . $type . '_init';
114
	$initFunc();
115
}
116
117
/**
118
 * Fix up the prefix so it doesn't require the database to be selected.
119
 *
120
 * @param string &$db_prefix The table prefix
121
 * @param string $db_name The database name
122
 */
123
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...
124
{
125
	$db_prefix = is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix;
126
}
127
128
/**
129
 * Wrap mysqli_select_db so the connection does not need to be specified
130
 *
131
 * @param string &$database The database
132
 * @param object $connection The connection object (if null, $db_connection is used)
133
 * @return bool Whether the database was selected
134
 */
135
function smf_db_select($database, $connection = null)
136
{
137
	global $db_connection;
138
	return mysqli_select_db($connection === null ? $db_connection : $connection, $database);
139
}
140
141
/**
142
 * Wrap mysqli_get_server_info so the connection does not need to be specified
143
 *
144
 * @param object $connection The connection to use (if null, $db_connection is used)
145
 * @return string The server info
146
 */
147
function smf_db_get_server_info($connection = null)
148
{
149
	global $db_connection;
150
	return mysqli_get_server_info($connection === null ? $db_connection : $connection);
151
}
152
153
/**
154
 * Callback for preg_replace_callback on the query.
155
 * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board', etc), with
156
 * their current values from $user_info.
157
 * In addition, it performs checks and sanitization on the values sent to the database.
158
 *
159
 * @param array $matches The matches from preg_replace_callback
160
 * @return string The appropriate string depending on $matches[1]
161
 */
162
function smf_db_replacement__callback($matches)
163
{
164
	global $db_callback, $user_info, $db_prefix, $smcFunc;
165
166
	list ($values, $connection) = $db_callback;
167
	if (!is_object($connection))
168
		display_db_error();
169
170
	if ($matches[1] === 'db_prefix')
171
		return $db_prefix;
172
173
	if (isset($user_info[$matches[1]]) && strpos($matches[1], 'query_') !== false)
174
		return $user_info[$matches[1]];
175
176
	if ($matches[1] === 'empty')
177
		return '\'\'';
178
179
	if (!isset($matches[2]))
180
		smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__);
181
182
	if ($matches[1] === 'literal')
183
		return '\'' . mysqli_real_escape_string($connection, $matches[2]) . '\'';
184
185
	if (!isset($values[$matches[2]]))
186
		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__);
187
188
	$replacement = $values[$matches[2]];
189
190
	switch ($matches[1])
191
	{
192
		case 'int':
193
			if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement)
194
				smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
195
			return (string) (int) $replacement;
196
		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...
197
198
		case 'string':
199
		case 'text':
200
			return sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $replacement));
201
		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...
202
203
		case 'array_int':
204
			if (is_array($replacement))
205
			{
206
				if (empty($replacement))
207
					smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
208
209
				foreach ($replacement as $key => $value)
210
				{
211
					if (!is_numeric($value) || (string) $value !== (string) (int) $value)
212
						smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
213
214
					$replacement[$key] = (string) (int) $value;
215
				}
216
217
				return implode(', ', $replacement);
218
			}
219
			else
220
				smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
221
222
		break;
223
224
		case 'array_string':
225
			if (is_array($replacement))
226
			{
227
				if (empty($replacement))
228
					smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
229
230
				foreach ($replacement as $key => $value)
231
					$replacement[$key] = sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $value));
232
233
				return implode(', ', $replacement);
234
			}
235
			else
236
				smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
237
		break;
238
239 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...
240
			if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1)
241
				return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]);
242
			else
243
				smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
244
		break;
245
246 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...
247
			if (preg_match('~^([0-1]?\d|2[0-3]):([0-5]\d):([0-5]\d)$~', $replacement, $time_matches) === 1)
248
				return sprintf('\'%02d:%02d:%02d\'', $time_matches[1], $time_matches[2], $time_matches[3]);
249
			else
250
				smf_db_error_backtrace('Wrong value type sent to the database. Time expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
251
		break;
252
253 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...
254
			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)
255
				return 'str_to_date('.
256
					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]).
257
					',\'%Y-%m-%d %h:%i:%s\')';
258
			else
259
				smf_db_error_backtrace('Wrong value type sent to the database. Datetime expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
260
		break;
261
262
		case 'float':
263
			if (!is_numeric($replacement))
264
				smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
265
			return (string) (float) $replacement;
266
		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...
267
268
		case 'identifier':
269
			// Backticks inside identifiers are supported as of MySQL 4.1. We don't need them for SMF.
270
			return '`' . strtr($replacement, array('`' => '', '.' => '`.`')) . '`';
271
		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...
272
273
		case 'raw':
274
			return $replacement;
275
		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...
276
277
		case 'inet':
278
			if ($replacement == 'null' || $replacement == '')
279
				return 'null';
280
			if (!isValidIP($replacement))
281
				smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
282
			//we don't use the native support of mysql > 5.6.2
283
			return sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($replacement)));
284
285
		case 'array_inet':
286
			if (is_array($replacement))
287
			{
288
				if (empty($replacement))
289
					smf_db_error_backtrace('Database error, given array of IPv4 or IPv6 values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
290
291
				foreach ($replacement as $key => $value)
292
				{
293
					if ($replacement == 'null' || $replacement == '')
294
						$replacement[$key] = 'null';
295
					if (!isValidIP($value))
296
						smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
297
					$replacement[$key] = sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($value)));
298
				}
299
300
				return implode(', ', $replacement);
301
			}
302
			else
303
				smf_db_error_backtrace('Wrong value type sent to the database. Array of IPv4 or IPv6 expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
304
		break;
305
306
		default:
307
			smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__);
308
		break;
309
	}
310
}
311
312
/**
313
 * Just like the db_query, escape and quote a string, but not executing the query.
314
 *
315
 * @param string $db_string The database string
316
 * @param array $db_values An array of values to be injected into the string
317
 * @param resource $connection = null The connection to use (null to use $db_connection)
318
 * @return string The string with the values inserted
319
 */
320 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...
321
{
322
	global $db_callback, $db_connection;
323
324
	// Only bother if there's something to replace.
325
	if (strpos($db_string, '{') !== false)
326
	{
327
		// This is needed by the callback function.
328
		$db_callback = array($db_values, $connection === null ? $db_connection : $connection);
329
330
		// Do the quoting and escaping
331
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
332
333
		// Clear this global variable.
334
		$db_callback = array();
335
	}
336
337
	return $db_string;
338
}
339
340
/**
341
 * Do a query.  Takes care of errors too.
342
 *
343
 * @param string $identifier An identifier. Only used in Postgres when we need to do things differently...
344
 * @param string $db_string The database string
345
 * @param array $db_values = array() The values to be inserted into the string
346
 * @param resource $connection = null The connection to use (null to use $db_connection)
347
 * @return resource|bool Returns a MySQL result resource (for SELECT queries), true (for UPDATE queries) or false if the query failed
348
 */
349
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...
350
{
351
	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
352
	global $db_unbuffered, $db_callback, $modSettings;
353
354
	// Comments that are allowed in a query are preg_removed.
355
	static $allowed_comments_from = array(
356
		'~\s+~s',
357
		'~/\*!40001 SQL_NO_CACHE \*/~',
358
		'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
359
		'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
360
	);
361
	static $allowed_comments_to = array(
362
		' ',
363
		'',
364
		'',
365
		'',
366
	);
367
368
	// Decide which connection to use.
369
	$connection = $connection === null ? $db_connection : $connection;
370
371
	// Get a connection if we are shutting down, sometimes the link is closed before sessions are written
372
	if (!is_object($connection))
373
	{
374
		global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
375
376
		// Are we in SSI mode?  If so try that username and password first
377
		if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
378
		{
379
			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...
380
				$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
381
			else
382
				$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
383
		}
384
		// Fall back to the regular username and password if need be
385
		if (!$db_connection)
386
		{
387
			if (empty($db_persist))
388
				$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
389
			else
390
				$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
391
		}
392
393
		if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
394
			$db_connection = false;
395
396
		$connection = $db_connection;
397
	}
398
399
	// One more query....
400
	$db_count = !isset($db_count) ? 1 : $db_count + 1;
401
402 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...
403
		smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
404
405
	// Use "ORDER BY null" to prevent Mysql doing filesorts for Group By clauses without an Order By
406
	if (strpos($db_string, 'GROUP BY') !== false && strpos($db_string, 'ORDER BY') === false && preg_match('~^\s+SELECT~i', $db_string))
407
	{
408
		// Add before LIMIT
409
		if ($pos = strpos($db_string, 'LIMIT '))
410
			$db_string = substr($db_string, 0, $pos) . "\t\t\tORDER BY null\n" . substr($db_string, $pos, strlen($db_string));
411
		else
412
			// Append it.
413
			$db_string .= "\n\t\t\tORDER BY null";
414
	}
415
416 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...
417
	{
418
		// Pass some values to the global space for use in the callback function.
419
		$db_callback = array($db_values, $connection);
420
421
		// Inject the values passed to this function.
422
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
423
424
		// This shouldn't be residing in global space any longer.
425
		$db_callback = array();
426
	}
427
428
	// Debugging.
429 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...
430
	{
431
		// Get the file and line number this function was called.
432
		list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
433
434
		// Initialize $db_cache if not already initialized.
435
		if (!isset($db_cache))
436
			$db_cache = array();
437
438
		if (!empty($_SESSION['debug_redirect']))
439
		{
440
			$db_cache = array_merge($_SESSION['debug_redirect'], $db_cache);
441
			$db_count = count($db_cache) + 1;
442
			$_SESSION['debug_redirect'] = array();
443
		}
444
445
		// Don't overload it.
446
		$st = microtime();
447
		$db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...';
448
		$db_cache[$db_count]['f'] = $file;
449
		$db_cache[$db_count]['l'] = $line;
450
		$db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start));
451
	}
452
453
	// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
454 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...
455
	{
456
		$clean = '';
457
		$old_pos = 0;
458
		$pos = -1;
459
		while (true)
460
		{
461
			$pos = strpos($db_string, '\'', $pos + 1);
462
			if ($pos === false)
463
				break;
464
			$clean .= substr($db_string, $old_pos, $pos - $old_pos);
465
466
			while (true)
467
			{
468
				$pos1 = strpos($db_string, '\'', $pos + 1);
469
				$pos2 = strpos($db_string, '\\', $pos + 1);
470
				if ($pos1 === false)
471
					break;
472
				elseif ($pos2 === false || $pos2 > $pos1)
473
				{
474
					$pos = $pos1;
475
					break;
476
				}
477
478
				$pos = $pos2 + 1;
479
			}
480
			$clean .= ' %s ';
481
482
			$old_pos = $pos + 1;
483
		}
484
		$clean .= substr($db_string, $old_pos);
485
		$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
486
487
		// Comments?  We don't use comments in our queries, we leave 'em outside!
488
		if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
489
			$fail = true;
490
		// Trying to change passwords, slow us down, or something?
491
		elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
492
			$fail = true;
493
		elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
494
			$fail = true;
495
496
		if (!empty($fail) && function_exists('log_error'))
497
			smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
498
	}
499
500
	if (empty($db_unbuffered))
501
		$ret = @mysqli_query($connection, $db_string);
502
	else
503
		$ret = @mysqli_query($connection, $db_string, MYSQLI_USE_RESULT);
504
505 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...
506
		$ret = smf_db_error($db_string, $connection);
507
508
	// Debugging.
509 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...
510
		$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...
511
512
	return $ret;
513
}
514
515
/**
516
 * affected_rows
517
 * @param resource $connection A connection to use (if null, $db_connection is used)
518
 * @return int The number of rows affected by the last query
519
 */
520
function smf_db_affected_rows($connection = null)
521
{
522
	global $db_connection;
523
524
	return mysqli_affected_rows($connection === null ? $db_connection : $connection);
525
}
526
527
/**
528
 * Gets the ID of the most recently inserted row.
529
 *
530
 * @param string $table The table (only used for Postgres)
531
 * @param string $field = null The specific field (not used here)
532
 * @param resource $connection = null The connection (if null, $db_connection is used)
533
 * @return int The ID of the most recently inserted row
534
 */
535
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...
536
{
537
	global $db_connection;
538
539
	// MySQL doesn't need the table or field information.
540
	return mysqli_insert_id($connection === null ? $db_connection : $connection);
541
}
542
543
/**
544
 * Do a transaction.
545
 *
546
 * @param string $type The step to perform (i.e. 'begin', 'commit', 'rollback')
547
 * @param resource $connection The connection to use (if null, $db_connection is used)
548
 * @return bool True if successful, false otherwise
549
 */
550 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...
551
{
552
	global $db_connection;
553
554
	// Decide which connection to use
555
	$connection = $connection === null ? $db_connection : $connection;
556
557
	if ($type == 'begin')
558
		return @mysqli_query($connection, 'BEGIN');
559
	elseif ($type == 'rollback')
560
		return @mysqli_query($connection, 'ROLLBACK');
561
	elseif ($type == 'commit')
562
		return @mysqli_query($connection, 'COMMIT');
563
564
	return false;
565
}
566
567
/**
568
 * Database error!
569
 * Backtrace, log, try to fix.
570
 *
571
 * @param string $db_string The DB string
572
 * @param object $connection The connection to use (if null, $db_connection is used)
573
 */
574
function smf_db_error($db_string, $connection = null)
575
{
576
	global $txt, $context, $sourcedir, $webmaster_email, $modSettings;
577
	global $db_connection, $db_last_error, $db_persist;
578
	global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
579
	global $smcFunc;
580
581
	// Get the file and line numbers.
582
	list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
583
584
	// Decide which connection to use
585
	$connection = $connection === null ? $db_connection : $connection;
586
587
	// This is the error message...
588
	$query_error = mysqli_error($connection);
589
	$query_errno = mysqli_errno($connection);
590
591
	// Error numbers:
592
	//    1016: Can't open file '....MYI'
593
	//    1030: Got error ??? from table handler.
594
	//    1034: Incorrect key file for table.
595
	//    1035: Old key file for table.
596
	//    1205: Lock wait timeout exceeded.
597
	//    1213: Deadlock found.
598
	//    2006: Server has gone away.
599
	//    2013: Lost connection to server during query.
600
601
	// Log the error.
602
	if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error'))
603
		log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line);
604
605
	// Database error auto fixing ;).
606
	if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1'))
607
	{
608
		// Force caching on, just for the error checking.
609
		$old_cache = @$modSettings['cache_enable'];
610
		$modSettings['cache_enable'] = '1';
611
612
		if (($temp = cache_get_data('db_last_error', 600)) !== null)
613
			$db_last_error = max(@$db_last_error, $temp);
614
615
		if (@$db_last_error < time() - 3600 * 24 * 3)
616
		{
617
			// We know there's a problem... but what?  Try to auto detect.
618
			if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false)
619
			{
620
				preg_match_all('~(?:[\n\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\n\r(]+?(?:, )?)*)~s', $db_string, $matches);
621
622
				$fix_tables = array();
623
				foreach ($matches[1] as $tables)
624
				{
625
					$tables = array_unique(explode(',', $tables));
626
					foreach ($tables as $table)
627
					{
628
						// Now, it's still theoretically possible this could be an injection.  So backtick it!
629
						if (trim($table) != '')
630
							$fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`';
631
					}
632
				}
633
634
				$fix_tables = array_unique($fix_tables);
635
			}
636
			// Table crashed.  Let's try to fix it.
637
			elseif ($query_errno == 1016)
638
			{
639
				if (preg_match('~\'([^\.\']+)~', $query_error, $match) != 0)
640
					$fix_tables = array('`' . $match[1] . '`');
641
			}
642
			// Indexes crashed.  Should be easy to fix!
643
			elseif ($query_errno == 1034 || $query_errno == 1035)
644
			{
645
				preg_match('~\'([^\']+?)\'~', $query_error, $match);
646
				$fix_tables = array('`' . $match[1] . '`');
647
			}
648
		}
649
650
		// 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...)
651
		if (!empty($fix_tables))
652
		{
653
			// Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail().
654
			require_once($sourcedir . '/Subs-Admin.php');
655
			require_once($sourcedir . '/Subs-Post.php');
656
657
			// Make a note of the REPAIR...
658
			cache_put_data('db_last_error', time(), 600);
659
			if (($temp = cache_get_data('db_last_error', 600)) === null)
660
				updateSettingsFile(array('db_last_error' => time()));
661
662
			// Attempt to find and repair the broken table.
663
			foreach ($fix_tables as $table)
664
				$smcFunc['db_query']('', "
665
					REPAIR TABLE $table", false, false);
666
667
			// And send off an email!
668
			sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair'], null, 'dberror');
669
670
			$modSettings['cache_enable'] = $old_cache;
671
672
			// Try the query again...?
673
			$ret = $smcFunc['db_query']('', $db_string, false, false);
674
			if ($ret !== false)
675
				return $ret;
676
		}
677
		else
678
			$modSettings['cache_enable'] = $old_cache;
679
680
		// Check for the "lost connection" or "deadlock found" errors - and try it just one more time.
681
		if (in_array($query_errno, array(1205, 1213, 2006, 2013)))
682
		{
683
			if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection)
684
			{
685
				// Are we in SSI mode?  If so try that username and password first
686
				if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
687
				{
688
					if (empty($db_persist))
689
						$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
690
					else
691
						$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
692
				}
693
				// Fall back to the regular username and password if need be
694
				if (!$db_connection)
695
				{
696
					if (empty($db_persist))
697
						$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
698
					else
699
						$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
700
				}
701
702
				if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
703
					$db_connection = false;
704
			}
705
706
			if ($db_connection)
707
			{
708
				// Try a deadlock more than once more.
709
				for ($n = 0; $n < 4; $n++)
710
				{
711
					$ret = $smcFunc['db_query']('', $db_string, false, false);
712
713
					$new_errno = mysqli_errno($db_connection);
714
					if ($ret !== false || in_array($new_errno, array(1205, 1213)))
715
						break;
716
				}
717
718
				// If it failed again, shucks to be you... we're not trying it over and over.
719
				if ($ret !== false)
720
					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...
721
			}
722
		}
723
		// Are they out of space, perhaps?
724
		elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false))
725
		{
726
			if (!isset($txt))
727
				$query_error .= ' - check database storage space.';
728
			else
729
			{
730
				if (!isset($txt['mysql_error_space']))
731
					loadLanguage('Errors');
732
733
				$query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space'];
734
			}
735
		}
736
	}
737
738
	// Nothing's defined yet... just die with it.
739
	if (empty($context) || empty($txt))
740
		die($query_error);
741
742
	// Show an error message, if possible.
743
	$context['error_title'] = $txt['database_error'];
744
	if (allowedTo('admin_forum'))
745
		$context['error_message'] = nl2br($query_error) . '<br>' . $txt['file'] . ': ' . $file . '<br>' . $txt['line'] . ': ' . $line;
746
	else
747
		$context['error_message'] = $txt['try_again'];
748
749
	if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true)
750
	{
751
		$context['error_message'] .= '<br><br>' . nl2br($db_string);
752
	}
753
754
	// It's already been logged... don't log it again.
755
	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...
756
}
757
758
/**
759
 * Inserts data into a table
760
 *
761
 * @param string $method The insert method - can be 'replace', 'ignore' or 'insert'
762
 * @param string $table The table we're inserting the data into
763
 * @param array $columns An array of the columns we're inserting the data into. Should contain 'column' => 'datatype' pairs
764
 * @param array $data The data to insert
765
 * @param array $keys The keys for the table
766
 * @param int returnmode 0 = nothing(default), 1 = last row id, 2 = all rows id as array; every mode runs only with method = '' or 'insert'
767
 * @param object $connection The connection to use (if null, $db_connection is used)
768
 * @return mixed value of the first key, behavior based on returnmode. null if no data.
769
 */
770
function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $returnmode = 0, $connection = null)
771
{
772
	global $smcFunc, $db_connection, $db_prefix;
773
774
	$connection = $connection === null ? $db_connection : $connection;
775
776
	// With nothing to insert, simply return.
777
	if (empty($data))
778
		return;
779
780
	// Replace the prefix holder with the actual prefix.
781
	$table = str_replace('{db_prefix}', $db_prefix, $table);
782
783
	// Inserting data as a single row can be done as a single array.
784
	if (!is_array($data[array_rand($data)]))
785
		$data = array($data);
786
787
	// Create the mold for a single row insert.
788
	$insertData = '(';
789
	foreach ($columns as $columnName => $type)
790
	{
791
		// Are we restricting the length?
792
		if (strpos($type, 'string-') !== false)
793
			$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
794
		else
795
			$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
796
	}
797
	$insertData = substr($insertData, 0, -2) . ')';
798
799
	// Create an array consisting of only the columns.
800
	$indexed_columns = array_keys($columns);
801
802
	// Here's where the variables are injected to the query.
803
	$insertRows = array();
804
	foreach ($data as $dataRow)
805
		$insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection);
806
807
	// Determine the method of insertion.
808
	$queryTitle = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT');
809
810
	// Do the insert.
811
	$smcFunc['db_query']('', '
812
		' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
813
		VALUES
814
			' . implode(',
815
			', $insertRows),
816
		array(
817
			'security_override' => true,
818
			'db_error_skip' => $table === $db_prefix . 'log_errors',
819
		),
820
		$connection
821
	);
822
823
	if(!empty($keys) && (count($keys) > 0) && ($method === '' || $method === 'insert') && $returnmode > 0)
824
	{
825
		if ($returnmode == 1)
826
			$return_var = smf_db_insert_id($table, $keys[0]) + count($insertRows) - 1;
827
		else if ($returnmode == 2)
828
		{
829
			$return_var = array();
830
			$count = count($insertRows);
831
			$start = smf_db_insert_id($table, $keys[0]);
832
			for ($i = 0; $i < $count; $i++ )
833
				$return_var[] = $start + $i;
834
		}
835
		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...
836
	}
837
}
838
839
/**
840
 * This function tries to work out additional error information from a back trace.
841
 *
842
 * @param string $error_message The error message
843
 * @param string $log_message The message to log
844
 * @param string|bool $error_type What type of error this is
845
 * @param string $file The file the error occurred in
846
 * @param int $line What line of $file the code which generated the error is on
847
 * @return void|array Returns an array with the file and line if $error_type is 'return'
848
 */
849 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...
850
{
851
	if (empty($log_message))
852
		$log_message = $error_message;
853
854
	foreach (debug_backtrace() as $step)
855
	{
856
		// Found it?
857
		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)
858
		{
859
			$log_message .= '<br>Function: ' . $step['function'];
860
			break;
861
		}
862
863
		if (isset($step['line']))
864
		{
865
			$file = $step['file'];
866
			$line = $step['line'];
867
		}
868
	}
869
870
	// A special case - we want the file and line numbers for debugging.
871
	if ($error_type == 'return')
872
		return array($file, $line);
873
874
	// Is always a critical error.
875
	if (function_exists('log_error'))
876
		log_error($log_message, 'critical', $file, $line);
877
878
	if (function_exists('fatal_error'))
879
	{
880
		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...
881
882
		// Cannot continue...
883
		exit;
884
	}
885
	elseif ($error_type)
886
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type);
887
	else
888
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
889
}
890
891
/**
892
 * Escape the LIKE wildcards so that they match the character and not the wildcard.
893
 *
894
 * @param string $string The string to escape
895
 * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards.
896
 * @return string The escaped string
897
 */
898 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...
899
{
900
	$replacements = array(
901
		'%' => '\%',
902
		'_' => '\_',
903
		'\\' => '\\\\',
904
	);
905
906
	if ($translate_human_wildcards)
907
		$replacements += array(
908
			'*' => '%',
909
		);
910
911
	return strtr($string, $replacements);
912
}
913
914
/**
915
 * Validates whether the resource is a valid mysqli instance.
916
 * Mysqli uses objects rather than resource. https://bugs.php.net/bug.php?id=42797
917
 *
918
 * @param mixed $result The string to test
919
 * @return bool True if it is, false otherwise
920
 */
921
function smf_is_resource($result)
922
{
923
	if ($result instanceof mysqli_result)
924
		return true;
925
926
	return false;
927
}
928
929
?>
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...