Completed
Pull Request — development (#3050)
by John
23:37
created

Database_PostgreSQL   D

Complexity

Total Complexity 144

Size/Duplication

Total Lines 1020
Duplicated Lines 16.47 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 168
loc 1020
ccs 0
cts 570
cp 0
rs 4.4371
c 0
b 0
f 0
wmc 144
lcom 1
cbo 6

31 Methods

Rating   Name   Duplication   Size   Complexity  
B initiate() 11 29 6
A fix_prefix() 0 4 1
F query() 76 187 38
B affected_rows() 0 9 5
A insert_id() 0 23 3
A fetch_row() 14 14 3
A free_result() 0 5 1
A num_rows() 0 5 1
A num_fields() 0 4 1
A data_seek() 0 8 1
B db_transaction() 0 20 5
A last_error() 0 8 3
D error() 12 40 10
F insert() 8 105 25
A unescape_string() 0 4 1
A support_ignore() 0 4 1
C insert_sql() 24 70 10
D db_table_sql() 0 99 11
A db_list_tables() 0 20 3
B db_backup_table() 0 32 1
A db_server_version() 0 6 1
A db_title() 0 4 1
A db_case_sensitive() 0 4 1
A _replaceIdentifier() 9 9 2
A escape_string() 0 4 1
A fetch_assoc() 14 14 3
A db_server_info() 0 7 1
A db_client_version() 0 6 1
A select_db() 0 4 1
A db() 0 4 1
A validConnection() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Database_PostgreSQL often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Database_PostgreSQL, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file has all the main functions in it that relate to the Postgre database.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 2.0 dev
15
 *
16
 */
17
18
// Let's define the name of the class so that we will be able to use it in the instantiations
19
if (!defined('DB_TYPE'))
20
	define('DB_TYPE', 'PostgreSQL');
21
22
/**
23
 * PostgreSQL database class, implements database class to control mysql functions
24
 */
25
class Database_PostgreSQL extends Database_Abstract
26
{
27
	/**
28
	 * Holds current instance of the class
29
	 * @var Database_PostgreSQL
30
	 */
31
	private static $_db = null;
32
33
	/**
34
	 * Holds last query result
35
	 * @var string
36
	 */
37
	private $_db_last_result = null;
38
39
	/**
40
	 * Since PostgreSQL doesn't support INSERT REPLACE we are using this to remember
41
	 * the rows affected by the delete
42
	 * @var int
43
	 */
44
	private $_db_replace_result = null;
45
46
	/**
47
	 * A variable to remember if a transaction was started already or not
48
	 * @var boolean
49
	 */
50
	private $_in_transaction = false;
51
52
	/**
53
	 * Initializes a database connection.
54
	 * It returns the connection, if successful.
55
	 *
56
	 * @param string $db_server
57
	 * @param string $db_name
58
	 * @param string $db_user
59
	 * @param string $db_passwd
60
	 * @param string $db_prefix
61
	 * @param mixed[] $db_options
62
	 *
63
	 * @return resource
64
	 * @throws Elk_Exception
65
	 */
66
	public static function 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...
67
	{
68
		// initialize the instance... if not done already!
69
		if (self::$_db === null)
70
			self::$_db = new self();
71
72 View Code Duplication
		if (!empty($db_options['port']))
73
			$db_port = ' port=' . (int) $db_options['port'];
74
		else
75
			$db_port = '';
76
77
		if (!empty($db_options['persist']))
78
			$connection = @pg_pconnect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'');
79
		else
80
			$connection = @pg_connect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'');
81
82
		// Something's wrong, show an error if its fatal (which we assume it is)
83 View Code Duplication
		if (!$connection)
84
		{
85
			if (!empty($db_options['non_fatal']))
86
				return null;
87
			else
88
				Errors::instance()->display_db_error();
89
		}
90
91
		self::$_db->_connection = $connection;
92
93
		return $connection;
94
	}
95
96
	/**
97
	 * Fix the database prefix if necessary.
98
	 * Do nothing on postgreSQL
99
	 *
100
	 * @param string $db_prefix
101
	 * @param string $db_name
102
	 *
103
	 * @return string
104
	 */
105
	public function fix_prefix($db_prefix, $db_name)
106
	{
107
		return $db_prefix;
108
	}
109
110
	/**
111
	 * Do a query.  Takes care of errors too.
112
	 * Special queries may need additional replacements to be appropriate
113
	 * for PostgreSQL.
114
	 *
115
	 * @param string $identifier
116
	 * @param string $db_string
117
	 * @param mixed[] $db_values
118
	 * @param resource|null $connection
119
	 *
120
	 * @return bool|resource|string
121
	 * @throws Elk_Exception
122
	 */
123
	public function query($identifier, $db_string, $db_values = array(), $connection = null)
124
	{
125
		global $db_show_debug, $time_start, $modSettings;
126
127
		// Decide which connection to use.
128
		$connection = $connection === null ? $this->_connection : $connection;
129
130
		// Special queries that need processing.
131
		$replacements = array(
132
			'ban_suggest_error_ips' => array(
133
				'~RLIKE~' => '~',
134
				'~\\.~' => '\.',
135
			),
136
			'ban_suggest_message_ips' => array(
137
				'~RLIKE~' => '~',
138
				'~\\.~' => '\.',
139
			),
140
			'consolidate_spider_stats' => array(
141
				'~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))',
142
			),
143
			'attach_download_increase' => array(
144
				'~LOW_PRIORITY~' => '',
145
			),
146
			'insert_log_search_topics' => array(
147
				'~NOT RLIKE~' => '!~',
148
			),
149
			'insert_log_search_results_no_index' => array(
150
				'~NOT RLIKE~' => '!~',
151
			),
152
			'insert_log_search_results_subject' => array(
153
				'~NOT RLIKE~' => '!~',
154
			),
155
			'pm_conversation_list' => array(
156
				'~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']) : ''),
157
			),
158
			'profile_board_stats' => array(
159
				'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)',
160
			),
161
		);
162
163
		if (isset($replacements[$identifier]))
164
			$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
165
166
		// Limits need to be a little different.
167
		$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string);
168
169
		if (trim($db_string) == '')
170
			return false;
171
172
		// Comments that are allowed in a query are preg_removed.
173
		static $allowed_comments_from = array(
174
			'~\s+~s',
175
			'~/\*!40001 SQL_NO_CACHE \*/~',
176
			'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
177
			'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
178
		);
179
		static $allowed_comments_to = array(
180
			' ',
181
			'',
182
			'',
183
			'',
184
		);
185
186
		// One more query....
187
		$this->_query_count++;
188
		$this->_db_replace_result = null;
189
190 View Code Duplication
		if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
191
			$this->error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
192
193 View Code Duplication
		if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
194
		{
195
			// Store these values for use in the callback function.
196
			$this->_db_callback_values = $db_values;
197
			$this->_db_callback_connection = $connection;
198
199
			// Inject the values passed to this function.
200
			$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', array($this, 'replacement__callback'), $db_string);
201
202
			// No need for them any longer.
203
			$this->_db_callback_values = array();
204
			$this->_db_callback_connection = null;
205
		}
206
207
		// Debugging.
208 View Code Duplication
		if ($db_show_debug === true)
209
		{
210
			$debug = Debug::instance();
211
212
			// Get the file and line number this function was called.
213
			list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
214
215
			if (!empty($_SESSION['debug_redirect']))
216
			{
217
				$debug->merge_db($_SESSION['debug_redirect']);
218
				// @todo this may be off by 1
219
				$this->_query_count += count($_SESSION['debug_redirect']);
220
				$_SESSION['debug_redirect'] = array();
221
			}
222
223
			// Don't overload it.
224
			$st = microtime(true);
225
			$db_cache = array();
226
			$db_cache['q'] = $this->_query_count < 50 ? $db_string : '...';
227
			$db_cache['f'] = $file;
228
			$db_cache['l'] = $line;
229
			$db_cache['s'] = $st - $time_start;
230
		}
231
232
		// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
233
		if (empty($modSettings['disableQueryCheck']))
234
		{
235
			$clean = '';
236
			$old_pos = 0;
237
			$pos = -1;
238 View Code Duplication
			while (true)
239
			{
240
				$pos = strpos($db_string, '\'', $pos + 1);
241
				if ($pos === false)
242
					break;
243
				$clean .= substr($db_string, $old_pos, $pos - $old_pos);
244
245
				while (true)
246
				{
247
					$pos1 = strpos($db_string, '\'', $pos + 1);
248
					$pos2 = strpos($db_string, '\'\'', $pos + 1);
249
250
					if ($pos1 === false)
251
						break;
252
					elseif ($pos2 === false || $pos2 > $pos1)
253
					{
254
						$pos = $pos1;
255
						break;
256
					}
257
258
					$pos = $pos2 + 1;
259
				}
260
261
				$clean .= ' %s ';
262
				$old_pos = $pos + 1;
263
			}
264
265
			$clean .= substr($db_string, $old_pos);
266
			$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
267
268
			// Comments?  We don't use comments in our queries, we leave 'em outside!
269 View Code Duplication
			if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
270
				$fail = true;
271
			// Trying to change passwords, slow us down, or something?
272
			elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
273
				$fail = true;
274
			elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
275
				$fail = true;
276
277
			if (!empty($fail) && class_exists('Errors'))
278
				$this->error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
279
280
			// If we are updating something, better start a transaction so that indexes may be kept consistent
281
			if (!$this->_in_transaction && strpos($clean, 'update') !== false)
282
				$this->db_transaction('begin', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 128 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
283
		}
284
285
		$this->_db_last_result = @pg_query($connection, $db_string);
0 ignored issues
show
Security SQL Injection introduced by
$db_string can contain request data and is used in sql context(s) leading to a potential security vulnerability.

13 paths for user data to reach this point

  1. Path: Read from $_POST, and $user_info is assigned in sources/controllers/Post.controller.php on line 924
  1. Read from $_POST, and $user_info is assigned
    in sources/controllers/Post.controller.php on line 924
  2. $msgOptions is assigned
    in sources/controllers/Post.controller.php on line 1032
  3. $msgOptions is passed to modifyPost()
    in sources/controllers/Post.controller.php on line 1039
  4. $messages_columns is assigned
    in sources/subs/Post.subs.php on line 446
  5. $messages_columns is passed through implode()
    in sources/subs/Post.subs.php on line 486
  6. ``' UPDATE {db_prefix}messages SET ' . implode(', ', $messages_columns) . ' WHERE id_msg = {int:id_msg}'`` is passed to Database_PostgreSQL::query()
    in sources/subs/Post.subs.php on line 484
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  2. Path: Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString() in sources/controllers/Search.controller.php on line 166
  1. Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString()
    in sources/controllers/Search.controller.php on line 166
  2. $string is passed through str_replace(), and str_replace(array('-', '_', '.'), array('+', '/', '='), $string) is decoded by base64_decode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 643
  3. !empty($temp_params2) ? $temp_params2 : $temp_params is passed through explode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 647
  4. $data is assigned
    in sources/subs/Search/Search.php on line 649
  5. $data is passed through explode(), and explode('|\'|', $data) is passed through array_pad(), and $v is assigned
    in sources/subs/Search/Search.php on line 651
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 652
  7. Tainted property Search::$_search_params is read, and $this->_search_params['brd'] is passed through implode(), and Search::$_boardQuery is assigned
    in sources/subs/Search/Search.php on line 908
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1672
  9. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}' . ($this->_createTemporary ? 'tmp_' : '') . 'log_search_topics (' . ($this->_createTemporary ? '' : 'id_search, ') . 'id_topic)' : '') . ' SELECT ' . ($this->_createTemporary ? '' : $id_search . ', ') . 't.id_topic FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($subject_query['inner_join']))) . (empty($subject_query['left_join']) ? '' : ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($subject_query['left_join']))) . ' WHERE ' . implode(' AND ', array_unique($subject_query['where'])) . (empty($modSettings['search_max_results']) ? '' : ' LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults))`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1695
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  3. Path: Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString() in sources/controllers/Search.controller.php on line 302
  1. Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString()
    in sources/controllers/Search.controller.php on line 302
  2. $string is passed through str_replace(), and str_replace(array('-', '_', '.'), array('+', '/', '='), $string) is decoded by base64_decode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 643
  3. !empty($temp_params2) ? $temp_params2 : $temp_params is passed through explode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 647
  4. $data is assigned
    in sources/subs/Search/Search.php on line 649
  5. $data is passed through explode(), and explode('|\'|', $data) is passed through array_pad(), and $v is assigned
    in sources/subs/Search/Search.php on line 651
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 652
  7. Tainted property Search::$_search_params is read, and $this->_search_params['brd'] is passed through implode(), and Search::$_boardQuery is assigned
    in sources/subs/Search/Search.php on line 908
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1672
  9. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}' . ($this->_createTemporary ? 'tmp_' : '') . 'log_search_topics (' . ($this->_createTemporary ? '' : 'id_search, ') . 'id_topic)' : '') . ' SELECT ' . ($this->_createTemporary ? '' : $id_search . ', ') . 't.id_topic FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($subject_query['inner_join']))) . (empty($subject_query['left_join']) ? '' : ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($subject_query['left_join']))) . ' WHERE ' . implode(' AND ', array_unique($subject_query['where'])) . (empty($modSettings['search_max_results']) ? '' : ' LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults))`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1695
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  4. Path: Read from $_REQUEST, and $_REQUEST is passed to Search::mergeSearchParams() in sources/controllers/Search.controller.php on line 304
  1. Read from $_REQUEST, and $_REQUEST is passed to Search::mergeSearchParams()
    in sources/controllers/Search.controller.php on line 304
  2. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 738
  3. Tainted property Search::$_search_params is read, and $this->_search_params['brd'] is passed through implode(), and Search::$_boardQuery is assigned
    in sources/subs/Search/Search.php on line 908
  4. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1672
  5. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}' . ($this->_createTemporary ? 'tmp_' : '') . 'log_search_topics (' . ($this->_createTemporary ? '' : 'id_search, ') . 'id_topic)' : '') . ' SELECT ' . ($this->_createTemporary ? '' : $id_search . ', ') . 't.id_topic FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($subject_query['inner_join']))) . (empty($subject_query['left_join']) ? '' : ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($subject_query['left_join']))) . ' WHERE ' . implode(' AND ', array_unique($subject_query['where'])) . (empty($modSettings['search_max_results']) ? '' : ' LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults))`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1695
  6. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  5. Path: Read from $_GET in sources/subs/Search/Search.php on line 962
  1. Read from $_GET
    in sources/subs/Search/Search.php on line 962
  2. Data is passed through htmlspecialchars_decode()
    in vendor/sources/Subs.php on line 550
  3. Data is passed through str_replace()
    in vendor/sources/Subs.php on line 551
  4. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 962
  5. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1172
  6. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1203
  7. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1211
  8. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1222
  9. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}log_search_results (' . implode(', ', array_keys($main_query['select'])) . ')' : '') . ' SELECT ' . implode(', ', $main_query['select']) . ' FROM ' . $main_query['from'] . (!empty($main_query['inner_join']) ? ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($main_query['inner_join'])) : '') . (!empty($main_query['left_join']) ? ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($main_query['left_join'])) : '') . (!empty($main_query['where']) ? ' WHERE ' : '') . implode(' AND ', array_unique($main_query['where'])) . (!empty($main_query['group_by']) ? ' GROUP BY ' . implode(', ', array_unique($main_query['group_by'])) : '') . (!empty($main_query['parameters']['limit']) ? ' LIMIT {int:limit}' : '')`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1919
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  6. Path: Read from $_POST, and Search::$_search_params is assigned in sources/subs/Search/Search.php on line 966
  1. Read from $_POST, and Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 966
  2. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1172
  3. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1203
  4. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1211
  5. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1222
  6. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}log_search_results (' . implode(', ', array_keys($main_query['select'])) . ')' : '') . ' SELECT ' . implode(', ', $main_query['select']) . ' FROM ' . $main_query['from'] . (!empty($main_query['inner_join']) ? ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($main_query['inner_join'])) : '') . (!empty($main_query['left_join']) ? ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($main_query['left_join'])) : '') . (!empty($main_query['where']) ? ' WHERE ' : '') . implode(' AND ', array_unique($main_query['where'])) . (!empty($main_query['group_by']) ? ' GROUP BY ' . implode(', ', array_unique($main_query['group_by'])) : '') . (!empty($main_query['parameters']['limit']) ? ' LIMIT {int:limit}' : '')`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1919
  7. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  8. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  7. Path: Read from $_POST, and $_POST is passed to Data_Validator::is_valid() in sources/controllers/Post.controller.php on line 883
  1. Read from $_POST, and $_POST is passed to Data_Validator::is_valid()
    in sources/controllers/Post.controller.php on line 883
  2. $data is passed to Data_Validator::validate()
    in sources/subs/DataValidator.class.php on line 147
  3. Data_Validator::$_data is assigned
    in sources/subs/DataValidator.class.php on line 249
  4. Tainted property Data_Validator::$_data is read
    in sources/subs/DataValidator.class.php on line 281
  5. Data_Validator::validation_data() returns tainted data
    in sources/subs/HttpReq.class.php on line 359
  6. HttpReq::cleanValue() returns tainted data, and HttpReq::$_param is assigned
    in sources/subs/HttpReq.class.php on line 219
  7. Tainted property HttpReq::$_param is read
    in sources/subs/HttpReq.class.php on line 278
  8. HttpReq::getQuery() returns tainted data, and $sort is assigned
    in sources/controllers/Memberlist.controller.php on line 407
  9. $field is assigned
    in sources/controllers/Memberlist.controller.php on line 501
  10. $customJoin is assigned
    in sources/controllers/Memberlist.controller.php on line 505
  11. $customJoin is passed through array_unique(), and array_unique($customJoin) is passed to ml_searchMembers()
    in sources/controllers/Memberlist.controller.php on line 518
  12. $customJoin is passed through implode()
    in sources/subs/Memberlist.subs.php on line 235
  13. ``' SELECT COUNT(*) FROM {db_prefix}members AS mem LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:regular_id_group} THEN mem.id_post_group ELSE mem.id_group END) ' . (empty($customJoin) ? '' : implode(' ', $customJoin)) . ' WHERE (' . $where . ') AND mem.is_activated = {int:is_activated}'`` is passed to Database_PostgreSQL::query()
    in sources/subs/Memberlist.subs.php on line 230
  14. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  8. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned in sources/Request.php on line 207
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned
    in sources/Request.php on line 207
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 560
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 564
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 570
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 579
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  9. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned in sources/Request.php on line 209
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned
    in sources/Request.php on line 209
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 560
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 564
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 570
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 579
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  10. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned in sources/Request.php on line 216
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned
    in sources/Request.php on line 216
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 560
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 564
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 570
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 579
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  11. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned in sources/Request.php on line 218
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned
    in sources/Request.php on line 218
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 560
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 564
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 570
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 579
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  12. Path: Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and $_SERVER['HTTP_X_FORWARDED_FOR'] is passed through explode(), and explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']) is passed through array_reverse(), and $ips is assigned in sources/Request.php on line 225
  1. Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and $_SERVER['HTTP_X_FORWARDED_FOR'] is passed through explode(), and explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']) is passed through array_reverse(), and $ips is assigned
    in sources/Request.php on line 225
  2. $ip is assigned
    in sources/Request.php on line 228
  3. $ip is passed through trim(), and Request::$_ban_ip is assigned
    in sources/Request.php on line 235
  4. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  5. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 487
  6. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  8. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  9. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  10. $var is assigned
    in sources/subs/Members.subs.php on line 677
  11. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  12. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  13. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 560
  14. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 564
  15. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 570
  16. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 579
  17. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167
  13. Path: Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and Request::$_ban_ip is assigned in sources/Request.php on line 241
  1. Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and Request::$_ban_ip is assigned
    in sources/Request.php on line 241
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 560
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 564
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 570
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 579
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 167

Preventing SQL Injection

There are two options to prevent SQL injection. Generally, it is recommended to use parameter binding:

$stmt = mysqli_prepare("SELECT * FROM users WHERE name = ?");
$stmt->bind_param("s", $taintedUserName);

An alternative – although generally not recommended – is to escape your data manually:

$mysqli = new mysqli('localhost', 'user', 'pass', 'dbname');

$escaped = $mysqli->real_escape_string($taintedUserName);
$mysqli->query("SELECT * FROM users WHERE name = '".$escaped."'");

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
286
287
		if ($this->_db_last_result === false && !$this->_skip_error)
288
		{
289
			$this->error($db_string, $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 128 can also be of type object<mysqli>; however, Database_PostgreSQL::error() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
290
		}
291
292
		// Revert not to skip errors
293
		if ($this->_skip_error === true)
294
		{
295
			$this->_skip_error = false;
296
		}
297
298
		if ($this->_in_transaction)
299
			$this->db_transaction('commit', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 128 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
300
301
		// Debugging.
302 View Code Duplication
		if ($db_show_debug === true)
303
		{
304
			$db_cache['t'] = microtime(true) - $st;
0 ignored issues
show
Bug introduced by
The variable $db_cache 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...
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...
305
			$debug->db_query($db_cache);
0 ignored issues
show
Bug introduced by
The variable $debug 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...
306
		}
307
308
		return $this->_db_last_result;
309
	}
310
311
	/**
312
	 * Affected rows from previous operation.
313
	 *
314
	 * @param resource|null $result
315
	 */
316
	public function affected_rows($result = null)
317
	{
318
		if ($this->_db_replace_result !== null)
319
			return $this->_db_replace_result;
320
		elseif ($result === null && !$this->_db_last_result)
321
			return 0;
322
323
		return pg_affected_rows($result === null ? $this->_db_last_result : $result);
324
	}
325
326
	/**
327
	 * Last inserted id.
328
	 *
329
	 * @param string $table
330
	 * @param string|null $field = null
331
	 * @param resource|null $connection = null
332
	 * @throws Elk_Exception
333
	 */
334
	public function insert_id($table, $field = null, $connection = null)
335
	{
336
		global $db_prefix;
337
338
		$table = str_replace('{db_prefix}', $db_prefix, $table);
339
340
		$connection = $connection === null ? $this->_connection : $connection;
341
342
		// Try get the last ID for the auto increment field.
343
		$request = $this->query('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID',
344
			array(
345
			),
346
			$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 340 can also be of type object<mysqli>; however, Database_PostgreSQL::query() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
347
		);
348
349
		if (!$request)
350
			return false;
351
352
		list ($lastID) = $this->fetch_row($request);
0 ignored issues
show
Documentation introduced by
$request is of type boolean|string, but the function expects a resource.

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...
353
		$this->free_result($request);
0 ignored issues
show
Documentation introduced by
$request is of type boolean|string, but the function expects a resource.

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...
354
355
		return $lastID;
356
	}
357
358
	/**
359
	 * Tracking the current row.
360
	 * Fetch a row from the resultset given as parameter.
361
	 *
362
	 * @param resource $request
363
	 * @param integer|bool $counter = false
364
	 */
365 View Code Duplication
	public function fetch_row($request, $counter = false)
366
	{
367
		global $db_row_count;
368
369
		if ($counter !== false)
370
			return pg_fetch_row($request, $counter);
371
372
		// Reset the row counter...
373
		if (!isset($db_row_count[(int) $request]))
374
			$db_row_count[(int) $request] = 0;
375
376
		// Return the right row.
377
		return @pg_fetch_row($request, $db_row_count[(int) $request]++);
378
	}
379
380
	/**
381
	 * Free the resultset.
382
	 *
383
	 * @param resource $result
384
	 */
385
	public function free_result($result)
386
	{
387
		// Just delegate to the native function
388
		pg_free_result($result);
389
	}
390
391
	/**
392
	 * Get the number of rows in the result.
393
	 *
394
	 * @param resource $result
395
	 */
396
	public function num_rows($result)
397
	{
398
		// simply delegate to the native function
399
		return pg_num_rows($result);
400
	}
401
402
	/**
403
	 * Get the number of fields in the resultset.
404
	 *
405
	 * @param resource $request
406
	 */
407
	public function num_fields($request)
408
	{
409
		return pg_num_fields($request);
410
	}
411
412
	/**
413
	 * Reset the internal result pointer.
414
	 *
415
	 * @param boolean $request
416
	 * @param integer $counter
417
	 */
418
	public function data_seek($request, $counter)
419
	{
420
		global $db_row_count;
421
422
		$db_row_count[(int) $request] = $counter;
423
424
		return true;
425
	}
426
427
	/**
428
	 * Do a transaction.
429
	 *
430
	 * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
431
	 * @param resource|null $connection = null
432
	 */
433
	public function db_transaction($type = 'commit', $connection = null)
434
	{
435
		// Decide which connection to use
436
		$connection = $connection === null ? $this->_connection : $connection;
437
438
		if ($type == 'begin')
439
		{
440
			$this->_in_transaction = true;
441
			return @pg_query($connection, 'BEGIN');
442
		}
443
		elseif ($type == 'rollback')
444
			return @pg_query($connection, 'ROLLBACK');
445
		elseif ($type == 'commit')
446
		{
447
			$this->_in_transaction = false;
448
			return @pg_query($connection, 'COMMIT');
449
		}
450
451
		return false;
452
	}
453
454
	/**
455
	 * Return last error string from the database server
456
	 *
457
	 * @param resource|null $connection = null
458
	 */
459
	public function last_error($connection = null)
460
	{
461
		// Decide which connection to use
462
		$connection = $connection === null ? $this->_connection : $connection;
463
464
		if (is_resource($connection))
465
			return pg_last_error($connection);
466
	}
467
468
	/**
469
	 * Database error.
470
	 * Backtrace, log, try to fix.
471
	 *
472
	 * @param string        $db_string
473
	 * @param resource|null $connection = null
474
	 *
475
	 * @throws Elk_Exception
476
	 */
477
	public function error($db_string, $connection = null)
478
	{
479
		global $txt, $context, $modSettings, $db_show_debug;
480
481
		// We'll try recovering the file and line number the original db query was called from.
482
		list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
483
484
		// Decide which connection to use
485
		$connection = $connection === null ? $this->_connection : $connection;
486
487
		// This is the error message...
488
		$query_error = @pg_last_error($connection);
489
490
		// Log the error.
491 View Code Duplication
		if (class_exists('Errors'))
492
		{
493
			Errors::instance()->log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line);
494
		}
495
496
		// Nothing's defined yet... just die with it.
497
		if (empty($context) || empty($txt))
498
			die($query_error);
0 ignored issues
show
Coding Style Compatibility introduced by
The method error() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
499
500
		// Show an error message, if possible.
501
		$context['error_title'] = $txt['database_error'];
502 View Code Duplication
		if (allowedTo('admin_forum'))
503
			$context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line;
504
		else
505
			$context['error_message'] = $txt['try_again'];
506
507
		// Add database version that we know of, for the admin to know. (and ask for support)
508 View Code Duplication
		if (allowedTo('admin_forum'))
509
			$context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $modSettings['elkVersion']);
510
511 View Code Duplication
		if (allowedTo('admin_forum') && $db_show_debug === true)
512
			$context['error_message'] .= '<br /><br />' . nl2br($db_string);
513
514
		// It's already been logged... don't log it again.
515
		throw new Elk_Exception($context['error_message'], false);
516
	}
517
518
	/**
519
	 * Insert data.
520
	 *
521
	 * @param string $method - options 'replace', 'ignore', 'insert'
522
	 * @param string $table
523
	 * @param mixed[] $columns
524
	 * @param mixed[] $data
525
	 * @param mixed[] $keys
526
	 * @param bool $disable_trans = false
527
	 * @param resource|null $connection = null
528
	 * @throws Elk_Exception
529
	 */
530
	public function insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
531
	{
532
		global $db_prefix;
533
534
		$connection = $connection === null ? $this->_connection : $connection;
535
536
		// With nothing to insert, simply return.
537
		if (empty($data))
538
			return;
539
540
		// Inserting data as a single row can be done as a single array.
541
		if (!is_array($data[array_rand($data)]))
542
			$data = array($data);
543
544
		// Replace the prefix holder with the actual prefix.
545
		$table = str_replace('{db_prefix}', $db_prefix, $table);
546
547
		$priv_trans = false;
548
		if ((count($data) > 1 || $method == 'replace') && !$this->_in_transaction && !$disable_trans)
549
		{
550
			$this->db_transaction('begin', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 534 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
551
			$priv_trans = true;
552
		}
553
554
		// PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead
555
		if ($method == 'replace')
556
		{
557
			$count = 0;
558
			$where = '';
559
			$db_replace_result = 0;
560
			foreach ($columns as $columnName => $type)
561
			{
562
				// Are we restricting the length?
563
				if (strpos($type, 'string-') !== false)
564
					$actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count);
565
				else
566
					$actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count);
567
568
				// A key? That's what we were looking for.
569
				if (in_array($columnName, $keys))
570
					$where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2);
571
				$count++;
572
			}
573
574
			// Make it so.
575
			if (!empty($where) && !empty($data))
576
			{
577
				foreach ($data as $k => $entry)
578
				{
579
					$this->query('', '
580
						DELETE FROM ' . $table .
581
						' WHERE ' . $where,
582
						$entry, $connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 534 can also be of type object<mysqli>; however, Database_PostgreSQL::query() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
583
					);
584
					$db_replace_result += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
585
				}
586
			}
587
		}
588
589
		if (!empty($data))
590
		{
591
			// Create the mold for a single row insert.
592
			$insertData = '(';
593 View Code Duplication
			foreach ($columns as $columnName => $type)
594
			{
595
				// Are we restricting the length?
596
				if (strpos($type, 'string-') !== false)
597
					$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
598
				else
599
					$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
600
			}
601
			$insertData = substr($insertData, 0, -2) . ')';
602
603
			// Create an array consisting of only the columns.
604
			$indexed_columns = array_keys($columns);
605
606
			// Here's where the variables are injected to the query.
607
			$insertRows = array();
608
			foreach ($data as $dataRow)
609
				$insertRows[] = $this->quote($insertData, $this->_array_combine($indexed_columns, $dataRow), $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 534 can also be of type resource; however, Database_Abstract::quote() does only seem to accept object<mysqli>|object<postgre>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
610
611
			$inserted_results = 0;
612
			$skip_error = $method == 'ignore' || $table === $db_prefix . 'log_errors';
613
			$this->_skip_error = $skip_error;
614
615
			// Do the insert.
616
			$this->query('', '
617
				INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '")
618
				VALUES
619
				' . implode(',
620
				', $insertRows),
621
				array(
622
					'security_override' => true,
623
				),
624
				$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 534 can also be of type object<mysqli>; however, Database_PostgreSQL::query() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
625
			);
626
			$inserted_results += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
627
628
			if (isset($db_replace_result))
629
				$this->_db_replace_result = $db_replace_result + $inserted_results;
630
		}
631
632
		if ($priv_trans)
633
			$this->db_transaction('commit', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 534 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
634
	}
635
636
	/**
637
	 * Unescape an escaped string!
638
	 *
639
	 * @param string $string
640
	 */
641
	public function unescape_string($string)
642
	{
643
		return strtr($string, array('\'\'' => '\''));
644
	}
645
646
	/**
647
	 * Returns whether the database system supports ignore.
648
	 *
649
	 * @return false
650
	 */
651
	public function support_ignore()
652
	{
653
		return false;
654
	}
655
656
	/**
657
	 * Gets all the necessary INSERTs for the table named table_name.
658
	 * It goes in 250 row segments.
659
	 *
660
	 * @param string $tableName - the table to create the inserts for.
661
	 * @param bool $new_table
662
	 *
663
	 * @return string the query to insert the data back in, or an empty string if the table was empty.
664
	 * @throws Elk_Exception
665
	 */
666
	public function insert_sql($tableName, $new_table = false)
667
	{
668
		global $db_prefix;
669
670
		static $start = 0, $num_rows, $fields, $limit;
671
672 View Code Duplication
		if ($new_table)
673
		{
674
			$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
675
			$start = 0;
676
		}
677
678
		$data = '';
679
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
680
681
		// This will be handy...
682
		$crlf = "\r\n";
683
684
		$result = $this->query('', '
685
			SELECT *
686
			FROM ' . $tableName . '
687
			LIMIT ' . $start . ', ' . $limit,
688
			array(
689
				'security_override' => true,
690
			)
691
		);
692
693
		// The number of rows, just for record keeping and breaking INSERTs up.
694
		$num_rows = $this->num_rows($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 684 can also be of type boolean or string; however, Database_PostgreSQL::num_rows() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
695
696
		if ($num_rows == 0)
697
			return '';
698
699
		if ($new_table)
700
		{
701
			$fields = array_keys($this->fetch_assoc($result));
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 684 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
702
			$this->data_seek($result, 0);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 684 can also be of type resource or string; however, Database_PostgreSQL::data_seek() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
703
		}
704
705
		// Start it off with the basic INSERT INTO.
706
		$insert_msg = 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t";
707
708
		// Loop through each row.
709 View Code Duplication
		while ($row = $this->fetch_assoc($result))
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 684 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
710
		{
711
			// Get the fields in this row...
712
			$field_list = array();
713
714
			foreach ($row as $key => $item)
715
			{
716
				// Try to figure out the type of each field. (NULL, number, or 'string'.)
717
				if (!isset($item))
718
					$field_list[] = 'NULL';
719
				elseif (is_numeric($item) && (int) $item == $item)
720
					$field_list[] = $item;
721
				else
722
					$field_list[] = '\'' . $this->escape_string($item) . '\'';
723
			}
724
725
			// 'Insert' the data.
726
			$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
727
		}
728
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 684 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
729
730
		$data .= $crlf;
731
732
		$start += $limit;
733
734
		return $data;
735
	}
736
737
	/**
738
	 * Dumps the schema (CREATE) for a table.
739
	 *
740
	 * @param string $tableName - the table
741
	 *
742
	 * @return string - the CREATE statement as string
743
	 * @throws Elk_Exception
744
	 */
745
	public function db_table_sql($tableName)
746
	{
747
		global $db_prefix;
748
749
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
750
751
		// This will be needed...
752
		$crlf = "\r\n";
753
754
		// Start the create table...
755
		$schema_create = 'CREATE TABLE ' . $tableName . ' (' . $crlf;
756
		$index_create = '';
757
		$seq_create = '';
758
759
		// Find all the fields.
760
		$result = $this->query('', '
761
			SELECT column_name, column_default, is_nullable, data_type, character_maximum_length
762
			FROM information_schema.columns
763
			WHERE table_name = {string:table}
764
			ORDER BY ordinal_position',
765
			array(
766
				'table' => $tableName,
767
			)
768
		);
769
		while ($row = $this->fetch_assoc($result))
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 760 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
770
		{
771
			if ($row['data_type'] == 'character varying')
772
				$row['data_type'] = 'varchar';
773
			elseif ($row['data_type'] == 'character')
774
				$row['data_type'] = 'char';
775
776
			if ($row['character_maximum_length'])
777
				$row['data_type'] .= '(' . $row['character_maximum_length'] . ')';
778
779
			// Make the CREATE for this column.
780
			$schema_create .= ' "' . $row['column_name'] . '" ' . $row['data_type'] . ($row['is_nullable'] != 'YES' ? ' NOT NULL' : '');
781
782
			// Add a default...?
783
			if (trim($row['column_default']) != '')
784
			{
785
				$schema_create .= ' default ' . $row['column_default'] . '';
786
787
				// Auto increment?
788
				if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0)
789
				{
790
					// Get to find the next variable first!
791
					$count_req = $this->query('', '
792
						SELECT MAX("{raw:column}")
793
						FROM {raw:table}',
794
						array(
795
							'column' => $row['column_name'],
796
							'table' => $tableName,
797
						)
798
					);
799
					list ($max_ind) = $this->fetch_row($count_req);
0 ignored issues
show
Bug introduced by
It seems like $count_req can also be of type boolean or string; however, Database_PostgreSQL::fetch_row() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
800
					$this->free_result($count_req);
0 ignored issues
show
Bug introduced by
It seems like $count_req defined by $this->query('', ' ...'table' => $tableName)) on line 791 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
801
802
					// Get the right bloody start!
803
					$seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf;
804
				}
805
			}
806
807
			$schema_create .= ',' . $crlf;
808
		}
809
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 760 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
810
811
		// Take off the last comma.
812
		$schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
813
814
		$result = $this->query('', '
815
			SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, pg_get_indexdef(i.indexrelid) AS inddef
816
			FROM pg_class AS c
817
				INNER JOIN pg_index AS i ON (i.indrelid = c.oid)
818
				INNER JOIN pg_class AS c2 ON (c2.oid = i.indexrelid)
819
			WHERE c.relname = {string:table}',
820
			array(
821
				'table' => $tableName,
822
			)
823
		);
824
825
		while ($row = $this->fetch_assoc($result))
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 814 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
826
		{
827
			if ($row['is_primary'])
828
			{
829
				if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0)
830
					continue;
831
832
				$index_create .= $crlf . 'ALTER TABLE ' . $tableName . ' ADD PRIMARY KEY ("' . $matches[1] . '");';
833
			}
834
			else
835
				$index_create .= $crlf . $row['inddef'] . ';';
836
		}
837
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 814 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
838
839
		// Finish it off!
840
		$schema_create .= $crlf . ');';
841
842
		return $seq_create . $schema_create . $index_create;
843
	}
844
845
	/**
846
	 * {@inheritdoc}
847
	 */
848
	public function db_list_tables($db_name_str = false, $filter = false)
849
	{
850
		$request = $this->query('', '
851
			SELECT tablename
852
			FROM pg_tables
853
			WHERE schemaname = {string:schema_public}' . ($filter === false ? '' : '
854
				AND tablename LIKE {string:filter}') . '
855
			ORDER BY tablename',
856
			array(
857
				'schema_public' => 'public',
858
				'filter' => $filter,
859
			)
860
		);
861
		$tables = array();
862
		while ($row = $this->fetch_row($request))
0 ignored issues
show
Bug introduced by
It seems like $request defined by $this->query('', ' SE..., 'filter' => $filter)) on line 850 can also be of type boolean or string; however, Database_PostgreSQL::fetch_row() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
863
			$tables[] = $row[0];
864
		$this->free_result($request);
0 ignored issues
show
Bug introduced by
It seems like $request defined by $this->query('', ' SE..., 'filter' => $filter)) on line 850 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
865
866
		return $tables;
867
	}
868
869
	/**
870
	 * Backup $table to $backup_table.
871
	 *
872
	 * @param string $table
873
	 * @param string $backup_table
874
	 * @throws Elk_Exception
875
	 */
876
	public function db_backup_table($table, $backup_table)
877
	{
878
		global $db_prefix;
879
880
		$table = str_replace('{db_prefix}', $db_prefix, $table);
881
882
		// Do we need to drop it first?
883
		$db_table = db_table();
884
		$db_table->db_drop_table($backup_table);
885
886
		// @todo Should we create backups of sequences as well?
887
		$this->query('', '
888
			CREATE TABLE {raw:backup_table}
889
			(
890
				LIKE {raw:table}
891
				INCLUDING DEFAULTS
892
			)',
893
			array(
894
				'backup_table' => $backup_table,
895
				'table' => $table,
896
			)
897
		);
898
899
		$this->query('', '
900
			INSERT INTO {raw:backup_table}
901
			SELECT * FROM {raw:table}',
902
			array(
903
				'backup_table' => $backup_table,
904
				'table' => $table,
905
			)
906
		);
907
	}
908
909
	/**
910
	 * Get the server version number.
911
	 *
912
	 * @return string - the version
913
	 */
914
	public function db_server_version()
915
	{
916
		$version = pg_version();
917
918
		return $version['server'];
919
	}
920
921
	/**
922
	 * Get the name (title) of the database system.
923
	 *
924
	 * @return string
925
	 */
926
	public function db_title()
927
	{
928
		return 'PostgreSQL';
929
	}
930
931
	/**
932
	 * Whether the database system is case sensitive.
933
	 *
934
	 * @return boolean
935
	 */
936
	public function db_case_sensitive()
937
	{
938
		return true;
939
	}
940
941
	/**
942
	 * Quotes identifiers for replacement__callback.
943
	 *
944
	 * @param mixed $replacement
945
	 * @return string
946
	 * @throws Elk_Exception
947
	 */
948 View Code Duplication
	protected function _replaceIdentifier($replacement)
949
	{
950
		if (preg_match('~[a-z_][0-9,a-z,A-Z$_]{0,60}~', $replacement) !== 1)
951
		{
952
			$this->error_backtrace('Wrong value type sent to the database. Invalid identifier used. (' . $replacement . ')', '', E_USER_ERROR, __FILE__, __LINE__);
953
		}
954
955
		return '"' . $replacement . '"';
956
	}
957
958
	/**
959
	 * Escape string for the database input
960
	 *
961
	 * @param string $string
962
	 */
963
	public function escape_string($string)
964
	{
965
		return pg_escape_string($string);
966
	}
967
968
	/**
969
	 * Fetch next result as association.
970
	 *
971
	 * @param resource $request
972
	 * @param int|bool $counter = false
973
	 */
974 View Code Duplication
	public function fetch_assoc($request, $counter = false)
975
	{
976
		global $db_row_count;
977
978
		if ($counter !== false)
979
			return pg_fetch_assoc($request, $counter);
980
981
		// Reset the row counter...
982
		if (!isset($db_row_count[(int) $request]))
983
			$db_row_count[(int) $request] = 0;
984
985
		// Return the right row.
986
		return @pg_fetch_assoc($request, $db_row_count[(int) $request]++);
987
	}
988
989
	/**
990
	 * Return server info.
991
	 *
992
	 * @return string
993
	 */
994
	public function db_server_info()
995
	{
996
		// give info on client! we use it in install and upgrade and such things.
997
		$version = pg_version();
998
999
		return $version['client'];
1000
	}
1001
1002
	/**
1003
	 * Return client version.
1004
	 *
1005
	 * @return string - the version
1006
	 */
1007
	public function db_client_version()
1008
	{
1009
		$version = pg_version();
1010
1011
		return $version['client'];
1012
	}
1013
1014
	/**
1015
	 * Dummy function really. Doesn't do anything on PostgreSQL.
1016
	 *
1017
	 * @param string|null $db_name = null
1018
	 * @param resource|null $connection = null
1019
	 *
1020
	 * @return boolean
1021
	 */
1022
	public function select_db($db_name = null, $connection = null)
1023
	{
1024
		return true;
1025
	}
1026
1027
	/**
1028
	 * Returns a reference to the existing instance
1029
	 */
1030
	public static function db()
1031
	{
1032
		return self::$_db;
1033
	}
1034
1035
	/**
1036
	 * Finds out if the connection is still valid.
1037
	 *
1038
	 * @param postgre|null $connection = null
1039
	 */
1040
	public function validConnection($connection = null)
1041
	{
1042
		return is_resource($connection);
1043
	}
1044
}
1045