Completed
Push — release-2.1 ( f6199f...02df86 )
by Michael
18:56 queued 08:36
created

Subs-Db-mysql.php ➔ smf_db_fetch_all()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
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
			'db_fetch_all'              => 'smf_db_fetch_all',
63
		);
64
65
	if (!empty($db_options['persist']))
66
		$db_server = 'p:' . $db_server;
67
68
	$connection = mysqli_init();
69
70
	$flags = MYSQLI_CLIENT_FOUND_ROWS;
71
72
	$success = false;
73
74
	if ($connection) {
75
		if (!empty($db_options['port']))
76
			$success = mysqli_real_connect($connection, $db_server, $db_user, $db_passwd, '', $db_options['port'], null, $flags);
77
		else
78
			$success = mysqli_real_connect($connection, $db_server, $db_user, $db_passwd, '', 0, null, $flags);
79
	}
80
81
	// Something's wrong, show an error if its fatal (which we assume it is)
82
	if ($success === false)
83
	{
84
		if (!empty($db_options['non_fatal']))
85
			return null;
86
		else
87
			display_db_error();
88
	}
89
90
	// Select the database, unless told not to
91
	if (empty($db_options['dont_select_db']) && !@mysqli_select_db($connection, $db_name) && empty($db_options['non_fatal']))
92
		display_db_error();
93
94
	$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\'',
95
		array(),
96
		false
97
	);
98
99
	return $connection;
100
}
101
102
/**
103
 * Extend the database functionality. It calls the respective file's init
104
 * to add the implementations in that file to $smcFunc array.
105
 *
106
 * @param string $type Indicates which additional file to load. ('extra', 'packages')
107
 */
108
function db_extend($type = 'extra')
109
{
110
	global $sourcedir;
111
112
	// we force the MySQL files as nothing syntactically changes with MySQLi
113
	require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-mysql.php');
114
	$initFunc = 'db_' . $type . '_init';
115
	$initFunc();
116
}
117
118
/**
119
 * Fix up the prefix so it doesn't require the database to be selected.
120
 *
121
 * @param string &$db_prefix The table prefix
122
 * @param string $db_name The database name
123
 */
124
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...
125
{
126
	$db_prefix = is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix;
127
}
128
129
/**
130
 * Wrap mysqli_select_db so the connection does not need to be specified
131
 *
132
 * @param string &$database The database
133
 * @param object $connection The connection object (if null, $db_connection is used)
134
 * @return bool Whether the database was selected
135
 */
136
function smf_db_select($database, $connection = null)
137
{
138
	global $db_connection;
139
	return mysqli_select_db($connection === null ? $db_connection : $connection, $database);
140
}
141
142
/**
143
 * Wrap mysqli_get_server_info so the connection does not need to be specified
144
 *
145
 * @param object $connection The connection to use (if null, $db_connection is used)
146
 * @return string The server info
147
 */
148
function smf_db_get_server_info($connection = null)
149
{
150
	global $db_connection;
151
	return mysqli_get_server_info($connection === null ? $db_connection : $connection);
152
}
153
154
/**
155
 * Callback for preg_replace_callback on the query.
156
 * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board', etc), with
157
 * their current values from $user_info.
158
 * In addition, it performs checks and sanitization on the values sent to the database.
159
 *
160
 * @param array $matches The matches from preg_replace_callback
161
 * @return string The appropriate string depending on $matches[1]
162
 */
163
function smf_db_replacement__callback($matches)
164
{
165
	global $db_callback, $user_info, $db_prefix, $smcFunc;
166
167
	list ($values, $connection) = $db_callback;
168
	if (!is_object($connection))
169
		display_db_error();
170
171
	if ($matches[1] === 'db_prefix')
172
		return $db_prefix;
173
174
	if (isset($user_info[$matches[1]]) && strpos($matches[1], 'query_') !== false)
175
		return $user_info[$matches[1]];
176
177
	if ($matches[1] === 'empty')
178
		return '\'\'';
179
180
	if (!isset($matches[2]))
181
		smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__);
182
183
	if ($matches[1] === 'literal')
184
		return '\'' . mysqli_real_escape_string($connection, $matches[2]) . '\'';
185
186
	if (!isset($values[$matches[2]]))
187
		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__);
188
189
	$replacement = $values[$matches[2]];
190
191
	switch ($matches[1])
192
	{
193
		case 'int':
194
			if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement)
195
				smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
196
			return (string) (int) $replacement;
197
		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...
198
199
		case 'string':
200
		case 'text':
201
			return sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $replacement));
202
		break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

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

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

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

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

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

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

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

Loading history...
273
274
		case 'raw':
275
			return $replacement;
276
		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...
277
278
		case 'inet':
279
			if ($replacement == 'null' || $replacement == '')
280
				return 'null';
281
			if (!isValidIP($replacement))
282
				smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
283
			//we don't use the native support of mysql > 5.6.2
284
			return sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($replacement)));
285
286
		case 'array_inet':
287
			if (is_array($replacement))
288
			{
289
				if (empty($replacement))
290
					smf_db_error_backtrace('Database error, given array of IPv4 or IPv6 values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
291
292
				foreach ($replacement as $key => $value)
293
				{
294
					if ($replacement == 'null' || $replacement == '')
295
						$replacement[$key] = 'null';
296
					if (!isValidIP($value))
297
						smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
298
					$replacement[$key] = sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($value)));
299
				}
300
301
				return implode(', ', $replacement);
302
			}
303
			else
304
				smf_db_error_backtrace('Wrong value type sent to the database. Array of IPv4 or IPv6 expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
305
		break;
306
307
		default:
308
			smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__);
309
		break;
310
	}
311
}
312
313
/**
314
 * Just like the db_query, escape and quote a string, but not executing the query.
315
 *
316
 * @param string $db_string The database string
317
 * @param array $db_values An array of values to be injected into the string
318
 * @param resource $connection = null The connection to use (null to use $db_connection)
319
 * @return string The string with the values inserted
320
 */
321 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...
322
{
323
	global $db_callback, $db_connection;
324
325
	// Only bother if there's something to replace.
326
	if (strpos($db_string, '{') !== false)
327
	{
328
		// This is needed by the callback function.
329
		$db_callback = array($db_values, $connection === null ? $db_connection : $connection);
330
331
		// Do the quoting and escaping
332
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
333
334
		// Clear this global variable.
335
		$db_callback = array();
336
	}
337
338
	return $db_string;
339
}
340
341
/**
342
 * Do a query.  Takes care of errors too.
343
 *
344
 * @param string $identifier An identifier. Only used in Postgres when we need to do things differently...
345
 * @param string $db_string The database string
346
 * @param array $db_values = array() The values to be inserted into the string
347
 * @param resource $connection = null The connection to use (null to use $db_connection)
348
 * @return resource|bool Returns a MySQL result resource (for SELECT queries), true (for UPDATE queries) or false if the query failed
349
 */
350
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...
351
{
352
	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
353
	global $db_unbuffered, $db_callback, $modSettings;
354
355
	// Comments that are allowed in a query are preg_removed.
356
	static $allowed_comments_from = array(
357
		'~\s+~s',
358
		'~/\*!40001 SQL_NO_CACHE \*/~',
359
		'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
360
		'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
361
	);
362
	static $allowed_comments_to = array(
363
		' ',
364
		'',
365
		'',
366
		'',
367
	);
368
369
	// Decide which connection to use.
370
	$connection = $connection === null ? $db_connection : $connection;
371
372
	// Get a connection if we are shutting down, sometimes the link is closed before sessions are written
373
	if (!is_object($connection))
374
	{
375
		global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
376
377
		// Are we in SSI mode?  If so try that username and password first
378
		if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
379
		{
380
			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...
381
				$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
382
			else
383
				$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
384
		}
385
		// Fall back to the regular username and password if need be
386
		if (!$db_connection)
387
		{
388
			if (empty($db_persist))
389
				$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
390
			else
391
				$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
392
		}
393
394
		if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
395
			$db_connection = false;
396
397
		$connection = $db_connection;
398
	}
399
400
	// One more query....
401
	$db_count = !isset($db_count) ? 1 : $db_count + 1;
402
403 View Code Duplication
	if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
404
		smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
405
406
	// Use "ORDER BY null" to prevent Mysql doing filesorts for Group By clauses without an Order By
407
	if (strpos($db_string, 'GROUP BY') !== false && strpos($db_string, 'ORDER BY') === false && preg_match('~^\s+SELECT~i', $db_string))
408
	{
409
		// Add before LIMIT
410
		if ($pos = strpos($db_string, 'LIMIT '))
411
			$db_string = substr($db_string, 0, $pos) . "\t\t\tORDER BY null\n" . substr($db_string, $pos, strlen($db_string));
412
		else
413
			// Append it.
414
			$db_string .= "\n\t\t\tORDER BY null";
415
	}
416
417 View Code Duplication
	if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
418
	{
419
		// Pass some values to the global space for use in the callback function.
420
		$db_callback = array($db_values, $connection);
421
422
		// Inject the values passed to this function.
423
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
424
425
		// This shouldn't be residing in global space any longer.
426
		$db_callback = array();
427
	}
428
429
	// Debugging.
430 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
431
	{
432
		// Get the file and line number this function was called.
433
		list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
434
435
		// Initialize $db_cache if not already initialized.
436
		if (!isset($db_cache))
437
			$db_cache = array();
438
439
		if (!empty($_SESSION['debug_redirect']))
440
		{
441
			$db_cache = array_merge($_SESSION['debug_redirect'], $db_cache);
442
			$db_count = count($db_cache) + 1;
443
			$_SESSION['debug_redirect'] = array();
444
		}
445
446
		// Don't overload it.
447
		$st = microtime();
448
		$db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...';
449
		$db_cache[$db_count]['f'] = $file;
450
		$db_cache[$db_count]['l'] = $line;
451
		$db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start));
452
	}
453
454
	// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
455 View Code Duplication
	if (empty($modSettings['disableQueryCheck']))
456
	{
457
		$clean = '';
458
		$old_pos = 0;
459
		$pos = -1;
460
		while (true)
461
		{
462
			$pos = strpos($db_string, '\'', $pos + 1);
463
			if ($pos === false)
464
				break;
465
			$clean .= substr($db_string, $old_pos, $pos - $old_pos);
466
467
			while (true)
468
			{
469
				$pos1 = strpos($db_string, '\'', $pos + 1);
470
				$pos2 = strpos($db_string, '\\', $pos + 1);
471
				if ($pos1 === false)
472
					break;
473
				elseif ($pos2 === false || $pos2 > $pos1)
474
				{
475
					$pos = $pos1;
476
					break;
477
				}
478
479
				$pos = $pos2 + 1;
480
			}
481
			$clean .= ' %s ';
482
483
			$old_pos = $pos + 1;
484
		}
485
		$clean .= substr($db_string, $old_pos);
486
		$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
487
488
		// Comments?  We don't use comments in our queries, we leave 'em outside!
489
		if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
490
			$fail = true;
491
		// Trying to change passwords, slow us down, or something?
492
		elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
493
			$fail = true;
494
		elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
495
			$fail = true;
496
497
		if (!empty($fail) && function_exists('log_error'))
498
			smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
499
	}
500
501
	if (empty($db_unbuffered))
502
		$ret = @mysqli_query($connection, $db_string);
503
	else
504
		$ret = @mysqli_query($connection, $db_string, MYSQLI_USE_RESULT);
505
506 View Code Duplication
	if ($ret === false && empty($db_values['db_error_skip']))
507
		$ret = smf_db_error($db_string, $connection);
508
509
	// Debugging.
510 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
511
		$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...
512
513
	return $ret;
514
}
515
516
/**
517
 * affected_rows
518
 * @param resource $connection A connection to use (if null, $db_connection is used)
519
 * @return int The number of rows affected by the last query
520
 */
521
function smf_db_affected_rows($connection = null)
522
{
523
	global $db_connection;
524
525
	return mysqli_affected_rows($connection === null ? $db_connection : $connection);
526
}
527
528
/**
529
 * Gets the ID of the most recently inserted row.
530
 *
531
 * @param string $table The table (only used for Postgres)
532
 * @param string $field = null The specific field (not used here)
533
 * @param resource $connection = null The connection (if null, $db_connection is used)
534
 * @return int The ID of the most recently inserted row
535
 */
536
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...
537
{
538
	global $db_connection;
539
540
	// MySQL doesn't need the table or field information.
541
	return mysqli_insert_id($connection === null ? $db_connection : $connection);
542
}
543
544
/**
545
 * Do a transaction.
546
 *
547
 * @param string $type The step to perform (i.e. 'begin', 'commit', 'rollback')
548
 * @param resource $connection The connection to use (if null, $db_connection is used)
549
 * @return bool True if successful, false otherwise
550
 */
551 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...
552
{
553
	global $db_connection;
554
555
	// Decide which connection to use
556
	$connection = $connection === null ? $db_connection : $connection;
557
558
	if ($type == 'begin')
559
		return @mysqli_query($connection, 'BEGIN');
560
	elseif ($type == 'rollback')
561
		return @mysqli_query($connection, 'ROLLBACK');
562
	elseif ($type == 'commit')
563
		return @mysqli_query($connection, 'COMMIT');
564
565
	return false;
566
}
567
568
/**
569
 * Database error!
570
 * Backtrace, log, try to fix.
571
 *
572
 * @param string $db_string The DB string
573
 * @param object $connection The connection to use (if null, $db_connection is used)
574
 */
575
function smf_db_error($db_string, $connection = null)
576
{
577
	global $txt, $context, $sourcedir, $webmaster_email, $modSettings;
578
	global $db_connection, $db_last_error, $db_persist;
579
	global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
580
	global $smcFunc;
581
582
	// Get the file and line numbers.
583
	list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
584
585
	// Decide which connection to use
586
	$connection = $connection === null ? $db_connection : $connection;
587
588
	// This is the error message...
589
	$query_error = mysqli_error($connection);
590
	$query_errno = mysqli_errno($connection);
591
592
	// Error numbers:
593
	//    1016: Can't open file '....MYI'
594
	//    1030: Got error ??? from table handler.
595
	//    1034: Incorrect key file for table.
596
	//    1035: Old key file for table.
597
	//    1205: Lock wait timeout exceeded.
598
	//    1213: Deadlock found.
599
	//    2006: Server has gone away.
600
	//    2013: Lost connection to server during query.
601
602
	// Log the error.
603
	if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error'))
604
		log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line);
605
606
	// Database error auto fixing ;).
607
	if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1'))
608
	{
609
		// Force caching on, just for the error checking.
610
		$old_cache = @$modSettings['cache_enable'];
611
		$modSettings['cache_enable'] = '1';
612
613
		if (($temp = cache_get_data('db_last_error', 600)) !== null)
614
			$db_last_error = max(@$db_last_error, $temp);
615
616
		if (@$db_last_error < time() - 3600 * 24 * 3)
617
		{
618
			// We know there's a problem... but what?  Try to auto detect.
619
			if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false)
620
			{
621
				preg_match_all('~(?:[\n\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\n\r(]+?(?:, )?)*)~s', $db_string, $matches);
622
623
				$fix_tables = array();
624
				foreach ($matches[1] as $tables)
625
				{
626
					$tables = array_unique(explode(',', $tables));
627
					foreach ($tables as $table)
628
					{
629
						// Now, it's still theoretically possible this could be an injection.  So backtick it!
630
						if (trim($table) != '')
631
							$fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`';
632
					}
633
				}
634
635
				$fix_tables = array_unique($fix_tables);
636
			}
637
			// Table crashed.  Let's try to fix it.
638
			elseif ($query_errno == 1016)
639
			{
640
				if (preg_match('~\'([^\.\']+)~', $query_error, $match) != 0)
641
					$fix_tables = array('`' . $match[1] . '`');
642
			}
643
			// Indexes crashed.  Should be easy to fix!
644
			elseif ($query_errno == 1034 || $query_errno == 1035)
645
			{
646
				preg_match('~\'([^\']+?)\'~', $query_error, $match);
647
				$fix_tables = array('`' . $match[1] . '`');
648
			}
649
		}
650
651
		// 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...)
652
		if (!empty($fix_tables))
653
		{
654
			// Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail().
655
			require_once($sourcedir . '/Subs-Admin.php');
656
			require_once($sourcedir . '/Subs-Post.php');
657
658
			// Make a note of the REPAIR...
659
			cache_put_data('db_last_error', time(), 600);
660
			if (($temp = cache_get_data('db_last_error', 600)) === null)
661
				updateSettingsFile(array('db_last_error' => time()));
662
663
			// Attempt to find and repair the broken table.
664
			foreach ($fix_tables as $table)
665
				$smcFunc['db_query']('', "
666
					REPAIR TABLE $table", false, false);
667
668
			// And send off an email!
669
			sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair'], null, 'dberror');
670
671
			$modSettings['cache_enable'] = $old_cache;
672
673
			// Try the query again...?
674
			$ret = $smcFunc['db_query']('', $db_string, false, false);
675
			if ($ret !== false)
676
				return $ret;
677
		}
678
		else
679
			$modSettings['cache_enable'] = $old_cache;
680
681
		// Check for the "lost connection" or "deadlock found" errors - and try it just one more time.
682
		if (in_array($query_errno, array(1205, 1213, 2006, 2013)))
683
		{
684
			if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection)
685
			{
686
				// Are we in SSI mode?  If so try that username and password first
687
				if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
688
				{
689
					if (empty($db_persist))
690
						$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
691
					else
692
						$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
693
				}
694
				// Fall back to the regular username and password if need be
695
				if (!$db_connection)
696
				{
697
					if (empty($db_persist))
698
						$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
699
					else
700
						$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
701
				}
702
703
				if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
704
					$db_connection = false;
705
			}
706
707
			if ($db_connection)
708
			{
709
				// Try a deadlock more than once more.
710
				for ($n = 0; $n < 4; $n++)
711
				{
712
					$ret = $smcFunc['db_query']('', $db_string, false, false);
713
714
					$new_errno = mysqli_errno($db_connection);
715
					if ($ret !== false || in_array($new_errno, array(1205, 1213)))
716
						break;
717
				}
718
719
				// If it failed again, shucks to be you... we're not trying it over and over.
720
				if ($ret !== false)
721
					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...
722
			}
723
		}
724
		// Are they out of space, perhaps?
725
		elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false))
726
		{
727
			if (!isset($txt))
728
				$query_error .= ' - check database storage space.';
729
			else
730
			{
731
				if (!isset($txt['mysql_error_space']))
732
					loadLanguage('Errors');
733
734
				$query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space'];
735
			}
736
		}
737
	}
738
739
	// Nothing's defined yet... just die with it.
740
	if (empty($context) || empty($txt))
741
		die($query_error);
742
743
	// Show an error message, if possible.
744
	$context['error_title'] = $txt['database_error'];
745
	if (allowedTo('admin_forum'))
746
		$context['error_message'] = nl2br($query_error) . '<br>' . $txt['file'] . ': ' . $file . '<br>' . $txt['line'] . ': ' . $line;
747
	else
748
		$context['error_message'] = $txt['try_again'];
749
750
	if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true)
751
	{
752
		$context['error_message'] .= '<br><br>' . nl2br($db_string);
753
	}
754
755
	// It's already been logged... don't log it again.
756
	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...
757
}
758
759
/**
760
 * Inserts data into a table
761
 *
762
 * @param string $method The insert method - can be 'replace', 'ignore' or 'insert'
763
 * @param string $table The table we're inserting the data into
764
 * @param array $columns An array of the columns we're inserting the data into. Should contain 'column' => 'datatype' pairs
765
 * @param array $data The data to insert
766
 * @param array $keys The keys for the table
767
 * @param int returnmode 0 = nothing(default), 1 = last row id, 2 = all rows id as array
768
 * @param object $connection The connection to use (if null, $db_connection is used)
769
 * @return mixed value of the first key, behavior based on returnmode. null if no data.
770
 */
771
function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $returnmode = 0, $connection = null)
772
{
773
	global $smcFunc, $db_connection, $db_prefix;
774
775
	$connection = $connection === null ? $db_connection : $connection;
776
	
777
	$return_var = null;
778
779
	// With nothing to insert, simply return.
780
	if (empty($data))
781
		return;
782
783
	// Replace the prefix holder with the actual prefix.
784
	$table = str_replace('{db_prefix}', $db_prefix, $table);
785
	
786
	$with_returning = false;
787
	
788 View Code Duplication
	if (!empty($keys) && (count($keys) > 0) && $returnmode > 0)
789
	{
790
		$with_returning = true;
791
		if ($returnmode == 2)
792
			$return_var = array();
793
	}
794
795
	// Inserting data as a single row can be done as a single array.
796
	if (!is_array($data[array_rand($data)]))
797
		$data = array($data);
798
799
	// Create the mold for a single row insert.
800
	$insertData = '(';
801
	foreach ($columns as $columnName => $type)
802
	{
803
		// Are we restricting the length?
804
		if (strpos($type, 'string-') !== false)
805
			$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
806
		else
807
			$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
808
	}
809
	$insertData = substr($insertData, 0, -2) . ')';
810
811
	// Create an array consisting of only the columns.
812
	$indexed_columns = array_keys($columns);
813
814
	// Here's where the variables are injected to the query.
815
	$insertRows = array();
816
	foreach ($data as $dataRow)
817
		$insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection);
818
819
	// Determine the method of insertion.
820
	$queryTitle = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT');
821
822
	if (!$with_returning || $method != 'ingore')
823
	{
824
		// Do the insert.
825
		$smcFunc['db_query']('', '
826
			' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
827
			VALUES
828
				' . implode(',
829
				', $insertRows),
830
			array(
831
				'security_override' => true,
832
				'db_error_skip' => $table === $db_prefix . 'log_errors',
833
			),
834
			$connection
835
		);
836
	}
837
	else //special way for ignore method with returning
838
	{
839
		$count = count($insertRows);
840
		$ai = 0;
841
		for($i = 0; $i < $count; $i++)
842
		{
843
			$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...
844
			
845
			$smcFunc['db_query']('', '
846
				' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
847
				VALUES
848
					' . $insertRows[$i],
849
				array(
850
					'security_override' => true,
851
					'db_error_skip' => $table === $db_prefix . 'log_errors',
852
				),
853
				$connection
854
			);
855
			$new_id = $smcFunc['db_insert_id']();
856
			
857
			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...
858
			{
859
				$ai = $new_id;
860
			}
861
			else	// the inserted value already exists we need to find the pk
862
			{
863
				$where_string = '';
864
				$count2 = count($indexed_columns);
865
				for ($x = 0; $x < $count2; $x++)
866
				{
867
					$where_string += key($indexed_columns[$x]) . ' = '. $insertRows[$i][$x];
868
					if (($x + 1) < $count2)
869
						$where_string += ' AND ';
870
				}
871
872
				$request = $smcFunc['db_query']('','
873
					SELECT `'. $keys[0] . '` FROM ' . $table .'
874
					WHERE ' . $where_string . ' LIMIT 1',
875
					array()
876
				);
877
				
878
				if ($request !== false && $smcFunc['db_num_rows']($request) == 1)
879
				{
880
					$row = $smcFunc['db_fetch_assoc']($request);
881
					$ai = $row[$keys[0]];
882
				}
883
			}
884
			
885
			if ($returnmode == 1)
886
				$return_var = $ai;
887
			else if ($returnmode == 2)
888
				$return_var[] = $ai;
889
		}
890
	}
891
	
892
893
	if ($with_returning)
894
	{
895
		if ($returnmode == 1 && empty($return_var))
896
			$return_var = smf_db_insert_id($table, $keys[0]) + count($insertRows) - 1;
897
		else if ($returnmode == 2 && empty($return_var))
898
		{
899
			$return_var = array();
900
			$count = count($insertRows);
901
			$start = smf_db_insert_id($table, $keys[0]);
902
			for ($i = 0; $i < $count; $i++ )
903
				$return_var[] = $start + $i;
904
		}
905
		return $return_var;
906
	}
907
}
908
909
/**
910
 * This function tries to work out additional error information from a back trace.
911
 *
912
 * @param string $error_message The error message
913
 * @param string $log_message The message to log
914
 * @param string|bool $error_type What type of error this is
915
 * @param string $file The file the error occurred in
916
 * @param int $line What line of $file the code which generated the error is on
917
 * @return void|array Returns an array with the file and line if $error_type is 'return'
918
 */
919 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...
920
{
921
	if (empty($log_message))
922
		$log_message = $error_message;
923
924
	foreach (debug_backtrace() as $step)
925
	{
926
		// Found it?
927
		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)
928
		{
929
			$log_message .= '<br>Function: ' . $step['function'];
930
			break;
931
		}
932
933
		if (isset($step['line']))
934
		{
935
			$file = $step['file'];
936
			$line = $step['line'];
937
		}
938
	}
939
940
	// A special case - we want the file and line numbers for debugging.
941
	if ($error_type == 'return')
942
		return array($file, $line);
943
944
	// Is always a critical error.
945
	if (function_exists('log_error'))
946
		log_error($log_message, 'critical', $file, $line);
947
948
	if (function_exists('fatal_error'))
949
	{
950
		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...
951
952
		// Cannot continue...
953
		exit;
954
	}
955
	elseif ($error_type)
956
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type);
957
	else
958
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
959
}
960
961
/**
962
 * Escape the LIKE wildcards so that they match the character and not the wildcard.
963
 *
964
 * @param string $string The string to escape
965
 * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards.
966
 * @return string The escaped string
967
 */
968 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...
969
{
970
	$replacements = array(
971
		'%' => '\%',
972
		'_' => '\_',
973
		'\\' => '\\\\',
974
	);
975
976
	if ($translate_human_wildcards)
977
		$replacements += array(
978
			'*' => '%',
979
		);
980
981
	return strtr($string, $replacements);
982
}
983
984
/**
985
 * Validates whether the resource is a valid mysqli instance.
986
 * Mysqli uses objects rather than resource. https://bugs.php.net/bug.php?id=42797
987
 *
988
 * @param mixed $result The string to test
989
 * @return bool True if it is, false otherwise
990
 */
991
function smf_is_resource($result)
992
{
993
	if ($result instanceof mysqli_result)
994
		return true;
995
996
	return false;
997
}
998
999
/**
1000
 * Fetches all rows from a result as an array 
1001
 *
1002
 * @param resource $request A MySQL result resource
1003
 * @return array An array that contains all rows (records) in the result resource
1004
 */
1005
function smf_db_fetch_all($request)
1006
{
1007
	// Return the right row.
1008
	return mysqli_fetch_all($request);
1009
}
1010
1011
?>
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...