Completed
Pull Request — patch_1-1-4 (#3191)
by Emanuele
11:20
created

Database_PostgreSQL::num_rows()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 2
cts 2
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file has all the main functions in it that relate to the 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 1.1
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 39
	public function query($identifier, $db_string, $db_values = array(), $connection = null)
124
	{
125 39
		global $db_show_debug, $time_start, $modSettings;
126
127
		// Decide which connection to use.
128 39
		$connection = $connection === null ? $this->_connection : $connection;
129
130
		// Special queries that need processing.
131
		$replacements = array(
132
			'alter_table' => array(
133 39
				'~(.+)~' => '',
134 39
			),
135
			'ban_suggest_error_ips' => array(
136 39
				'~RLIKE~' => '~',
137 39
				'~\\.~' => '\.',
138 39
			),
139
			'ban_suggest_message_ips' => array(
140 39
				'~RLIKE~' => '~',
141 39
				'~\\.~' => '\.',
142 39
			),
143
			'consolidate_spider_stats' => array(
144 39
				'~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))',
145 39
			),
146
			'display_get_post_poster' => array(
147 39
				'~GROUP BY id_msg\s+HAVING~' => 'AND',
148 39
			),
149
			'attach_download_increase' => array(
150 39
				'~LOW_PRIORITY~' => '',
151 39
			),
152
			'boardindex_fetch_boards' => array(
153 39
				'~COALESCE\(lb.id_msg, 0\) >= b.id_msg_updated~' => 'CASE WHEN COALESCE(lb.id_msg, 0) >= b.id_msg_updated THEN 1 ELSE 0 END',
154 39
			),
155
			'get_random_number' => array(
156 39
				'~RAND~' => 'RANDOM',
157 39
			),
158
			'insert_log_search_topics' => array(
159 39
				'~NOT RLIKE~' => '!~',
160 39
			),
161
			'insert_log_search_results_no_index' => array(
162 39
				'~NOT RLIKE~' => '!~',
163 39
			),
164
			'insert_log_search_results_subject' => array(
165 39
				'~NOT RLIKE~' => '!~',
166 39
			),
167
			'pm_conversation_list' => array(
168 39
				'~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']) : ''),
169 39
			),
170
			'top_topic_starters' => array(
171 39
				'~ORDER BY FIND_IN_SET\(id_member,(.+?)\)~' => 'ORDER BY STRPOS(\',\' || $1 || \',\', \',\' || id_member|| \',\')',
172 39
			),
173
			'unread_replies' => array(
174 39
				'~SELECT\\s+DISTINCT\\s+t.id_topic~' => 'SELECT t.id_topic, {raw:sort}',
175 39
			),
176
			'profile_board_stats' => array(
177 39
				'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)',
178 39
			),
179 39
		);
180
181 39
		if (isset($replacements[$identifier]))
182 39
			$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
183
184
		// Limits need to be a little different.
185 39
		$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string);
186
187 39
		if (trim($db_string) == '')
188 39
			return false;
189
190
		// Comments that are allowed in a query are preg_removed.
191
		static $allowed_comments_from = array(
192
			'~\s+~s',
193
			'~/\*!40001 SQL_NO_CACHE \*/~',
194
			'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
195
			'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
196 39
		);
197
		static $allowed_comments_to = array(
198
			' ',
199
			'',
200
			'',
201
			'',
202 39
		);
203
204
		// One more query....
205 39
		$this->_query_count++;
206 39
		$this->_db_replace_result = null;
207
208 39 View Code Duplication
		if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
209 39
			$this->error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
210
211 39 View Code Duplication
		if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
212 39
		{
213
			// Store these values for use in the callback function.
214 39
			$this->_db_callback_values = $db_values;
215 39
			$this->_db_callback_connection = $connection;
216
217
			// Inject the values passed to this function.
218 39
			$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', array($this, 'replacement__callback'), $db_string);
219
220
			// No need for them any longer.
221 39
			$this->_db_callback_values = array();
222 39
			$this->_db_callback_connection = null;
223 39
		}
224
225
		// Debugging.
226 39 View Code Duplication
		if ($db_show_debug === true)
227 39
		{
228
			$debug = Debug::instance();
229
230
			// Get the file and line number this function was called.
231
			list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
232
233
			if (!empty($_SESSION['debug_redirect']))
234
			{
235
				$debug->merge_db($_SESSION['debug_redirect']);
236
				// @todo this may be off by 1
237
				$this->_query_count += count($_SESSION['debug_redirect']);
238
				$_SESSION['debug_redirect'] = array();
239
			}
240
241
			// Don't overload it.
242
			$st = microtime(true);
243
			$db_cache = array();
244
			$db_cache['q'] = $this->_query_count < 50 ? $db_string : '...';
245
			$db_cache['f'] = $file;
246
			$db_cache['l'] = $line;
247
			$db_cache['s'] = $st - $time_start;
248
		}
249
250
		// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
251 39
		if (empty($modSettings['disableQueryCheck']))
252 39
		{
253 39
			$clean = '';
254 39
			$old_pos = 0;
255 39
			$pos = -1;
256 39 View Code Duplication
			while (true)
257
			{
258 39
				$pos = strpos($db_string, '\'', $pos + 1);
259 39
				if ($pos === false)
260 39
					break;
261 37
				$clean .= substr($db_string, $old_pos, $pos - $old_pos);
262
263 37
				while (true)
264
				{
265 37
					$pos1 = strpos($db_string, '\'', $pos + 1);
266 37
					$pos2 = strpos($db_string, '\'\'', $pos + 1);
267
268 37
					if ($pos1 === false)
269 37
						break;
270 37
					elseif ($pos2 === false || $pos2 > $pos1)
271
					{
272 37
						$pos = $pos1;
273 37
						break;
274
					}
275
276 1
					$pos = $pos2 + 1;
277 1
				}
278
279 37
				$clean .= ' %s ';
280 37
				$old_pos = $pos + 1;
281 37
			}
282
283 39
			$clean .= substr($db_string, $old_pos);
284 39
			$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
285
286
			// Comments?  We don't use comments in our queries, we leave 'em outside!
287 39 View Code Duplication
			if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
288 39
				$fail = true;
289
			// Trying to change passwords, slow us down, or something?
290 39
			elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
291
				$fail = true;
292 39
			elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
293
				$fail = true;
294
295 39
			if (!empty($fail) && class_exists('Errors'))
296 39
				$this->error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
297
298
			// If we are updating something, better start a transaction so that indexes may be kept consistent
299 39
			if (!$this->_in_transaction && strpos($clean, 'update') !== false)
300 39
				$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...
301 39
		}
302
303 39
		$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 185
  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 647
  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 651
  4. $data is assigned
    in sources/subs/Search/Search.php on line 653
  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 655
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 656
  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 912
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1678
  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 1701
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 647
  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 651
  4. $data is assigned
    in sources/subs/Search/Search.php on line 653
  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 655
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 656
  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 912
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1678
  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 1701
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 742
  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 912
  4. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1678
  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 1701
  6. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  5. Path: Read from $_GET in sources/subs/Search/Search.php on line 966
  1. Read from $_GET
    in sources/subs/Search/Search.php on line 966
  2. Data is passed through htmlspecialchars_decode()
    in vendor/sources/Subs.php on line 569
  3. Data is passed through str_replace()
    in vendor/sources/Subs.php on line 570
  4. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 966
  5. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1176
  6. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1207
  7. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1215
  8. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1226
  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 1925
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  6. Path: Read from $_POST, and Search::$_search_params is assigned in sources/subs/Search/Search.php on line 970
  1. Read from $_POST, and Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 970
  2. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1176
  3. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1207
  4. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1215
  5. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1226
  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 1925
  7. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  8. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 237
  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 232
  14. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  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 584
  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 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  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 584
  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 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  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 584
  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 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  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 584
  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 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 519
  6. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  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 584
  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 588
  15. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  16. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  17. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  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 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  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 584
  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 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185

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...
304
305
		// @deprecated since 1.1 - use skip_next_error method
306 39
		if (!empty($db_values['db_error_skip']))
307 39
		{
308 11
			$this->_skip_error = true;
309 11
		}
310
311 39
		if ($this->_db_last_result === false && !$this->_skip_error)
312 39
		{
313
			$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...
314
		}
315
316
		// Revert not to skip errors
317 39
		if ($this->_skip_error === true)
318 39
		{
319 11
			$this->_skip_error = false;
320 11
		}
321
322 39
		if ($this->_in_transaction)
323 39
			$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...
324
325
		// Debugging.
326 39 View Code Duplication
		if ($db_show_debug === true)
327 39
		{
328
			$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...
329
			$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...
330
		}
331
332 39
		return $this->_db_last_result;
333
	}
334
335
	/**
336
	 * Affected rows from previous operation.
337
	 *
338
	 * @param resource|null $result
339
	 */
340 9
	public function affected_rows($result = null)
341
	{
342 9
		if ($this->_db_replace_result !== null)
343 9
			return $this->_db_replace_result;
344 9
		elseif ($result === null && !$this->_db_last_result)
345
			return 0;
346
347 9
		return pg_affected_rows($result === null ? $this->_db_last_result : $result);
348
	}
349
350
	/**
351
	 * Last inserted id.
352
	 *
353
	 * @param string $table
354
	 * @param string|null $field = null
355
	 * @param resource|null $connection = null
356
	 * @throws Elk_Exception
357
	 */
358 15
	public function insert_id($table, $field = null, $connection = null)
359
	{
360 15
		global $db_prefix;
361
362 15
		$table = str_replace('{db_prefix}', $db_prefix, $table);
363
364 15
		$connection = $connection === null ? $this->_connection : $connection;
365
366
		// Try get the last ID for the auto increment field.
367 15
		$request = $this->query('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID',
368
			array(
369 15
			),
370
			$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 364 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...
371 15
		);
372
373 15
		if (!$request)
374 15
			return false;
375
376 15
		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...
377 15
		$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...
378
379 15
		return $lastID;
380
	}
381
382
	/**
383
	 * Tracking the current row.
384
	 * Fetch a row from the resultset given as parameter.
385
	 *
386
	 * @param resource $request
387
	 * @param integer|bool $counter = false
388
	 */
389 23 View Code Duplication
	public function fetch_row($request, $counter = false)
390
	{
391 23
		global $db_row_count;
392
393 23
		if ($counter !== false)
394 23
			return pg_fetch_row($request, $counter);
395
396
		// Reset the row counter...
397 23
		if (!isset($db_row_count[(int) $request]))
398 23
			$db_row_count[(int) $request] = 0;
399
400
		// Return the right row.
401 23
		return @pg_fetch_row($request, $db_row_count[(int) $request]++);
402
	}
403
404
	/**
405
	 * Free the resultset.
406
	 *
407
	 * @param resource $result
408
	 */
409 37
	public function free_result($result)
410
	{
411
		// Just delegate to the native function
412 37
		pg_free_result($result);
413 37
	}
414
415
	/**
416
	 * Get the number of rows in the result.
417
	 *
418
	 * @param resource $result
419
	 */
420 19
	public function num_rows($result)
421
	{
422
		// simply delegate to the native function
423 19
		return pg_num_rows($result);
424
	}
425
426
	/**
427
	 * Get the number of fields in the resultset.
428
	 *
429
	 * @param resource $request
430
	 */
431
	public function num_fields($request)
432
	{
433
		return pg_num_fields($request);
434
	}
435
436
	/**
437
	 * Reset the internal result pointer.
438
	 *
439
	 * @param boolean $request
440
	 * @param integer $counter
441
	 */
442
	public function data_seek($request, $counter)
443
	{
444
		global $db_row_count;
445
446
		$db_row_count[(int) $request] = $counter;
447
448
		return true;
449
	}
450
451
	/**
452
	 * Do a transaction.
453
	 *
454
	 * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
455
	 * @param resource|null $connection = null
456
	 */
457 23
	public function db_transaction($type = 'commit', $connection = null)
458
	{
459
		// Decide which connection to use
460 23
		$connection = $connection === null ? $this->_connection : $connection;
461
462 23
		if ($type == 'begin')
463 23
		{
464 23
			$this->_in_transaction = true;
465 23
			return @pg_query($connection, 'BEGIN');
466
		}
467 23
		elseif ($type == 'rollback')
468
			return @pg_query($connection, 'ROLLBACK');
469 23
		elseif ($type == 'commit')
470
		{
471 23
			$this->_in_transaction = false;
472 23
			return @pg_query($connection, 'COMMIT');
473
		}
474
475
		return false;
476
	}
477
478
	/**
479
	 * Return last error string from the database server
480
	 *
481
	 * @param resource|null $connection = null
482
	 */
483
	public function last_error($connection = null)
484
	{
485
		// Decide which connection to use
486
		$connection = $connection === null ? $this->_connection : $connection;
487
488
		if (is_resource($connection))
489
			return pg_last_error($connection);
490
	}
491
492
	/**
493
	 * Database error.
494
	 * Backtrace, log, try to fix.
495
	 *
496
	 * @param string        $db_string
497
	 * @param resource|null $connection = null
498
	 *
499
	 * @throws Elk_Exception
500
	 */
501
	public function error($db_string, $connection = null)
502
	{
503
		global $txt, $context, $modSettings, $db_show_debug;
504
505
		// We'll try recovering the file and line number the original db query was called from.
506
		list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
507
508
		// Decide which connection to use
509
		$connection = $connection === null ? $this->_connection : $connection;
510
511
		// This is the error message...
512
		$query_error = @pg_last_error($connection);
513
514
		// Log the error.
515 View Code Duplication
		if (class_exists('Errors'))
516
		{
517
			Errors::instance()->log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line);
518
		}
519
520
		// Nothing's defined yet... just die with it.
521
		if (empty($context) || empty($txt))
522
			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...
523
524
		// Show an error message, if possible.
525
		$context['error_title'] = $txt['database_error'];
526 View Code Duplication
		if (allowedTo('admin_forum'))
527
			$context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line;
528
		else
529
			$context['error_message'] = $txt['try_again'];
530
531
		// Add database version that we know of, for the admin to know. (and ask for support)
532 View Code Duplication
		if (allowedTo('admin_forum'))
533
			$context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $modSettings['elkVersion']);
534
535 View Code Duplication
		if (allowedTo('admin_forum') && $db_show_debug === true)
536
			$context['error_message'] .= '<br /><br />' . nl2br($db_string);
537
538
		// It's already been logged... don't log it again.
539
		throw new Elk_Exception($context['error_message'], false);
540
	}
541
542
	/**
543
	 * Insert data.
544
	 *
545
	 * @param string $method - options 'replace', 'ignore', 'insert'
546
	 * @param string $table
547
	 * @param mixed[] $columns
548
	 * @param mixed[] $data
549
	 * @param mixed[] $keys
550
	 * @param bool $disable_trans = false
551
	 * @param resource|null $connection = null
552
	 * @throws Elk_Exception
553
	 */
554 24
	public function insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
555
	{
556 24
		global $db_prefix;
557
558 24
		$connection = $connection === null ? $this->_connection : $connection;
559
560
		// With nothing to insert, simply return.
561 24
		if (empty($data))
562 24
			return;
563
564
		// Inserting data as a single row can be done as a single array.
565 24
		if (!is_array($data[array_rand($data)]))
566 24
			$data = array($data);
567
568
		// Replace the prefix holder with the actual prefix.
569 24
		$table = str_replace('{db_prefix}', $db_prefix, $table);
570
571 24
		$priv_trans = false;
572 24
		if ((count($data) > 1 || $method == 'replace') && !$this->_in_transaction && !$disable_trans)
573 24
		{
574 20
			$this->db_transaction('begin', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 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...
575 20
			$priv_trans = true;
576 20
		}
577
578
		// PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead
579 24
		if ($method == 'replace')
580 24
		{
581 16
			$count = 0;
582 16
			$where = '';
583 16
			$db_replace_result = 0;
584 16
			foreach ($columns as $columnName => $type)
585
			{
586
				// Are we restricting the length?
587 16
				if (strpos($type, 'string-') !== false)
588 16
					$actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count);
589
				else
590 5
					$actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count);
591
592
				// A key? That's what we were looking for.
593 16
				if (in_array($columnName, $keys))
594 16
					$where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2);
595 16
				$count++;
596 16
			}
597
598
			// Make it so.
599 16
			if (!empty($where) && !empty($data))
600 16
			{
601 16
				foreach ($data as $k => $entry)
602
				{
603 16
					$this->query('', '
604 16
						DELETE FROM ' . $table .
605 16
						' WHERE ' . $where,
606 16
						$entry, $connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 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...
607 16
					);
608 16
					$db_replace_result += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
609 16
				}
610 16
			}
611 16
		}
612
613 24
		if (!empty($data))
614 24
		{
615
			// Create the mold for a single row insert.
616 24
			$insertData = '(';
617 24 View Code Duplication
			foreach ($columns as $columnName => $type)
618
			{
619
				// Are we restricting the length?
620 24
				if (strpos($type, 'string-') !== false)
621 24
					$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
622
				else
623 20
					$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
624 24
			}
625 24
			$insertData = substr($insertData, 0, -2) . ')';
626
627
			// Create an array consisting of only the columns.
628 24
			$indexed_columns = array_keys($columns);
629
630
			// Here's where the variables are injected to the query.
631 24
			$insertRows = array();
632 24
			foreach ($data as $dataRow)
633 24
				$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 558 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...
634
635 24
			$inserted_results = 0;
636 24
			$skip_error = $method == 'ignore' || $table === $db_prefix . 'log_errors';
637 24
			foreach ($insertRows as $entry)
638
			{
639 24
				$this->_skip_error = $skip_error;
640
641
				// Do the insert.
642 24
				$this->query('', '
643 24
					INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '")
644
					VALUES
645 24
						' . $entry,
646
					array(
647 24
						'security_override' => true,
648 24
					),
649
					$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 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...
650 24
				);
651 24
				$inserted_results += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
652 24
			}
653 24
			if (isset($db_replace_result))
654 24
				$this->_db_replace_result = $db_replace_result + $inserted_results;
655 24
		}
656
657
		if ($priv_trans)
658 24
			$this->db_transaction('commit', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 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...
659 24
	}
660
661
	/**
662
	 * Unescape an escaped string!
663
	 *
664
	 * @param string $string
665
	 */
666
	public function unescape_string($string)
667
	{
668
		return strtr($string, array('\'\'' => '\''));
669
	}
670
671
	/**
672
	 * Returns whether the database system supports ignore.
673
	 *
674
	 * @return false
675
	 */
676
	public function support_ignore()
677
	{
678
		return false;
679
	}
680
681
	/**
682
	 * Gets all the necessary INSERTs for the table named table_name.
683
	 * It goes in 250 row segments.
684
	 *
685
	 * @param string $tableName - the table to create the inserts for.
686
	 * @param bool $new_table
687
	 *
688
	 * @return string the query to insert the data back in, or an empty string if the table was empty.
689
	 * @throws Elk_Exception
690
	 */
691
	public function insert_sql($tableName, $new_table = false)
692
	{
693
		global $db_prefix;
694
695
		static $start = 0, $num_rows, $fields, $limit;
696
697 View Code Duplication
		if ($new_table)
698
		{
699
			$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
700
			$start = 0;
701
		}
702
703
		$data = '';
704
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
705
706
		// This will be handy...
707
		$crlf = "\r\n";
708
709
		$result = $this->query('', '
710
			SELECT *
711
			FROM ' . $tableName . '
712
			LIMIT ' . $start . ', ' . $limit,
713
			array(
714
				'security_override' => true,
715
			)
716
		);
717
718
		// The number of rows, just for record keeping and breaking INSERTs up.
719
		$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 709 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...
720
721
		if ($num_rows == 0)
722
			return '';
723
724
		if ($new_table)
725
		{
726
			$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 709 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...
727
			$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 709 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...
728
		}
729
730
		// Start it off with the basic INSERT INTO.
731
		$insert_msg = 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t";
732
733
		// Loop through each row.
734 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 709 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...
735
		{
736
			// Get the fields in this row...
737
			$field_list = array();
738
739
			foreach ($row as $key => $item)
740
			{
741
				// Try to figure out the type of each field. (NULL, number, or 'string'.)
742
				if (!isset($item))
743
					$field_list[] = 'NULL';
744
				elseif (is_numeric($item) && (int) $item == $item)
745
					$field_list[] = $item;
746
				else
747
					$field_list[] = '\'' . $this->escape_string($item) . '\'';
748
			}
749
750
			// 'Insert' the data.
751
			$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
752
		}
753
		$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 709 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...
754
755
		$data .= $crlf;
756
757
		$start += $limit;
758
759
		return $data;
760
	}
761
762
	/**
763
	 * Dumps the schema (CREATE) for a table.
764
	 *
765
	 * @param string $tableName - the table
766
	 *
767
	 * @return string - the CREATE statement as string
768
	 * @throws Elk_Exception
769
	 */
770
	public function db_table_sql($tableName)
771
	{
772
		global $db_prefix;
773
774
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
775
776
		// This will be needed...
777
		$crlf = "\r\n";
778
779
		// Start the create table...
780
		$schema_create = 'CREATE TABLE ' . $tableName . ' (' . $crlf;
781
		$index_create = '';
782
		$seq_create = '';
783
784
		// Find all the fields.
785
		$result = $this->query('', '
786
			SELECT column_name, column_default, is_nullable, data_type, character_maximum_length
787
			FROM information_schema.columns
788
			WHERE table_name = {string:table}
789
			ORDER BY ordinal_position',
790
			array(
791
				'table' => $tableName,
792
			)
793
		);
794
		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 785 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...
795
		{
796
			if ($row['data_type'] == 'character varying')
797
				$row['data_type'] = 'varchar';
798
			elseif ($row['data_type'] == 'character')
799
				$row['data_type'] = 'char';
800
801
			if ($row['character_maximum_length'])
802
				$row['data_type'] .= '(' . $row['character_maximum_length'] . ')';
803
804
			// Make the CREATE for this column.
805
			$schema_create .= ' "' . $row['column_name'] . '" ' . $row['data_type'] . ($row['is_nullable'] != 'YES' ? ' NOT NULL' : '');
806
807
			// Add a default...?
808
			if (trim($row['column_default']) != '')
809
			{
810
				$schema_create .= ' default ' . $row['column_default'] . '';
811
812
				// Auto increment?
813
				if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0)
814
				{
815
					// Get to find the next variable first!
816
					$count_req = $this->query('', '
817
						SELECT MAX("{raw:column}")
818
						FROM {raw:table}',
819
						array(
820
							'column' => $row['column_name'],
821
							'table' => $tableName,
822
						)
823
					);
824
					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...
825
					$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 816 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...
826
827
					// Get the right bloody start!
828
					$seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf;
829
				}
830
			}
831
832
			$schema_create .= ',' . $crlf;
833
		}
834
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 785 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...
835
836
		// Take off the last comma.
837
		$schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
838
839
		$result = $this->query('', '
840
			SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, pg_get_indexdef(i.indexrelid) AS inddef
841
			FROM pg_class AS c
842
				INNER JOIN pg_index AS i ON (i.indrelid = c.oid)
843
				INNER JOIN pg_class AS c2 ON (c2.oid = i.indexrelid)
844
			WHERE c.relname = {string:table}',
845
			array(
846
				'table' => $tableName,
847
			)
848
		);
849
850
		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 839 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...
851
		{
852
			if ($row['is_primary'])
853
			{
854
				if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0)
855
					continue;
856
857
				$index_create .= $crlf . 'ALTER TABLE ' . $tableName . ' ADD PRIMARY KEY ("' . $matches[1] . '");';
858
			}
859
			else
860
				$index_create .= $crlf . $row['inddef'] . ';';
861
		}
862
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 839 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...
863
864
		// Finish it off!
865
		$schema_create .= $crlf . ');';
866
867
		return $seq_create . $schema_create . $index_create;
868
	}
869
870
	/**
871
	 * {@inheritdoc}
872
	 */
873 1
	public function db_list_tables($db_name_str = false, $filter = false)
874
	{
875 1
		$request = $this->query('', '
876
			SELECT tablename
877
			FROM pg_tables
878 1
			WHERE schemaname = {string:schema_public}' . ($filter === false ? '' : '
879 1
				AND tablename LIKE {string:filter}') . '
880 1
			ORDER BY tablename',
881
			array(
882 1
				'schema_public' => 'public',
883 1
				'filter' => $filter,
884
			)
885 1
		);
886 1
		$tables = array();
887 1
		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 875 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...
888 1
			$tables[] = $row[0];
889 1
		$this->free_result($request);
0 ignored issues
show
Bug introduced by
It seems like $request defined by $this->query('', ' SE..., 'filter' => $filter)) on line 875 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...
890
891 1
		return $tables;
892
	}
893
894
	/**
895
	 * Backup $table to $backup_table.
896
	 *
897
	 * @param string $table
898
	 * @param string $backup_table
899
	 * @throws Elk_Exception
900
	 */
901
	public function db_backup_table($table, $backup_table)
902
	{
903
		global $db_prefix;
904
905
		$table = str_replace('{db_prefix}', $db_prefix, $table);
906
907
		// Do we need to drop it first?
908
		$db_table = db_table();
909
		$db_table->db_drop_table($backup_table);
910
911
		// @todo Should we create backups of sequences as well?
912
		$this->query('', '
913
			CREATE TABLE {raw:backup_table}
914
			(
915
				LIKE {raw:table}
916
				INCLUDING DEFAULTS
917
			)',
918
			array(
919
				'backup_table' => $backup_table,
920
				'table' => $table,
921
			)
922
		);
923
924
		$this->query('', '
925
			INSERT INTO {raw:backup_table}
926
			SELECT * FROM {raw:table}',
927
			array(
928
				'backup_table' => $backup_table,
929
				'table' => $table,
930
			)
931
		);
932
	}
933
934
	/**
935
	 * Get the server version number.
936
	 *
937
	 * @return string - the version
938
	 */
939
	public function db_server_version()
940
	{
941
		$version = pg_version();
942
943
		return $version['server'];
944
	}
945
946
	/**
947
	 * Get the name (title) of the database system.
948
	 *
949
	 * @return string
950
	 */
951
	public function db_title()
952
	{
953
		return 'PostgreSQL';
954
	}
955
956
	/**
957
	 * Whether the database system is case sensitive.
958
	 *
959
	 * @return boolean
960
	 */
961
	public function db_case_sensitive()
962
	{
963
		return true;
964
	}
965
966
	/**
967
	 * Quotes identifiers for replacement__callback.
968
	 *
969
	 * @param mixed $replacement
970
	 * @return string
971
	 * @throws Elk_Exception
972
	 */
973 View Code Duplication
	protected function _replaceIdentifier($replacement)
974
	{
975
		if (preg_match('~[a-z_][0-9,a-z,A-Z$_]{0,60}~', $replacement) !== 1)
976
		{
977
			$this->error_backtrace('Wrong value type sent to the database. Invalid identifier used. (' . $replacement . ')', '', E_USER_ERROR, __FILE__, __LINE__);
978
		}
979
980
		return '"' . $replacement . '"';
981
	}
982
983
	/**
984
	 * Escape string for the database input
985
	 *
986
	 * @param string $string
987
	 */
988 34
	public function escape_string($string)
989
	{
990 34
		return pg_escape_string($string);
991
	}
992
993
	/**
994
	 * Fetch next result as association.
995
	 *
996
	 * @param resource $request
997
	 * @param int|bool $counter = false
998
	 */
999 31 View Code Duplication
	public function fetch_assoc($request, $counter = false)
1000
	{
1001 31
		global $db_row_count;
1002
1003 31
		if ($counter !== false)
1004 31
			return pg_fetch_assoc($request, $counter);
1005
1006
		// Reset the row counter...
1007 31
		if (!isset($db_row_count[(int) $request]))
1008 31
			$db_row_count[(int) $request] = 0;
1009
1010
		// Return the right row.
1011 31
		return @pg_fetch_assoc($request, $db_row_count[(int) $request]++);
1012
	}
1013
1014
	/**
1015
	 * Return server info.
1016
	 *
1017
	 * @return string
1018
	 */
1019
	public function db_server_info()
1020
	{
1021
		// give info on client! we use it in install and upgrade and such things.
1022
		$version = pg_version();
1023
1024
		return $version['client'];
1025
	}
1026
1027
	/**
1028
	 * Return client version.
1029
	 *
1030
	 * @return string - the version
1031
	 */
1032
	public function db_client_version()
1033
	{
1034
		$version = pg_version();
1035
1036
		return $version['client'];
1037
	}
1038
1039
	/**
1040
	 * Dummy function really. Doesn't do anything on PostgreSQL.
1041
	 *
1042
	 * @param string|null $db_name = null
1043
	 * @param resource|null $connection = null
1044
	 *
1045
	 * @return boolean
1046
	 */
1047
	public function select_db($db_name = null, $connection = null)
1048
	{
1049
		return true;
1050
	}
1051
1052
	/**
1053
	 * Returns a reference to the existing instance
1054
	 */
1055 59
	public static function db()
1056
	{
1057 59
		return self::$_db;
1058
	}
1059
1060
	/**
1061
	 * Finds out if the connection is still valid.
1062
	 *
1063
	 * @param postgre|null $connection = null
1064
	 */
1065 39
	public function validConnection($connection = null)
1066
	{
1067 39
		return is_resource($connection);
1068
	}
1069
}
1070