Completed
Pull Request — development (#3098)
by John
09:23
created

Database_PostgreSQL::db()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 4
cp 0
crap 2
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
	 */
65
	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...
66
	{
67
		// initialize the instance... if not done already!
68
		if (self::$_db === null)
69
			self::$_db = new self();
70
71 View Code Duplication
		if (!empty($db_options['port']))
72
			$db_port = ' port=' . (int) $db_options['port'];
73
		else
74
			$db_port = '';
75
76
		if (!empty($db_options['persist']))
77
			$connection = @pg_pconnect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'');
78
		else
79
			$connection = @pg_connect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'');
80
81
		// Something's wrong, show an error if its fatal (which we assume it is)
82 View Code Duplication
		if (!$connection)
83
		{
84
			if (!empty($db_options['non_fatal']))
85
				return null;
86
			else
87
				Errors::instance()->display_db_error();
88
		}
89
90
		self::$_db->_connection = $connection;
91
92
		return $connection;
93
	}
94
95
	/**
96
	 * Fix the database prefix if necessary.
97
	 * Do nothing on postgreSQL
98
	 *
99
	 * @param string $db_prefix
100
	 * @param string $db_name
101
	 *
102
	 * @return string
103
	 */
104
	public function fix_prefix($db_prefix, $db_name)
105
	{
106
		return $db_prefix;
107
	}
108
109
	/**
110
	 * Do a query.  Takes care of errors too.
111
	 * Special queries may need additional replacements to be appropriate
112
	 * for PostgreSQL.
113
	 *
114
	 * @param string $identifier
115
	 * @param string $db_string
116
	 * @param mixed[] $db_values
117
	 * @param resource|null $connection
118
	 *
119
	 * @return bool|resource|string
120
	 * @throws Elk_Exception
121
	 */
122
	public function query($identifier, $db_string, $db_values = array(), $connection = null)
123
	{
124
		global $db_show_debug, $time_start, $modSettings;
125
126
		// Decide which connection to use.
127
		$connection = $connection === null ? $this->_connection : $connection;
128
129
		// Special queries that need processing.
130
		$replacements = array(
131
			'ban_suggest_error_ips' => array(
132
				'~RLIKE~' => '~',
133
				'~\\.~' => '\.',
134
			),
135
			'ban_suggest_message_ips' => array(
136
				'~RLIKE~' => '~',
137
				'~\\.~' => '\.',
138
			),
139
			'consolidate_spider_stats' => array(
140
				'~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))',
141
			),
142
			'attach_download_increase' => array(
143
				'~LOW_PRIORITY~' => '',
144
			),
145
			'insert_log_search_topics' => array(
146
				'~NOT RLIKE~' => '!~',
147
			),
148
			'insert_log_search_results_no_index' => array(
149
				'~NOT RLIKE~' => '!~',
150
			),
151
			'insert_log_search_results_subject' => array(
152
				'~NOT RLIKE~' => '!~',
153
			),
154
			'pm_conversation_list' => array(
155
				'~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']) : ''),
156
			),
157
			'profile_board_stats' => array(
158
				'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)',
159
			),
160
		);
161
162
		if (isset($replacements[$identifier]))
163
			$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
164
165
		// Limits need to be a little different.
166
		$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string);
167
168
		if (trim($db_string) == '')
169
			return false;
170
171
		// Comments that are allowed in a query are preg_removed.
172
		static $allowed_comments_from = array(
173
			'~\s+~s',
174
			'~/\*!40001 SQL_NO_CACHE \*/~',
175
			'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
176
			'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
177
		);
178
		static $allowed_comments_to = array(
179
			' ',
180
			'',
181
			'',
182
			'',
183
		);
184
185
		// One more query....
186
		$this->_query_count++;
187
		$this->_db_replace_result = null;
188
189 View Code Duplication
		if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
190
			$this->error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
191
192 View Code Duplication
		if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
193
		{
194
			// Store these values for use in the callback function.
195
			$this->_db_callback_values = $db_values;
196
			$this->_db_callback_connection = $connection;
197
198
			// Inject the values passed to this function.
199
			$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', array($this, 'replacement__callback'), $db_string);
200
201
			// No need for them any longer.
202
			$this->_db_callback_values = array();
203
			$this->_db_callback_connection = null;
204
		}
205
206
		// Debugging.
207 View Code Duplication
		if ($db_show_debug === true)
208
		{
209
			$debug = Debug::instance();
210
211
			// Get the file and line number this function was called.
212
			list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
213
214
			if (!empty($_SESSION['debug_redirect']))
215
			{
216
				$debug->merge_db($_SESSION['debug_redirect']);
217
				// @todo this may be off by 1
218
				$this->_query_count += count($_SESSION['debug_redirect']);
219
				$_SESSION['debug_redirect'] = array();
220
			}
221
222
			// Don't overload it.
223
			$st = microtime(true);
224
			$db_cache = array();
225
			$db_cache['q'] = $this->_query_count < 50 ? $db_string : '...';
226
			$db_cache['f'] = $file;
227
			$db_cache['l'] = $line;
228
			$db_cache['s'] = $st - $time_start;
229
		}
230
231
		// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
232
		if (empty($modSettings['disableQueryCheck']))
233
		{
234
			$clean = '';
235
			$old_pos = 0;
236
			$pos = -1;
237 View Code Duplication
			while (true)
238
			{
239
				$pos = strpos($db_string, '\'', $pos + 1);
240
				if ($pos === false)
241
					break;
242
				$clean .= substr($db_string, $old_pos, $pos - $old_pos);
243
244
				while (true)
245
				{
246
					$pos1 = strpos($db_string, '\'', $pos + 1);
247
					$pos2 = strpos($db_string, '\'\'', $pos + 1);
248
249
					if ($pos1 === false)
250
						break;
251
					elseif ($pos2 === false || $pos2 > $pos1)
252
					{
253
						$pos = $pos1;
254
						break;
255
					}
256
257
					$pos = $pos2 + 1;
258
				}
259
260
				$clean .= ' %s ';
261
				$old_pos = $pos + 1;
262
			}
263
264
			$clean .= substr($db_string, $old_pos);
265
			$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
266
267
			// Comments?  We don't use comments in our queries, we leave 'em outside!
268 View Code Duplication
			if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
269
				$fail = true;
270
			// Trying to change passwords, slow us down, or something?
271
			elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
272
				$fail = true;
273
			elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
274
				$fail = true;
275
276
			if (!empty($fail) && class_exists('Errors'))
277
				$this->error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
278
279
			// If we are updating something, better start a transaction so that indexes may be kept consistent
280
			if (!$this->_in_transaction && strpos($clean, 'update') !== false)
281
				$this->db_transaction('begin', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 127 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...
282
		}
283
284
		$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 455
  5. $messages_columns is passed through implode()
    in sources/subs/Post.subs.php on line 495
  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 493
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 649
  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 653
  4. $data is assigned
    in sources/subs/Search/Search.php on line 655
  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 657
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 658
  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 914
  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 64
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 649
  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 653
  4. $data is assigned
    in sources/subs/Search/Search.php on line 655
  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 657
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 658
  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 914
  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 64
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 744
  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 914
  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 64
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  5. Path: Read from $_GET in sources/subs/Search/Search.php on line 968
  1. Read from $_GET
    in sources/subs/Search/Search.php on line 968
  2. Data is passed through htmlspecialchars_decode()
    in vendor/sources/Subs.php on line 555
  3. Data is passed through str_replace()
    in vendor/sources/Subs.php on line 556
  4. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 968
  5. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1178
  6. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1209
  7. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1217
  8. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1228
  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 64
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  6. Path: Read from $_POST, and Search::$_search_params is assigned in sources/subs/Search/Search.php on line 972
  1. Read from $_POST, and Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 972
  2. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1178
  3. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1209
  4. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1217
  5. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1228
  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 64
  8. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 153
  3. Data_Validator::$_data is assigned
    in sources/subs/DataValidator.class.php on line 266
  4. Tainted property Data_Validator::$_data is read
    in sources/subs/DataValidator.class.php on line 303
  5. Data_Validator::validation_data() returns tainted data
    in sources/subs/HttpReq.class.php on line 377
  6. HttpReq::cleanValue() returns tainted data, and HttpReq::$_param is assigned
    in sources/subs/HttpReq.class.php on line 223
  7. Tainted property HttpReq::$_param is read
    in sources/subs/HttpReq.class.php on line 288
  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 239
  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 234
  14. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 575
  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 579
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 585
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 594
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 575
  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 579
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 585
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 594
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 575
  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 579
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 585
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 594
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 575
  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 579
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 585
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 594
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 575
  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 579
  15. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 585
  16. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 594
  17. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166
  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 575
  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 579
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 585
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 594
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 166

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