Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Subs-Db-postgresql.php ➔ smf_db_fetch_row()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 14
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 14
loc 14
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
 * @see Subs-Db-mysql.php#smf_db_initiate
23
 *
24
 * @param string $db_server The database server
25
 * @param string $db_name The name of the database
26
 * @param string $db_user The database username
27
 * @param string $db_passwd The database password
28
 * @param string $db_prefix The table prefix
29
 * @param array $db_options An array of database options
30
 * @return null|resource Returns null on failure if $db_options['non_fatal'] is true or a PostgreSQL connection resource handle if the connection was successful.
31
 */
32
function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, &$db_prefix, $db_options = array())
0 ignored issues
show
Best Practice introduced by
The function smf_db_initiate() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L31-100) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
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...
33
{
34
	global $smcFunc;
35
36
	// Map some database specific functions, only do this once.
37 View Code Duplication
	if (!isset($smcFunc['db_fetch_assoc']))
38
		$smcFunc += array(
39
			'db_query' => 'smf_db_query',
40
			'db_quote' => 'smf_db_quote',
41
			'db_insert' => 'smf_db_insert',
42
			'db_insert_id' => 'smf_db_insert_id',
43
			'db_fetch_assoc' => 'smf_db_fetch_assoc',
44
			'db_fetch_row' => 'smf_db_fetch_row',
45
			'db_free_result' => 'pg_free_result',
46
			'db_num_rows' => 'pg_num_rows',
47
			'db_data_seek' => 'smf_db_data_seek',
48
			'db_num_fields' => 'pg_num_fields',
49
			'db_escape_string' => 'pg_escape_string',
50
			'db_unescape_string' => 'smf_db_unescape_string',
51
			'db_server_info' => 'smf_db_version',
52
			'db_affected_rows' => 'smf_db_affected_rows',
53
			'db_transaction' => 'smf_db_transaction',
54
			'db_error' => 'pg_last_error',
55
			'db_select_db' => 'smf_db_select_db',
56
			'db_title' => 'PostgreSQL',
57
			'db_sybase' => true,
58
			'db_case_sensitive' => true,
59
			'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string',
60
			'db_is_resource' => 'is_resource',
61
			'db_mb4' => true,
62
			'db_ping' => 'pg_ping',
63
			'db_fetch_all' => 'smf_db_fetch_all',
64
		);
65
66
	if (!empty($db_options['persist']))
67
		$connection = @pg_pconnect('host=' . $db_server . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'' . (empty($db_options['port']) ? '' : ' port=\'' . $db_options['port'] . '\''));
68
	else
69
		$connection = @pg_connect('host=' . $db_server . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'' . (empty($db_options['port']) ? '' : ' port=\'' . $db_options['port'] . '\''));
70
71
	// Something's wrong, show an error if its fatal (which we assume it is)
72
	if (!$connection)
73
	{
74
		if (!empty($db_options['non_fatal']))
75
		{
76
			return null;
77
		}
78
		else
79
		{
80
			display_db_error();
81
		}
82
	}
83
84
	return $connection;
85
}
86
87
/**
88
 * Extend the database functionality. It calls the respective file's init
89
 * to add the implementations in that file to $smcFunc array.
90
 *
91
 * @param string $type Indicates which additional file to load. ('extra', 'packages')
92
 */
93
function db_extend($type = 'extra')
0 ignored issues
show
Best Practice introduced by
The function db_extend() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L108-116) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
94
{
95
	global $sourcedir, $db_type;
96
97
	require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-' . $db_type . '.php');
98
	$initFunc = 'db_' . $type . '_init';
99
	$initFunc();
100
}
101
102
/**
103
 * Fix the database prefix if necessary.
104
 * Does nothing on PostgreSQL
105
 *
106
 * @param string $db_prefix The database prefix
107
 * @param string $db_name The database name
108
 */
109
function db_fix_prefix(&$db_prefix, $db_name)
0 ignored issues
show
Best Practice introduced by
The function db_fix_prefix() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L124-127) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
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...
110
{
111
	return;
112
}
113
114
/**
115
 * Callback for preg_replace_callback on the query.
116
 * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board', etc), with
117
 * their current values from $user_info.
118
 * In addition, it performs checks and sanitization on the values sent to the database.
119
 *
120
 * @param array $matches The matches from preg_replace_callback
121
 * @return string The appropriate string depending on $matches[1]
122
 */
123
function smf_db_replacement__callback($matches)
0 ignored issues
show
Best Practice introduced by
The function smf_db_replacement__callback() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L163-311) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
124
{
125
	global $db_callback, $user_info, $db_prefix, $smcFunc;
126
127
	list ($values, $connection) = $db_callback;
0 ignored issues
show
Unused Code introduced by
The assignment to $connection is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
128
129
	if ($matches[1] === 'db_prefix')
130
		return $db_prefix;
131
132 View Code Duplication
	if (isset($user_info[$matches[1]]) && strpos($matches[1], 'query_') !== false)
133
		return $user_info[$matches[1]];
134
135
	if ($matches[1] === 'empty')
136
		return '\'\'';
137
138
	if (!isset($matches[2]))
139
		smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__);
140
141
	if ($matches[1] === 'literal')
142
		return '\'' . pg_escape_string($matches[2]) . '\'';
143
144
	if (!isset($values[$matches[2]]))
145
		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__);
146
147
	$replacement = $values[$matches[2]];
148
149
	switch ($matches[1])
150
	{
151
		case 'int':
152
			if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement)
153
				smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
154
			return (string) (int) $replacement;
155
		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...
156
157
		case 'string':
158
		case 'text':
159
			return sprintf('\'%1$s\'', pg_escape_string($replacement));
160
		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...
161
162
		case 'array_int':
163
			if (is_array($replacement))
164
			{
165
				if (empty($replacement))
166
					smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
167
168
				foreach ($replacement as $key => $value)
169
				{
170
					if (!is_numeric($value) || (string) $value !== (string) (int) $value)
171
						smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
172
173
					$replacement[$key] = (string) (int) $value;
174
				}
175
176
				return implode(', ', $replacement);
177
			}
178
			else
179
				smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
180
181
		break;
182
183
		case 'array_string':
184
			if (is_array($replacement))
185
			{
186
				if (empty($replacement))
187
					smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
188
189
				foreach ($replacement as $key => $value)
190
					$replacement[$key] = sprintf('\'%1$s\'', pg_escape_string($value));
191
192
				return implode(', ', $replacement);
193
			}
194
			else
195
				smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
196
		break;
197
198 View Code Duplication
		case 'date':
199
			if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1)
200
				return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]).'::date';
201
			else
202
				smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
203
		break;
204
205 View Code Duplication
		case 'time':
206
			if (preg_match('~^([0-1]?\d|2[0-3]):([0-5]\d):([0-5]\d)$~', $replacement, $time_matches) === 1)
207
				return sprintf('\'%02d:%02d:%02d\'', $time_matches[1], $time_matches[2], $time_matches[3]).'::time';
208
			else
209
				smf_db_error_backtrace('Wrong value type sent to the database. Time expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
210
		break;
211
212 View Code Duplication
		case 'datetime':
213
			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)
214
				return 'to_timestamp('.
215
					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]).
216
					',\'YYYY-MM-DD HH24:MI:SS\')';
217
			else
218
				smf_db_error_backtrace('Wrong value type sent to the database. Datetime expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
219
		break;
220
221
		case 'float':
222
			if (!is_numeric($replacement))
223
				smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
224
			return (string) (float) $replacement;
225
		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...
226
227
		case 'identifier':
228
			return '"' . strtr($replacement, array('`' => '', '.' => '"."')) . '"';
229
		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...
230
231
		case 'raw':
232
			return $replacement;
233
		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...
234
235 View Code Duplication
		case 'inet':
236
			if ($replacement == 'null' || $replacement == '')
237
				return 'null';
238
			if (inet_pton($replacement) === false)
239
				smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
240
			return sprintf('\'%1$s\'::inet', pg_escape_string($replacement));
241
242
		case 'array_inet':
243
			if (is_array($replacement))
244
			{
245
				if (empty($replacement))
246
					smf_db_error_backtrace('Database error, given array of IPv4 or IPv6 values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
247
248
				foreach ($replacement as $key => $value)
249
				{
250
					if ($replacement == 'null' || $replacement == '')
251
						$replacement[$key] = 'null';
252
					if (!isValidIP($value))
253
						smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
254
					$replacement[$key] = sprintf('\'%1$s\'::inet', pg_escape_string($value));
255
				}
256
257
				return implode(', ', $replacement);
258
			}
259
			else
260
				smf_db_error_backtrace('Wrong value type sent to the database. Array of IPv4 or IPv6 expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
261
		break;
262
263
		default:
264
			smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__);
265
		break;
266
	}
267
}
268
269
/**
270
 * Just like the db_query, escape and quote a string, but not executing the query.
271
 *
272
 * @param string $db_string The database string
273
 * @param array $db_values An array of values to be injected into the string
274
 * @param resource $connection = null The connection to use (null to use $db_connection)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $connection not be resource|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
275
 * @return string The string with the values inserted
276
 */
277 View Code Duplication
function smf_db_quote($db_string, $db_values, $connection = null)
0 ignored issues
show
Best Practice introduced by
The function smf_db_quote() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L321-339) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
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...
278
{
279
	global $db_callback, $db_connection;
280
281
	// Only bother if there's something to replace.
282
	if (strpos($db_string, '{') !== false)
283
	{
284
		// This is needed by the callback function.
285
		$db_callback = array($db_values, $connection === null ? $db_connection : $connection);
286
287
		// Do the quoting and escaping
288
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
289
290
		// Clear this global variable.
291
		$db_callback = array();
292
	}
293
294
	return $db_string;
295
}
296
297
/**
298
 * Do a query.  Takes care of errors too.
299
 * Special queries may need additional replacements to be appropriate
300
 * for PostgreSQL.
301
 *
302
 * @param string $identifier An identifier. Only used in Postgres when we need to do things differently...
303
 * @param string $db_string The database string
304
 * @param array $db_values = array() The values to be inserted into the string
305
 * @param resource $connection = null The connection to use (null to use $db_connection)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $connection not be resource|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
306
 * @return resource|bool Returns a MySQL result resource (for SELECT queries), true (for UPDATE queries) or false if the query failed
307
 */
308
function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null)
0 ignored issues
show
Best Practice introduced by
The function smf_db_query() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L350-514) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
309
{
310
	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
311
	global $db_callback, $db_last_result, $db_replace_result, $modSettings;
312
313
	// Decide which connection to use.
314
	$connection = $connection === null ? $db_connection : $connection;
315
316
	// Special queries that need processing.
317
	$replacements = array(
318
		'consolidate_spider_stats' => array(
319
			'~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))',
320
		),
321
		'attach_download_increase' => array(
322
			'~LOW_PRIORITY~' => '',
323
		),
324
		'get_random_number' => array(
325
			'~RAND~' => 'RANDOM',
326
		),
327
		'insert_log_search_topics' => array(
328
			'~NOT RLIKE~' => '!~',
329
		),
330
		'insert_log_search_results_no_index' => array(
331
			'~NOT RLIKE~' => '!~',
332
		),
333
		'insert_log_search_results_subject' => array(
334
			'~NOT RLIKE~' => '!~',
335
		),
336
		'pm_conversation_list' => array(
337
			'~ORDER\\s+BY\\s+\\{raw:sort\\}~' => 'ORDER BY ' . (isset($db_values['sort']) ? ($db_values['sort'] === 'pm.id_pm' ? 'MAX(pm.id_pm)' : $db_values['sort']) : ''),
338
		),
339
		'profile_board_stats' => array(
340
			'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)',
341
		),
342
	);
343
344
	// Special optimizer Hints
345
	$query_opt = array(
346
		'load_board_info' => array(
347
			'join_collapse_limit' => 1,
348
		),
349
		'calendar_get_events' => array(
350
			'enable_seqscan' => 'off',
351
		),
352
	);
353
354 View Code Duplication
	if (isset($replacements[$identifier]))
355
		$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
356
357
	// Limits need to be a little different.
358
	$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string);
359
360
	if (trim($db_string) == '')
361
		return false;
362
363
	// Comments that are allowed in a query are preg_removed.
364
	static $allowed_comments_from = array(
365
		'~\s+~s',
366
		'~/\*!40001 SQL_NO_CACHE \*/~',
367
		'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
368
		'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
369
	);
370
	static $allowed_comments_to = array(
371
		' ',
372
		'',
373
		'',
374
		'',
375
	);
376
377
	// One more query....
378
	$db_count = !isset($db_count) ? 1 : $db_count + 1;
379
	$db_replace_result = 0;
380
381 View Code Duplication
	if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
382
		smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
383
384 View Code Duplication
	if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
385
	{
386
		// Pass some values to the global space for use in the callback function.
387
		$db_callback = array($db_values, $connection);
388
389
		// Inject the values passed to this function.
390
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
391
392
		// This shouldn't be residing in global space any longer.
393
		$db_callback = array();
394
	}
395
396
	// Debugging.
397 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
398
	{
399
		// Get the file and line number this function was called.
400
		list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
401
402
		// Initialize $db_cache if not already initialized.
403
		if (!isset($db_cache))
404
			$db_cache = array();
405
406
		if (!empty($_SESSION['debug_redirect']))
407
		{
408
			$db_cache = array_merge($_SESSION['debug_redirect'], $db_cache);
409
			$db_count = count($db_cache) + 1;
410
			$_SESSION['debug_redirect'] = array();
411
		}
412
413
		$st = microtime();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $st. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
414
		// Don't overload it.
415
		$db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...';
416
		$db_cache[$db_count]['f'] = $file;
417
		$db_cache[$db_count]['l'] = $line;
418
		$db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start));
419
	}
420
421
	// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
422 View Code Duplication
	if (empty($modSettings['disableQueryCheck']))
423
	{
424
		$clean = '';
425
		$old_pos = 0;
426
		$pos = -1;
427
		while (true)
428
		{
429
			$pos = strpos($db_string, '\'', $pos + 1);
430
			if ($pos === false)
431
				break;
432
			$clean .= substr($db_string, $old_pos, $pos - $old_pos);
433
434
			while (true)
435
			{
436
				$pos1 = strpos($db_string, '\'', $pos + 1);
437
				$pos2 = strpos($db_string, '\\', $pos + 1);
438
				if ($pos1 === false)
439
					break;
440
				elseif ($pos2 === false || $pos2 > $pos1)
441
				{
442
					$pos = $pos1;
443
					break;
444
				}
445
446
				$pos = $pos2 + 1;
447
			}
448
			$clean .= ' %s ';
449
450
			$old_pos = $pos + 1;
451
		}
452
		$clean .= substr($db_string, $old_pos);
453
		$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
454
455
		// Comments?  We don't use comments in our queries, we leave 'em outside!
456
		if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
457
			$fail = true;
458
		// Trying to change passwords, slow us down, or something?
459
		elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
460
			$fail = true;
461
		elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
462
			$fail = true;
463
464
		if (!empty($fail) && function_exists('log_error'))
465
			smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
466
	}
467
468
	// Set optimize stuff
469
	if (isset($query_opt[$identifier]))
470
	{
471
		$query_hints = $query_opt[$identifier];
472
		$query_hints_set = '';
473
		if (isset($query_hints['join_collapse_limit']))
474
		{
475
			$query_hints_set .= 'SET LOCAL join_collapse_limit = ' . $query_hints['join_collapse_limit'] . ';';
476
		}
477
		if (isset($query_hints['enable_seqscan']))
478
		{
479
			$query_hints_set .= 'SET LOCAL enable_seqscan = ' . $query_hints['enable_seqscan'] . ';';
480
		}
481
482
		$db_string = $query_hints_set . $db_string;
483
		
484
		if (isset($db_show_debug) && $db_show_debug === true && $db_cache[$db_count]['q'] != '...')
485
			$db_cache[$db_count]['q'] = "\t\t" . $db_string;
486
	}
487
488
	$db_last_result = @pg_query($connection, $db_string);
489
490 View Code Duplication
	if ($db_last_result === false && empty($db_values['db_error_skip']))
491
		$db_last_result = smf_db_error($db_string, $connection);
492
493
	// Debugging.
494 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
495
		$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...
496
497
	return $db_last_result;
498
}
499
500
/**
501
 * affected_rows
502
 * @param resource $connection
0 ignored issues
show
Bug introduced by
There is no parameter named $connection. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
Documentation introduced by
Should the type for parameter $connection not be resource|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
503
 */
504
function smf_db_affected_rows($result = null)
0 ignored issues
show
Best Practice introduced by
The function smf_db_affected_rows() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L521-526) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
505
{
506
	global $db_last_result, $db_replace_result;
507
508
	if ($db_replace_result)
509
		return $db_replace_result;
510
	elseif ($result === null && !$db_last_result)
511
		return 0;
512
513
	return pg_affected_rows($result === null ? $db_last_result : $result);
514
}
515
516
/**
517
 * Gets the ID of the most recently inserted row.
518
 *
519
 * @param string $table The table (only used for Postgres)
520
 * @param string $field = null The specific field (not used here)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $field not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
521
 * @param resource $connection = null The connection (if null, $db_connection is used)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $connection not be resource|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
522
 * @return int The ID of the most recently inserted row
523
 */
524
function smf_db_insert_id($table, $field = null, $connection = null)
0 ignored issues
show
Best Practice introduced by
The function smf_db_insert_id() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L536-542) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

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...
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...
525
{
526
	global $smcFunc, $db_prefix;
527
528
	$table = str_replace('{db_prefix}', $db_prefix, $table);
529
530
	// Try get the last ID for the auto increment field.
531
	$request = $smcFunc['db_query']('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID',
532
		array(
533
		)
534
	);
535
	if (!$request)
536
		return false;
537
	list ($lastID) = $smcFunc['db_fetch_row']($request);
538
	$smcFunc['db_free_result']($request);
539
540
	return $lastID;
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)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $connection not be resource|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
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
Best Practice introduced by
The function smf_db_transaction() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L551-566) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
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 @pg_query($connection, 'BEGIN');
559
	elseif ($type == 'rollback')
560
		return @pg_query($connection, 'ROLLBACK');
561
	elseif ($type == 'commit')
562
		return @pg_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 resource $connection The connection to use (if null, $db_connection is used)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $connection not be object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
573
 */
574
function smf_db_error($db_string, $connection = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Best Practice introduced by
The function smf_db_error() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L575-757) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
575
{
576
	global $txt, $context, $modSettings;
577
	global $db_connection;
578
	global $db_show_debug;
579
580
	// We'll try recovering the file and line number the original db query was called from.
581
	list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
582
583
	// Decide which connection to use
584
	$connection = $connection === null ? $db_connection : $connection;
585
586
	// This is the error message...
587
	$query_error = @pg_last_error($connection);
588
589
	// Log the error.
590
	if (function_exists('log_error'))
591
		log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line);
592
593
	// Nothing's defined yet... just die with it.
594
	if (empty($context) || empty($txt))
595
		die($query_error);
596
597
	// Show an error message, if possible.
598
	$context['error_title'] = $txt['database_error'];
599
	if (allowedTo('admin_forum'))
600
		$context['error_message'] = nl2br($query_error) . '<br>' . $txt['file'] . ': ' . $file . '<br>' . $txt['line'] . ': ' . $line;
601
	else
602
		$context['error_message'] = $txt['try_again'];
603
604
	if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true)
605
	{
606
		$context['error_message'] .= '<br><br>' . nl2br($db_string);
607
	}
608
609
	// It's already been logged... don't log it again.
610
	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...
611
}
612
613
/**
614
 * A PostgreSQL specific function for tracking the current row...
615
 *
616
 * @param resource $request A PostgreSQL result resource
617
 * @param int $counter The row number in the result to fetch (false to fetch the next one)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $counter not be false|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
618
 * @return array The contents of the row that was fetched
619
 */
620 View Code Duplication
function smf_db_fetch_row($request, $counter = 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...
621
{
622
	global $db_row_count;
623
624
	if ($counter !== false)
625
		return pg_fetch_row($request, $counter);
626
627
	// Reset the row counter...
628
	if (!isset($db_row_count[(int) $request]))
629
		$db_row_count[(int) $request] = 0;
630
631
	// Return the right row.
632
	return @pg_fetch_row($request, $db_row_count[(int) $request]++);
633
}
634
635
/**
636
 * Get an associative array
637
 *
638
 * @param resource $request A PostgreSQL result resource
639
 * @param int $counter The row to get. If false, returns the next row.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $counter not be false|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
640
 * @return array An associative array of row contents
641
 */
642 View Code Duplication
function smf_db_fetch_assoc($request, $counter = 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...
643
{
644
	global $db_row_count;
645
646
	if ($counter !== false)
647
		return pg_fetch_assoc($request, $counter);
648
649
	// Reset the row counter...
650
	if (!isset($db_row_count[(int) $request]))
651
		$db_row_count[(int) $request] = 0;
652
653
	// Return the right row.
654
	return @pg_fetch_assoc($request, $db_row_count[(int) $request]++);
655
}
656
657
/**
658
 * Reset the pointer...
659
 *
660
 * @param resource $request A PostgreSQL result resource
661
 * @param int $counter The counter
662
 * @return bool Always returns true
663
 */
664
function smf_db_data_seek($request, $counter)
665
{
666
	global $db_row_count;
667
668
	$db_row_count[(int) $request] = $counter;
669
670
	return true;
671
}
672
673
/**
674
 * Unescape an escaped string!
675
 *
676
 * @param string $string The string to unescape
677
 * @return string The unescaped string
678
 */
679
function smf_db_unescape_string($string)
680
{
681
	return strtr($string, array('\'\'' => '\''));
682
}
683
684
/**
685
 * Inserts data into a table
686
 *
687
 * @param string $method The insert method - can be 'replace', 'ignore' or 'insert'
688
 * @param string $table The table we're inserting the data into
689
 * @param array $columns An array of the columns we're inserting the data into. Should contain 'column' => 'datatype' pairs
690
 * @param array $data The data to insert
691
 * @param array $keys The keys for the table
692
 * @param int returnmode 0 = nothing(default), 1 = last row id, 2 = all rows id as array; every mode runs only with method != 'ignore'
693
 * @param resource $connection The connection to use (if null, $db_connection is used)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $connection not be object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
694
 * @return mixed value of the first key, behavior based on returnmode. null if no data.
695
 */
696
function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $returnmode = 0, $connection = null)
0 ignored issues
show
Best Practice introduced by
The function smf_db_insert() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L771-907) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
697
{
698
	global $smcFunc, $db_connection, $db_prefix;
699
700
	$connection = $connection === null ? $db_connection : $connection;
701
702
	$replace = '';
703
704
	if (empty($data))
705
		return;
706
707
	if (!is_array($data[array_rand($data)]))
708
		$data = array($data);
709
710
	// Replace the prefix holder with the actual prefix.
711
	$table = str_replace('{db_prefix}', $db_prefix, $table);
712
713
	// PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead
714
	if ($method == 'replace')
715
	{
716
		$key_str = '';
717
		$col_str = '';
718
		static $pg_version;
719
		static $replace_support;
720
721
		if (empty($pg_version))
722
		{
723
			db_extend();
724
			//pg 9.5 got replace support
725
			$pg_version = $smcFunc['db_get_version']();
726
			// if we got a Beta Version
727 View Code Duplication
			if (stripos($pg_version, 'beta') !== false)
728
				$pg_version = substr($pg_version, 0, stripos($pg_version, 'beta')) . '.0';
729
			// or RC
730 View Code Duplication
			if (stripos($pg_version, 'rc') !== false)
731
				$pg_version = substr($pg_version, 0, stripos($pg_version, 'rc')) . '.0';
732
733
			$replace_support = (version_compare($pg_version, '9.5.0', '>=') ? true : false);
734
		}
735
736
		$count = 0;
737
		$where = '';
738
		$count_pk = 0;
739
740
		If ($replace_support)
741
		{
742
			foreach ($columns as $columnName => $type)
743
			{
744
				//check pk fiel
745
				IF (in_array($columnName, $keys))
746
				{
747
					$key_str .= ($count_pk > 0 ? ',' : '');
748
					$key_str .= $columnName;
749
					$count_pk++;
750
				}
751
				else //normal field
752
				{
753
					$col_str .= ($count > 0 ? ',' : '');
754
					$col_str .= $columnName . ' = EXCLUDED.' . $columnName;
755
					$count++;
756
				}
757
			}
758
			$replace = ' ON CONFLICT (' . $key_str . ') DO UPDATE SET ' . $col_str;
759
		}
760
		else
761
		{
762
			foreach ($columns as $columnName => $type)
763
			{
764
				// Are we restricting the length?
765
				if (strpos($type, 'string-') !== false)
766
					$actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count);
767
				else
768
					$actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count);
769
770
				// A key? That's what we were looking for.
771
				if (in_array($columnName, $keys))
772
					$where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2);
773
				$count++;
774
			}
775
776
			// Make it so.
777
			if (!empty($where) && !empty($data))
778
			{
779
				foreach ($data as $k => $entry)
780
				{
781
					$smcFunc['db_query']('', '
782
						DELETE FROM ' . $table .
783
						' WHERE ' . $where,
784
						$entry, $connection
785
					);
786
				}
787
			}
788
		}
789
	}
790
791
	$returning = '';
792
	$with_returning = false;
793
	// lets build the returning string, mysql allow only in normal mode
794 View Code Duplication
	if (!empty($keys) && (count($keys) > 0) && $returnmode > 0)
795
	{
796
		// we only take the first key
797
		$returning = ' RETURNING '.$keys[0];
798
		$with_returning = true;
799
	}
800
801
	if (!empty($data))
802
	{
803
		// Create the mold for a single row insert.
804
		$insertData = '(';
805
		foreach ($columns as $columnName => $type)
806
		{
807
			// Are we restricting the length?
808
			if (strpos($type, 'string-') !== false)
809
				$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
810
			else
811
				$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
812
		}
813
		$insertData = substr($insertData, 0, -2) . ')';
814
815
		// Create an array consisting of only the columns.
816
		$indexed_columns = array_keys($columns);
817
818
		// Here's where the variables are injected to the query.
819
		$insertRows = array();
820
		foreach ($data as $dataRow)
821
			$insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection);
822
823
		// Do the insert.
824
		$request = $smcFunc['db_query']('', '
825
			INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '")
826
			VALUES
827
				' . implode(',
828
				', $insertRows).$replace.$returning,
829
			array(
830
				'security_override' => true,
831
				'db_error_skip' => $method == 'ignore' || $table === $db_prefix . 'log_errors',
832
			),
833
			$connection
834
		);
835
836
		if ($with_returning && $request !== false)
837
		{
838
			if ($returnmode === 2)
839
				$return_var = array();
840
841
			while(($row = $smcFunc['db_fetch_row']($request)) && $with_returning)
842
			{
843
				if (is_numeric($row[0])) // try to emulate mysql limitation
844
				{
845
					if ($returnmode === 1)
846
						$return_var = $row[0];
847
					elseif ($returnmode === 2)
848
						$return_var[] = $row[0];
0 ignored issues
show
Bug introduced by
The variable $return_var does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
849
				}
850
				else
851
				{
852
					$with_returning = false;
853
					trigger_error('trying to returning ID Field which is not a Int field', E_USER_ERROR);
854
				}
855
			}
856
		}
857
	}
858
859
	if ($with_returning && !empty($return_var))
860
		return $return_var;
861
}
862
863
/**
864
 * Dummy function really. Doesn't do anything on PostgreSQL.
865
 *
866
 * @param string $db_name The database name
867
 * @param resource $db_connection The database connection
868
 * @return true Always returns true
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
869
 */
870
function smf_db_select_db($db_name, $db_connection)
0 ignored issues
show
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...
Unused Code introduced by
The parameter $db_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...
871
{
872
	return true;
873
}
874
875
/**
876
 * Get the current version.
877
 * @return string The client version
878
 */
879
function smf_db_version()
880
{
881
	$version = pg_version();
882
883
	return $version['client'];
884
}
885
886
/**
887
 * This function tries to work out additional error information from a back trace.
888
 *
889
 * @param string $error_message The error message
890
 * @param string $log_message The message to log
891
 * @param string|bool $error_type What type of error this is
892
 * @param string $file The file the error occurred in
0 ignored issues
show
Documentation introduced by
Should the type for parameter $file not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
893
 * @param int $line What line of $file the code which generated the error is on
0 ignored issues
show
Documentation introduced by
Should the type for parameter $line not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
894
 * @return void|array Returns an array with the file and line if $error_type is 'return'
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
895
 */
896 View Code Duplication
function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null)
0 ignored issues
show
Best Practice introduced by
The function smf_db_error_backtrace() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L919-959) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
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...
897
{
898
	if (empty($log_message))
899
		$log_message = $error_message;
900
901
	foreach (debug_backtrace() as $step)
902
	{
903
		// Found it?
904
		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)
905
		{
906
			$log_message .= '<br>Function: ' . $step['function'];
907
			break;
908
		}
909
910
		if (isset($step['line']))
911
		{
912
			$file = $step['file'];
913
			$line = $step['line'];
914
		}
915
	}
916
917
	// A special case - we want the file and line numbers for debugging.
918
	if ($error_type == 'return')
919
		return array($file, $line);
920
921
	// Is always a critical error.
922
	if (function_exists('log_error'))
923
		log_error($log_message, 'critical', $file, $line);
924
925
	if (function_exists('fatal_error'))
926
	{
927
		fatal_error($error_message, $error_type);
0 ignored issues
show
Bug introduced by
It seems like $error_type defined by parameter $error_type on line 896 can also be of type boolean; however, fatal_error() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
928
929
		// Cannot continue...
930
		exit;
931
	}
932
	elseif ($error_type)
933
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type);
934
	else
935
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
936
}
937
938
/**
939
 * Escape the LIKE wildcards so that they match the character and not the wildcard.
940
 *
941
 * @param string $string The string to escape
942
 * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards.
943
 * @return string The escaped string
944
 */
945 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...
Best Practice introduced by
The function smf_db_escape_wildcard_string() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L968-982) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
946
{
947
	$replacements = array(
948
		'%' => '\%',
949
		'_' => '\_',
950
		'\\' => '\\\\',
951
	);
952
953
	if ($translate_human_wildcards)
954
		$replacements += array(
955
			'*' => '%',
956
		);
957
958
	return strtr($string, $replacements);
959
}
960
961
/**
962
 * Fetches all rows from a result as an array 
963
 *
964
 * @param resource $request A PostgreSQL result resource
965
 * @return array An array that contains all rows (records) in the result resource
966
 */
967
function smf_db_fetch_all($request)
0 ignored issues
show
Best Practice introduced by
The function smf_db_fetch_all() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L1005-1009) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
968
{
969
	// Return the right row.
970
	return @pg_fetch_all($request);
971
}
972
973
?>