Completed
Push — release-2.1 ( 48a6f4...e3ceef )
by Colin
14:57 queued 06:49
created

Subs-Db-postgresql.php ➔ smf_db_native_replace()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 23
Code Lines 13

Duplication

Lines 4
Ratio 17.39 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 9
nop 0
dl 4
loc 23
rs 8.5906
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 2018 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
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...
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-101) 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...
33
{
34
	global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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
			'db_error_insert'			=> 'smf_db_error_insert',
65
			'db_custom_order'			=> 'smf_db_custom_order',
66
			'db_native_replace'			=> 'smf_db_native_replace',
67
		);
68
69
	if (!empty($db_options['persist']))
70
		$connection = @pg_pconnect((empty($db_server) ? '' : 'host=' . $db_server . ' ') . 'dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'' . (empty($db_options['port']) ? '' : ' port=\'' . $db_options['port'] . '\''));
71
	else
72
		$connection = @pg_connect((empty($db_server) ? '' : 'host=' . $db_server . ' ') . 'dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'' . (empty($db_options['port']) ? '' : ' port=\'' . $db_options['port'] . '\''));
73
74
	// Something's wrong, show an error if its fatal (which we assume it is)
75
	if (!$connection)
76
	{
77
		if (!empty($db_options['non_fatal']))
78
		{
79
			return null;
80
		}
81
		else
82
		{
83
			display_db_error();
84
		}
85
	}
86
87
	return $connection;
88
}
89
90
/**
91
 * Extend the database functionality. It calls the respective file's init
92
 * to add the implementations in that file to $smcFunc array.
93
 *
94
 * @param string $type Indicates which additional file to load. ('extra', 'packages')
95
 */
96
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 (L109-117) 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...
97
{
98
	global $sourcedir, $db_type;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
99
100
	require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-' . $db_type . '.php');
101
	$initFunc = 'db_' . $type . '_init';
102
	$initFunc();
103
}
104
105
/**
106
 * Fix the database prefix if necessary.
107
 * Does nothing on PostgreSQL
108
 *
109
 * @param string $db_prefix The database prefix
110
 * @param string $db_name The database name
111
 */
112
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 (L125-128) 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...
113
{
114
	return;
115
}
116
117
/**
118
 * Callback for preg_replace_callback on the query.
119
 * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board', etc), with
120
 * their current values from $user_info.
121
 * In addition, it performs checks and sanitization on the values sent to the database.
122
 *
123
 * @param array $matches The matches from preg_replace_callback
124
 * @return string The appropriate string depending on $matches[1]
125
 */
126
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 (L164-312) 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...
127
{
128
	global $db_callback, $user_info, $db_prefix, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

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

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
283
284
	// Only bother if there's something to replace.
285
	if (strpos($db_string, '{') !== false)
286
	{
287
		// This is needed by the callback function.
288
		$db_callback = array($db_values, $connection === null ? $db_connection : $connection);
289
290
		// Do the quoting and escaping
291
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
292
293
		// Clear this global variable.
294
		$db_callback = array();
295
	}
296
297
	return $db_string;
298
}
299
300
/**
301
 * Do a query.  Takes care of errors too.
302
 * Special queries may need additional replacements to be appropriate
303
 * for PostgreSQL.
304
 *
305
 * @param string $identifier An identifier. Only used in Postgres when we need to do things differently...
306
 * @param string $db_string The database string
307
 * @param array $db_values = array() The values to be inserted into the string
308
 * @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...
309
 * @return resource|bool Returns a MySQL result resource (for SELECT queries), true (for UPDATE queries) or false if the query failed
310
 */
311
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 (L351-488) 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...
312
{
313
	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
314
	global $db_callback, $db_last_result, $db_replace_result, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
315
316
	// Decide which connection to use.
317
	$connection = $connection === null ? $db_connection : $connection;
318
319
	// Special queries that need processing.
320
	$replacements = array(
321
		'consolidate_spider_stats' => array(
322
			'~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))',
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
		'profile_board_stats' => array(
337
			'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)',
338
		),
339
	);
340
341
	// Special optimizer Hints
342
	$query_opt = array(
343
		'load_board_info' => array(
344
			'join_collapse_limit' => 1,
345
		),
346
		'calendar_get_events' => array(
347
			'enable_seqscan' => 'off',
348
		),
349
	);
350
351 View Code Duplication
	if (isset($replacements[$identifier]))
352
		$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
353
354
	// Limits need to be a little different.
355
	$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string);
356
357
	if (trim($db_string) == '')
358
		return false;
359
360
	// Comments that are allowed in a query are preg_removed.
361
	static $allowed_comments_from = array(
362
		'~\s+~s',
363
		'~/\*!40001 SQL_NO_CACHE \*/~',
364
		'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
365
		'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
366
	);
367
	static $allowed_comments_to = array(
368
		' ',
369
		'',
370
		'',
371
		'',
372
	);
373
374
	// One more query....
375
	$db_count = !isset($db_count) ? 1 : $db_count + 1;
376
	$db_replace_result = 0;
377
378 View Code Duplication
	if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
379
		smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
380
381 View Code Duplication
	if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
382
	{
383
		// Pass some values to the global space for use in the callback function.
384
		$db_callback = array($db_values, $connection);
385
386
		// Inject the values passed to this function.
387
		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
388
389
		// This shouldn't be residing in global space any longer.
390
		$db_callback = array();
391
	}
392
393
	// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
394 View Code Duplication
	if (empty($modSettings['disableQueryCheck']))
395
	{
396
		$clean = '';
397
		$old_pos = 0;
398
		$pos = -1;
399
		// Remove the string escape for better runtime
400
		$db_string_1 = str_replace('\'\'','',$db_string);
401
		while (true)
402
		{
403
			$pos = strpos($db_string_1, '\'', $pos + 1);
404
			if ($pos === false)
405
				break;
406
			$clean .= substr($db_string_1, $old_pos, $pos - $old_pos);
407
408
			while (true)
409
			{
410
				$pos1 = strpos($db_string_1, '\'', $pos + 1);
411
				$pos2 = strpos($db_string_1, '\\', $pos + 1);
412
				if ($pos1 === false)
413
					break;
414
				elseif ($pos2 === false || $pos2 > $pos1)
415
				{
416
					$pos = $pos1;
417
					break;
418
				}
419
420
				$pos = $pos2 + 1;
421
			}
422
			$clean .= ' %s ';
423
424
			$old_pos = $pos + 1;
425
		}
426
		$clean .= substr($db_string_1, $old_pos);
427
		$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
428
429
		// Comments?  We don't use comments in our queries, we leave 'em outside!
430
		if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
431
			$fail = true;
432
		// Trying to change passwords, slow us down, or something?
433
		elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
434
			$fail = true;
435
		elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
436
			$fail = true;
437
438
		if (!empty($fail) && function_exists('log_error'))
439
			smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
440
	}
441
442
	// Set optimize stuff
443
	if (isset($query_opt[$identifier]))
444
	{
445
		$query_hints = $query_opt[$identifier];
446
		$query_hints_set = '';
447
		if (isset($query_hints['join_collapse_limit']))
448
		{
449
			$query_hints_set .= 'SET LOCAL join_collapse_limit = ' . $query_hints['join_collapse_limit'] . ';';
450
		}
451
		if (isset($query_hints['enable_seqscan']))
452
		{
453
			$query_hints_set .= 'SET LOCAL enable_seqscan = ' . $query_hints['enable_seqscan'] . ';';
454
		}
455
456
		$db_string = $query_hints_set . $db_string;
457
	}
458
459
	// Debugging.
460 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
461
	{
462
		// Get the file and line number this function was called.
463
		list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
464
465
		// Initialize $db_cache if not already initialized.
466
		if (!isset($db_cache))
467
			$db_cache = array();
468
469
		if (!empty($_SESSION['debug_redirect']))
470
		{
471
			$db_cache = array_merge($_SESSION['debug_redirect'], $db_cache);
472
			$db_count = count($db_cache) + 1;
473
			$_SESSION['debug_redirect'] = array();
474
		}
475
476
		// Don't overload it.
477
		$db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...';
478
		$db_cache[$db_count]['f'] = $file;
479
		$db_cache[$db_count]['l'] = $line;
480
		$db_cache[$db_count]['s'] = ($st = microtime(true)) - $time_start;
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...
481
	}
482
483
	$db_last_result = @pg_query($connection, $db_string);
484
485 View Code Duplication
	if ($db_last_result === false && empty($db_values['db_error_skip']))
486
		$db_last_result = smf_db_error($db_string, $connection);
487
488
	// Debugging.
489 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
490
		$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...
491
492
	return $db_last_result;
493
}
494
495
/**
496
 * Returns the amount of affected rows for a query.
497
 *
498
 * @param mixed $result
499
 *
500
 * @return int
501
 *
502
 */
503
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 (L495-500) 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...
504
{
505
	global $db_last_result, $db_replace_result;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
506
507
	if ($db_replace_result)
508
		return $db_replace_result;
509
	elseif ($result === null && !$db_last_result)
510
		return 0;
511
512
	return pg_affected_rows($result === null ? $db_last_result : $result);
513
}
514
515
/**
516
 * Gets the ID of the most recently inserted row.
517
 *
518
 * @param string $table The table (only used for Postgres)
519
 * @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...
520
 * @param resource $connection = null The connection (if null, $db_connection is used) (not used here)
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...
521
 * @return int The ID of the most recently inserted row
522
 */
523
function smf_db_insert_id($table, $field = null, $connection = null)
0 ignored issues
show
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...
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 (L510-516) 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...
524
{
525
	global $smcFunc, $db_prefix;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
526
527
	$table = str_replace('{db_prefix}', $db_prefix, $table);
528
529
	// Try get the last ID for the auto increment field.
530
	$request = $smcFunc['db_query']('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID',
531
		array(
532
		)
533
	);
534
	if (!$request)
535
		return false;
536
	list ($lastID) = $smcFunc['db_fetch_row']($request);
537
	$smcFunc['db_free_result']($request);
538
539
	return $lastID;
540
}
541
542
/**
543
 * Do a transaction.
544
 *
545
 * @param string $type The step to perform (i.e. 'begin', 'commit', 'rollback')
546
 * @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...
547
 * @return bool True if successful, false otherwise
548
 */
549 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...
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 (L525-540) 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...
550
{
551
	global $db_connection;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
552
553
	// Decide which connection to use
554
	$connection = $connection === null ? $db_connection : $connection;
555
556
	if ($type == 'begin')
557
		return @pg_query($connection, 'BEGIN');
558
	elseif ($type == 'rollback')
559
		return @pg_query($connection, 'ROLLBACK');
560
	elseif ($type == 'commit')
561
		return @pg_query($connection, 'COMMIT');
562
563
	return false;
564
}
565
566
/**
567
 * Database error!
568
 * Backtrace, log, try to fix.
569
 *
570
 * @param string $db_string The DB string
571
 * @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...
572
 */
573
function smf_db_error($db_string, $connection = null)
0 ignored issues
show
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 (L549-706) 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...
574
{
575
	global $txt, $context, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
576
	global $db_connection;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
577
	global $db_show_debug;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

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

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
622
623
	if ($counter !== false)
624
		return pg_fetch_row($request, $counter);
625
626
	// Reset the row counter...
627
	if (!isset($db_row_count[(int) $request]))
628
		$db_row_count[(int) $request] = 0;
629
630
	// Return the right row.
631
	return @pg_fetch_row($request, $db_row_count[(int) $request]++);
632
}
633
634
/**
635
 * Get an associative array
636
 *
637
 * @param resource $request A PostgreSQL result resource
638
 * @param int|bool $counter The row to get. If false, returns the next row.
639
 * @return array An associative array of row contents
640
 */
641 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...
642
{
643
	global $db_row_count;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
644
645
	if ($counter !== false)
646
		return pg_fetch_assoc($request, $counter);
647
648
	// Reset the row counter...
649
	if (!isset($db_row_count[(int) $request]))
650
		$db_row_count[(int) $request] = 0;
651
652
	// Return the right row.
653
	return @pg_fetch_assoc($request, $db_row_count[(int) $request]++);
654
}
655
656
/**
657
 * Reset the pointer...
658
 *
659
 * @param resource $request A PostgreSQL result resource
660
 * @param int $counter The counter
661
 * @return bool Always returns true
662
 */
663
function smf_db_data_seek($request, $counter)
664
{
665
	global $db_row_count;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
666
667
	$db_row_count[(int) $request] = $counter;
668
669
	return true;
670
}
671
672
/**
673
 * Unescape an escaped string!
674
 *
675
 * @param string $string The string to unescape
676
 * @return string The unescaped string
677
 */
678
function smf_db_unescape_string($string)
679
{
680
	return strtr($string, array('\'\'' => '\''));
681
}
682
683
/**
684
 * Inserts data into a table
685
 *
686
 * @param string $method The insert method - can be 'replace', 'ignore' or 'insert'
687
 * @param string $table The table we're inserting the data into
688
 * @param array $columns An array of the columns we're inserting the data into. Should contain 'column' => 'datatype' pairs
689
 * @param array $data The data to insert
690
 * @param array $keys The keys for the table
691
 * @param int returnmode 0 = nothing(default), 1 = last row id, 2 = all rows id as array; every mode runs only with method != 'ignore'
692
 * @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...
693
 * @return mixed value of the first key, behavior based on returnmode. null if no data.
694
 */
695
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 (L720-856) 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...
696
{
697
	global $smcFunc, $db_connection, $db_prefix;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
698
699
	$connection = $connection === null ? $db_connection : $connection;
700
701
	$replace = '';
702
703
	if (empty($data))
704
		return;
705
706
	if (!is_array($data[array_rand($data)]))
707
		$data = array($data);
708
709
	// Replace the prefix holder with the actual prefix.
710
	$table = str_replace('{db_prefix}', $db_prefix, $table);
711
712
	// PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead
713
	if ($method == 'replace' || $method == 'ignore')
714
	{
715
		$key_str = '';
716
		$col_str = '';
717
		$replace_support = $smcFunc['db_native_replace']();
718
719
		$count = 0;
720
		$where = '';
721
		$count_pk = 0;
722
723
		If ($replace_support)
724
		{
725
			foreach ($columns as $columnName => $type)
726
			{
727
				//check pk fiel
728
				IF (in_array($columnName, $keys))
729
				{
730
					$key_str .= ($count_pk > 0 ? ',' : '');
731
					$key_str .= $columnName;
732
					$count_pk++;
733
				}
734
				else if ($method == 'replace') //normal field
735
				{
736
					$col_str .= ($count > 0 ? ',' : '');
737
					$col_str .= $columnName . ' = EXCLUDED.' . $columnName;
738
					$count++;
739
				}
740
			}
741
			if ($method == 'replace')
742
				$replace = ' ON CONFLICT (' . $key_str . ') DO UPDATE SET ' . $col_str;
743
			else
744
				$replace = ' ON CONFLICT (' . $key_str . ') DO NOTHING';
745
		}
746
		else if ($method == 'replace')
747
		{
748
			foreach ($columns as $columnName => $type)
749
			{
750
				// Are we restricting the length?
751
				if (strpos($type, 'string-') !== false)
752
					$actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count);
753
				else
754
					$actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count);
755
756
				// A key? That's what we were looking for.
757
				if (in_array($columnName, $keys))
758
					$where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2);
759
				$count++;
760
			}
761
762
			// Make it so.
763
			if (!empty($where) && !empty($data))
764
			{
765
				foreach ($data as $k => $entry)
766
				{
767
					$smcFunc['db_query']('', '
768
						DELETE FROM ' . $table .
769
						' WHERE ' . $where,
770
						$entry, $connection
771
					);
772
				}
773
			}
774
		}
775
	}
776
777
	$returning = '';
778
	$with_returning = false;
779
	// lets build the returning string, mysql allow only in normal mode
780 View Code Duplication
	if (!empty($keys) && (count($keys) > 0) && $returnmode > 0)
781
	{
782
		// we only take the first key
783
		$returning = ' RETURNING '.$keys[0];
784
		$with_returning = true;
785
	}
786
787
	if (!empty($data))
788
	{
789
		// Create the mold for a single row insert.
790
		$insertData = '(';
791
		foreach ($columns as $columnName => $type)
792
		{
793
			// Are we restricting the length?
794
			if (strpos($type, 'string-') !== false)
795
				$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
796
			else
797
				$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
798
		}
799
		$insertData = substr($insertData, 0, -2) . ')';
800
801
		// Create an array consisting of only the columns.
802
		$indexed_columns = array_keys($columns);
803
804
		// Here's where the variables are injected to the query.
805
		$insertRows = array();
806
		foreach ($data as $dataRow)
807
			$insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection);
808
809
		// Do the insert.
810
		$request = $smcFunc['db_query']('', '
811
			INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '")
812
			VALUES
813
				' . implode(',
814
				', $insertRows).$replace.$returning,
815
			array(
816
				'security_override' => true,
817
				'db_error_skip' => $method == 'ignore' || $table === $db_prefix . 'log_errors',
818
			),
819
			$connection
820
		);
821
822
		if ($with_returning && $request !== false)
823
		{
824
			if ($returnmode === 2)
825
				$return_var = array();
826
827
			while(($row = $smcFunc['db_fetch_row']($request)) && $with_returning)
828
			{
829
				if (is_numeric($row[0])) // try to emulate mysql limitation
830
				{
831
					if ($returnmode === 1)
832
						$return_var = $row[0];
833
					elseif ($returnmode === 2)
834
						$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...
835
				}
836
				else
837
				{
838
					$with_returning = false;
839
					trigger_error('trying to returning ID Field which is not a Int field', E_USER_ERROR);
840
				}
841
			}
842
		}
843
	}
844
845
	if ($with_returning && !empty($return_var))
846
		return $return_var;
847
}
848
849
/**
850
 * Dummy function really. Doesn't do anything on PostgreSQL.
851
 *
852
 * @param string $db_name The database name
853
 * @param resource $db_connection The database connection
854
 * @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...
855
 */
856
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...
857
{
858
	return true;
859
}
860
861
/**
862
 * Get the current version.
863
 * @return string The client version
864
 */
865
function smf_db_version()
866
{
867
	$version = pg_version();
868
869
	return $version['client'];
870
}
871
872
/**
873
 * This function tries to work out additional error information from a back trace.
874
 *
875
 * @param string $error_message The error message
876
 * @param string $log_message The message to log
877
 * @param string|bool $error_type What type of error this is
878
 * @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...
879
 * @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...
880
 * @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...
881
 */
882 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 (L868-908) 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...
883
{
884
	if (empty($log_message))
885
		$log_message = $error_message;
886
887
	foreach (debug_backtrace() as $step)
888
	{
889
		// Found it?
890
		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)
891
		{
892
			$log_message .= '<br>Function: ' . $step['function'];
893
			break;
894
		}
895
896
		if (isset($step['line']))
897
		{
898
			$file = $step['file'];
899
			$line = $step['line'];
900
		}
901
	}
902
903
	// A special case - we want the file and line numbers for debugging.
904
	if ($error_type == 'return')
905
		return array($file, $line);
906
907
	// Is always a critical error.
908
	if (function_exists('log_error'))
909
		log_error($log_message, 'critical', $file, $line);
910
911
	if (function_exists('fatal_error'))
912
	{
913
		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 882 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...
914
915
		// Cannot continue...
916
		exit;
917
	}
918
	elseif ($error_type)
919
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type);
920
	else
921
		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
922
}
923
924
/**
925
 * Escape the LIKE wildcards so that they match the character and not the wildcard.
926
 *
927
 * @param string $string The string to escape
928
 * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards.
929
 * @return string The escaped string
930
 */
931 View Code Duplication
function smf_db_escape_wildcard_string($string, $translate_human_wildcards = false)
0 ignored issues
show
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 (L917-931) 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...
932
{
933
	$replacements = array(
934
		'%' => '\%',
935
		'_' => '\_',
936
		'\\' => '\\\\',
937
	);
938
939
	if ($translate_human_wildcards)
940
		$replacements += array(
941
			'*' => '%',
942
		);
943
944
	return strtr($string, $replacements);
945
}
946
947
/**
948
 * Fetches all rows from a result as an array
949
 *
950
 * @param resource $request A PostgreSQL result resource
951
 * @return array An array that contains all rows (records) in the result resource
952
 */
953
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 (L954-958) 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...
954
{
955
	// Return the right row.
956
	return @pg_fetch_all($request);
957
}
958
959
/**
960
 * Function to save errors in database in a safe way
961
 *
962
 * @param array with keys in this order id_member, log_time, ip, url, message, session, error_type, file, line
963
 * @return void
964
 */
965
function smf_db_error_insert($error_array)
0 ignored issues
show
Best Practice introduced by
The function smf_db_error_insert() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L966-989) 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...
966
{
967
	global  $db_prefix, $db_connection;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
968
	static $pg_error_data_prep;
969
970
	// without database we can't do anything
971
	if (empty($db_connection))
972
		return;
973
974
	if (empty($pg_error_data_prep))
975
			$pg_error_data_prep = pg_prepare($db_connection, 'smf_log_errors',
976
				'INSERT INTO ' . $db_prefix . 'log_errors(id_member, log_time, ip, url, message, session, error_type, file, line)
977
													VALUES(		$1,		$2,		$3, $4, 	$5,		$6,			$7,		$8,	$9)'
978
			);
979
980
	pg_execute($db_connection, 'smf_log_errors', $error_array);
981
}
982
983
/**
984
 * Function which constructs an optimize custom order string
985
 * as an improved alternative to find_in_set()
986
 *
987
 * @param string $field name
988
 * @param array $array_values Field values sequenced in array via order priority. Must cast to int.
989
 * @param boolean $desc default false
990
 * @return string case field when ... then ... end
991
 */
992 View Code Duplication
function smf_db_custom_order($field, $array_values, $desc = false)
0 ignored issues
show
Best Practice introduced by
The function smf_db_custom_order() has been defined more than once; this definition is ignored, only the first definition in Sources/Subs-Db-mysql.php (L1000-1011) 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...
993
{
994
	$return = 'CASE '. $field . ' ';
995
	$count = count($array_values);
996
	$then = ($desc ? ' THEN -' : ' THEN ');
997
998
	for ($i = 0; $i < $count; $i++)
999
		$return .= 'WHEN ' . (int) $array_values[$i] . $then . $i . ' ';
1000
1001
	$return .= 'END';
1002
	return $return;
1003
}
1004
1005
/**
1006
 * Function which return the information if the database supports native replace inserts
1007
 *
1008
 * @return boolean true or false
1009
 */
1010
function smf_db_native_replace()
1011
{
1012
	global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1013
	static $pg_version;
1014
	static $replace_support;
1015
1016
	if (empty($pg_version))
1017
	{
1018
		db_extend();
1019
		//pg 9.5 got replace support
1020
		$pg_version = $smcFunc['db_get_version']();
1021
		// if we got a Beta Version
1022 View Code Duplication
		if (stripos($pg_version, 'beta') !== false)
1023
			$pg_version = substr($pg_version, 0, stripos($pg_version, 'beta')) . '.0';
1024
		// or RC
1025 View Code Duplication
		if (stripos($pg_version, 'rc') !== false)
1026
			$pg_version = substr($pg_version, 0, stripos($pg_version, 'rc')) . '.0';
1027
1028
		$replace_support = (version_compare($pg_version, '9.5.0', '>=') ? true : false);
1029
	}
1030
1031
	return $replace_support;
1032
}
1033
1034
?>