Completed
Push — release-2.1 ( af8dd6...5b3c87 )
by Michael
20:47 queued 10:04
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']))
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':
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':
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':
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']))
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))
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)
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']))
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']))
506
		$ret = smf_db_error($db_string, $connection);
507
508
	// Debugging.
509 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
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
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
	$return_var = null;
777
778
	// With nothing to insert, simply return.
779
	if (empty($data))
780
		return;
781
782
	// Replace the prefix holder with the actual prefix.
783
	$table = str_replace('{db_prefix}', $db_prefix, $table);
784
	
785
	$with_returning = false;
786
	
787 View Code Duplication
	if (!empty($keys) && (count($keys) > 0) && $returnmode > 0)
788
	{
789
		$with_returning = true;
790
		if ($returnmode == 2)
791
			$return_var = array();
792
	}
793
794
	// Inserting data as a single row can be done as a single array.
795
	if (!is_array($data[array_rand($data)]))
796
		$data = array($data);
797
798
	// Create the mold for a single row insert.
799
	$insertData = '(';
800
	foreach ($columns as $columnName => $type)
801
	{
802
		// Are we restricting the length?
803
		if (strpos($type, 'string-') !== false)
804
			$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
805
		else
806
			$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
807
	}
808
	$insertData = substr($insertData, 0, -2) . ')';
809
810
	// Create an array consisting of only the columns.
811
	$indexed_columns = array_keys($columns);
812
813
	// Here's where the variables are injected to the query.
814
	$insertRows = array();
815
	foreach ($data as $dataRow)
816
		$insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection);
817
818
	// Determine the method of insertion.
819
	$queryTitle = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT');
820
821
	if (!$with_returning || $method != 'ingore')
822
	{
823
		// Do the insert.
824
		$smcFunc['db_query']('', '
825
			' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
826
			VALUES
827
				' . implode(',
828
				', $insertRows),
829
			array(
830
				'security_override' => true,
831
				'db_error_skip' => $table === $db_prefix . 'log_errors',
832
			),
833
			$connection
834
		);
835
	}
836
	else //special way for ignore method with returning
837
	{
838
		$count = count($insertRows);
839
		$ai = 0;
840
		for($i = 0; $i < $count; $i++)
841
		{
842
			$old_id = $smcFunc['db_insert_id']();
0 ignored issues
show
Unused Code introduced by
$old_id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
843
			
844
			$smcFunc['db_query']('', '
845
				' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
846
				VALUES
847
					' . $insertRows[$i],
848
				array(
849
					'security_override' => true,
850
					'db_error_skip' => $table === $db_prefix . 'log_errors',
851
				),
852
				$connection
853
			);
854
			$new_id = $smcFunc['db_insert_id']();
855
			
856
			if ($last_id != $new_id) //the inserted value was new
0 ignored issues
show
Bug introduced by
The variable $last_id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
857
			{
858
				$ai = $new_id;
859
			}
860
			else	// the inserted value already exists we need to find the pk
861
			{
862
				$where_string = '';
863
				$count2 = count($indexed_columns);
864
				for ($x = 0; $x < $count2; $x++)
865
				{
866
					$where_string += key($indexed_columns[$x]) . ' = '. $insertRows[$i][$x];
867
					if (($x + 1) < $count2)
868
						$where_string += ' AND ';
869
				}
870
871
				$request = $smcFunc['db_query']('','
872
					SELECT `'. $keys[0] . '` FROM ' . $table .'
873
					WHERE ' . $where_string . ' LIMIT 1',
874
					array()
875
				);
876
				
877
				if ($request !== false && $smcFunc['db_num_rows']($request) == 1)
878
				{
879
					$row = $smcFunc['db_fetch_assoc']($request);
880
					$ai = $row[$keys[0]];
881
				}
882
			}
883
			
884
			if ($returnmode == 1)
885
				$return_var = $ai;
886
			else if ($returnmode == 2)
887
				$return_var[] = $ai;
888
		}
889
	}
890
	
891
892
	if ($with_returning)
893
	{
894
		if ($returnmode == 1 && empty($return_var))
895
			$return_var = smf_db_insert_id($table, $keys[0]) + count($insertRows) - 1;
896
		else if ($returnmode == 2 && empty($return_var))
897
		{
898
			$return_var = array();
899
			$count = count($insertRows);
900
			$start = smf_db_insert_id($table, $keys[0]);
901
			for ($i = 0; $i < $count; $i++ )
902
				$return_var[] = $start + $i;
903
		}
904
		return $return_var;
905
	}
906
}
907
908
/**
909
 * This function tries to work out additional error information from a back trace.
910
 *
911
 * @param string $error_message The error message
912
 * @param string $log_message The message to log
913
 * @param string|bool $error_type What type of error this is
914
 * @param string $file The file the error occurred in
915
 * @param int $line What line of $file the code which generated the error is on
916
 * @return void|array Returns an array with the file and line if $error_type is 'return'
917
 */
918 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...
919
{
920
	if (empty($log_message))
921
		$log_message = $error_message;
922
923
	foreach (debug_backtrace() as $step)
924
	{
925
		// Found it?
926
		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)
927
		{
928
			$log_message .= '<br>Function: ' . $step['function'];
929
			break;
930
		}
931
932
		if (isset($step['line']))
933
		{
934
			$file = $step['file'];
935
			$line = $step['line'];
936
		}
937
	}
938
939
	// A special case - we want the file and line numbers for debugging.
940
	if ($error_type == 'return')
941
		return array($file, $line);
942
943
	// Is always a critical error.
944
	if (function_exists('log_error'))
945
		log_error($log_message, 'critical', $file, $line);
946
947
	if (function_exists('fatal_error'))
948
	{
949
		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...
950
951
		// Cannot continue...
952
		exit;
953
	}
954
	elseif ($error_type)
955
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type);
956
	else
957
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
958
}
959
960
/**
961
 * Escape the LIKE wildcards so that they match the character and not the wildcard.
962
 *
963
 * @param string $string The string to escape
964
 * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards.
965
 * @return string The escaped string
966
 */
967 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...
968
{
969
	$replacements = array(
970
		'%' => '\%',
971
		'_' => '\_',
972
		'\\' => '\\\\',
973
	);
974
975
	if ($translate_human_wildcards)
976
		$replacements += array(
977
			'*' => '%',
978
		);
979
980
	return strtr($string, $replacements);
981
}
982
983
/**
984
 * Validates whether the resource is a valid mysqli instance.
985
 * Mysqli uses objects rather than resource. https://bugs.php.net/bug.php?id=42797
986
 *
987
 * @param mixed $result The string to test
988
 * @return bool True if it is, false otherwise
989
 */
990
function smf_is_resource($result)
991
{
992
	if ($result instanceof mysqli_result)
993
		return true;
994
995
	return false;
996
}
997
998
?>
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...