Yoshi2889 /
SMF2.1
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * This file has all the main functions in it that relate to the database. |
||
| 5 | * |
||
| 6 | * Simple Machines Forum (SMF) |
||
| 7 | * |
||
| 8 | * @package SMF |
||
| 9 | * @author Simple Machines http://www.simplemachines.org |
||
| 10 | * @copyright 2017 Simple Machines and individual contributors |
||
| 11 | * @license http://www.simplemachines.org/about/smf/license.php BSD |
||
| 12 | * |
||
| 13 | * @version 2.1 Beta 4 |
||
| 14 | */ |
||
| 15 | |||
| 16 | if (!defined('SMF')) |
||
| 17 | die('No direct access...'); |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Maps the implementations in this file (smf_db_function_name) |
||
| 21 | * to the $smcFunc['db_function_name'] variable. |
||
| 22 | * @see Subs-Db-mysql.php#smf_db_initiate |
||
| 23 | * |
||
| 24 | * @param string $db_server The database server |
||
| 25 | * @param string $db_name The name of the database |
||
| 26 | * @param string $db_user The database username |
||
| 27 | * @param string $db_passwd The database password |
||
| 28 | * @param string $db_prefix The table prefix |
||
| 29 | * @param array $db_options An array of database options |
||
| 30 | * @return null|resource Returns null on failure if $db_options['non_fatal'] is true or a PostgreSQL connection resource handle if the connection was successful. |
||
| 31 | */ |
||
| 32 | function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, &$db_prefix, $db_options = array()) |
||
| 33 | { |
||
| 34 | global $smcFunc; |
||
| 35 | |||
| 36 | // Map some database specific functions, only do this once. |
||
| 37 | View Code Duplication | if (!isset($smcFunc['db_fetch_assoc'])) |
|
| 38 | $smcFunc += array( |
||
| 39 | 'db_query' => 'smf_db_query', |
||
| 40 | 'db_quote' => 'smf_db_quote', |
||
| 41 | 'db_insert' => 'smf_db_insert', |
||
| 42 | 'db_insert_id' => 'smf_db_insert_id', |
||
| 43 | 'db_fetch_assoc' => 'smf_db_fetch_assoc', |
||
| 44 | 'db_fetch_row' => 'smf_db_fetch_row', |
||
| 45 | 'db_free_result' => 'pg_free_result', |
||
| 46 | 'db_num_rows' => 'pg_num_rows', |
||
| 47 | 'db_data_seek' => 'smf_db_data_seek', |
||
| 48 | 'db_num_fields' => 'pg_num_fields', |
||
| 49 | 'db_escape_string' => 'pg_escape_string', |
||
| 50 | 'db_unescape_string' => 'smf_db_unescape_string', |
||
| 51 | 'db_server_info' => 'smf_db_version', |
||
| 52 | 'db_affected_rows' => 'smf_db_affected_rows', |
||
| 53 | 'db_transaction' => 'smf_db_transaction', |
||
| 54 | 'db_error' => 'pg_last_error', |
||
| 55 | 'db_select_db' => 'smf_db_select_db', |
||
| 56 | 'db_title' => 'PostgreSQL', |
||
| 57 | 'db_sybase' => true, |
||
| 58 | 'db_case_sensitive' => true, |
||
| 59 | 'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string', |
||
| 60 | 'db_is_resource' => 'is_resource', |
||
| 61 | 'db_mb4' => true, |
||
| 62 | 'db_ping' => 'pg_ping', |
||
| 63 | 'db_fetch_all' => 'smf_db_fetch_all', |
||
| 64 | ); |
||
| 65 | |||
| 66 | if (!empty($db_options['persist'])) |
||
| 67 | $connection = @pg_pconnect('host=' . $db_server . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'' . (empty($db_options['port']) ? '' : ' port=\'' . $db_options['port'] . '\'')); |
||
| 68 | else |
||
| 69 | $connection = @pg_connect('host=' . $db_server . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'' . (empty($db_options['port']) ? '' : ' port=\'' . $db_options['port'] . '\'')); |
||
| 70 | |||
| 71 | // Something's wrong, show an error if its fatal (which we assume it is) |
||
| 72 | if (!$connection) |
||
| 73 | { |
||
| 74 | if (!empty($db_options['non_fatal'])) |
||
| 75 | { |
||
| 76 | return null; |
||
| 77 | } |
||
| 78 | else |
||
| 79 | { |
||
| 80 | display_db_error(); |
||
| 81 | } |
||
| 82 | } |
||
| 83 | |||
| 84 | return $connection; |
||
| 85 | } |
||
| 86 | |||
| 87 | /** |
||
| 88 | * Extend the database functionality. It calls the respective file's init |
||
| 89 | * to add the implementations in that file to $smcFunc array. |
||
| 90 | * |
||
| 91 | * @param string $type Indicates which additional file to load. ('extra', 'packages') |
||
| 92 | */ |
||
| 93 | function db_extend($type = 'extra') |
||
| 94 | { |
||
| 95 | global $sourcedir, $db_type; |
||
| 96 | |||
| 97 | require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-' . $db_type . '.php'); |
||
| 98 | $initFunc = 'db_' . $type . '_init'; |
||
| 99 | $initFunc(); |
||
| 100 | } |
||
| 101 | |||
| 102 | /** |
||
| 103 | * Fix the database prefix if necessary. |
||
| 104 | * Does nothing on PostgreSQL |
||
| 105 | * |
||
| 106 | * @param string $db_prefix The database prefix |
||
| 107 | * @param string $db_name The database name |
||
| 108 | */ |
||
| 109 | function db_fix_prefix(&$db_prefix, $db_name) |
||
| 110 | { |
||
| 111 | return; |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Callback for preg_replace_callback on the query. |
||
| 116 | * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board', etc), with |
||
| 117 | * their current values from $user_info. |
||
| 118 | * In addition, it performs checks and sanitization on the values sent to the database. |
||
| 119 | * |
||
| 120 | * @param array $matches The matches from preg_replace_callback |
||
| 121 | * @return string The appropriate string depending on $matches[1] |
||
| 122 | */ |
||
| 123 | function smf_db_replacement__callback($matches) |
||
| 124 | { |
||
| 125 | global $db_callback, $user_info, $db_prefix, $smcFunc; |
||
| 126 | |||
| 127 | list ($values, $connection) = $db_callback; |
||
| 128 | |||
| 129 | if ($matches[1] === 'db_prefix') |
||
| 130 | return $db_prefix; |
||
| 131 | |||
| 132 | View Code Duplication | if (isset($user_info[$matches[1]]) && strpos($matches[1], 'query_') !== false) |
|
| 133 | return $user_info[$matches[1]]; |
||
| 134 | |||
| 135 | if ($matches[1] === 'empty') |
||
| 136 | return '\'\''; |
||
| 137 | |||
| 138 | if (!isset($matches[2])) |
||
| 139 | smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 140 | |||
| 141 | if ($matches[1] === 'literal') |
||
| 142 | return '\'' . pg_escape_string($matches[2]) . '\''; |
||
| 143 | |||
| 144 | if (!isset($values[$matches[2]])) |
||
| 145 | smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . (isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($matches[2]) : htmlspecialchars($matches[2])), '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 146 | |||
| 147 | $replacement = $values[$matches[2]]; |
||
| 148 | |||
| 149 | switch ($matches[1]) |
||
| 150 | { |
||
| 151 | case 'int': |
||
| 152 | if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement) |
||
| 153 | smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 154 | return (string) (int) $replacement; |
||
| 155 | break; |
||
| 156 | |||
| 157 | case 'string': |
||
| 158 | case 'text': |
||
| 159 | return sprintf('\'%1$s\'', pg_escape_string($replacement)); |
||
| 160 | break; |
||
| 161 | |||
| 162 | case 'array_int': |
||
| 163 | if (is_array($replacement)) |
||
| 164 | { |
||
| 165 | if (empty($replacement)) |
||
| 166 | smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 167 | |||
| 168 | foreach ($replacement as $key => $value) |
||
| 169 | { |
||
| 170 | if (!is_numeric($value) || (string) $value !== (string) (int) $value) |
||
| 171 | smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 172 | |||
| 173 | $replacement[$key] = (string) (int) $value; |
||
| 174 | } |
||
| 175 | |||
| 176 | return implode(', ', $replacement); |
||
| 177 | } |
||
| 178 | else |
||
| 179 | smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 180 | |||
| 181 | break; |
||
| 182 | |||
| 183 | case 'array_string': |
||
| 184 | if (is_array($replacement)) |
||
| 185 | { |
||
| 186 | if (empty($replacement)) |
||
| 187 | smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 188 | |||
| 189 | foreach ($replacement as $key => $value) |
||
| 190 | $replacement[$key] = sprintf('\'%1$s\'', pg_escape_string($value)); |
||
| 191 | |||
| 192 | return implode(', ', $replacement); |
||
| 193 | } |
||
| 194 | else |
||
| 195 | smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 196 | break; |
||
| 197 | |||
| 198 | View Code Duplication | case 'date': |
|
| 199 | if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1) |
||
| 200 | return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]).'::date'; |
||
| 201 | else |
||
| 202 | smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 203 | break; |
||
| 204 | |||
| 205 | View Code Duplication | case 'time': |
|
| 206 | if (preg_match('~^([0-1]?\d|2[0-3]):([0-5]\d):([0-5]\d)$~', $replacement, $time_matches) === 1) |
||
| 207 | return sprintf('\'%02d:%02d:%02d\'', $time_matches[1], $time_matches[2], $time_matches[3]).'::time'; |
||
| 208 | else |
||
| 209 | smf_db_error_backtrace('Wrong value type sent to the database. Time expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 210 | break; |
||
| 211 | |||
| 212 | View Code Duplication | case 'datetime': |
|
| 213 | if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d) ([0-1]?\d|2[0-3]):([0-5]\d):([0-5]\d)$~', $replacement, $datetime_matches) === 1) |
||
| 214 | return 'to_timestamp('. |
||
| 215 | sprintf('\'%04d-%02d-%02d %02d:%02d:%02d\'', $datetime_matches[1], $datetime_matches[2], $datetime_matches[3], $datetime_matches[4], $datetime_matches[5] ,$datetime_matches[6]). |
||
| 216 | ',\'YYYY-MM-DD HH24:MI:SS\')'; |
||
| 217 | else |
||
| 218 | smf_db_error_backtrace('Wrong value type sent to the database. Datetime expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 219 | break; |
||
| 220 | |||
| 221 | case 'float': |
||
| 222 | if (!is_numeric($replacement)) |
||
| 223 | smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 224 | return (string) (float) $replacement; |
||
| 225 | break; |
||
| 226 | |||
| 227 | case 'identifier': |
||
| 228 | return '"' . strtr($replacement, array('`' => '', '.' => '"."')) . '"'; |
||
| 229 | break; |
||
| 230 | |||
| 231 | case 'raw': |
||
| 232 | return $replacement; |
||
| 233 | break; |
||
| 234 | |||
| 235 | View Code Duplication | case 'inet': |
|
| 236 | if ($replacement == 'null' || $replacement == '') |
||
| 237 | return 'null'; |
||
| 238 | if (inet_pton($replacement) === false) |
||
| 239 | smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 240 | return sprintf('\'%1$s\'::inet', pg_escape_string($replacement)); |
||
| 241 | |||
| 242 | case 'array_inet': |
||
| 243 | if (is_array($replacement)) |
||
| 244 | { |
||
| 245 | if (empty($replacement)) |
||
| 246 | smf_db_error_backtrace('Database error, given array of IPv4 or IPv6 values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 247 | |||
| 248 | foreach ($replacement as $key => $value) |
||
| 249 | { |
||
| 250 | if ($replacement == 'null' || $replacement == '') |
||
| 251 | $replacement[$key] = 'null'; |
||
| 252 | if (!isValidIP($value)) |
||
| 253 | smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 254 | $replacement[$key] = sprintf('\'%1$s\'::inet', pg_escape_string($value)); |
||
| 255 | } |
||
| 256 | |||
| 257 | return implode(', ', $replacement); |
||
| 258 | } |
||
| 259 | else |
||
| 260 | smf_db_error_backtrace('Wrong value type sent to the database. Array of IPv4 or IPv6 expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 261 | break; |
||
| 262 | |||
| 263 | default: |
||
| 264 | smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__); |
||
| 265 | break; |
||
| 266 | } |
||
| 267 | } |
||
| 268 | |||
| 269 | /** |
||
| 270 | * Just like the db_query, escape and quote a string, but not executing the query. |
||
| 271 | * |
||
| 272 | * @param string $db_string The database string |
||
| 273 | * @param array $db_values An array of values to be injected into the string |
||
| 274 | * @param resource $connection = null The connection to use (null to use $db_connection) |
||
| 275 | * @return string The string with the values inserted |
||
| 276 | */ |
||
| 277 | View Code Duplication | function smf_db_quote($db_string, $db_values, $connection = null) |
|
| 278 | { |
||
| 279 | global $db_callback, $db_connection; |
||
| 280 | |||
| 281 | // Only bother if there's something to replace. |
||
| 282 | if (strpos($db_string, '{') !== false) |
||
| 283 | { |
||
| 284 | // This is needed by the callback function. |
||
| 285 | $db_callback = array($db_values, $connection === null ? $db_connection : $connection); |
||
| 286 | |||
| 287 | // Do the quoting and escaping |
||
| 288 | $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); |
||
| 289 | |||
| 290 | // Clear this global variable. |
||
| 291 | $db_callback = array(); |
||
| 292 | } |
||
| 293 | |||
| 294 | return $db_string; |
||
| 295 | } |
||
| 296 | |||
| 297 | /** |
||
| 298 | * Do a query. Takes care of errors too. |
||
| 299 | * Special queries may need additional replacements to be appropriate |
||
| 300 | * for PostgreSQL. |
||
| 301 | * |
||
| 302 | * @param string $identifier An identifier. Only used in Postgres when we need to do things differently... |
||
| 303 | * @param string $db_string The database string |
||
| 304 | * @param array $db_values = array() The values to be inserted into the string |
||
| 305 | * @param resource $connection = null The connection to use (null to use $db_connection) |
||
| 306 | * @return resource|bool Returns a MySQL result resource (for SELECT queries), true (for UPDATE queries) or false if the query failed |
||
| 307 | */ |
||
| 308 | function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null) |
||
| 309 | { |
||
| 310 | global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start; |
||
| 311 | global $db_callback, $db_last_result, $db_replace_result, $modSettings; |
||
| 312 | |||
| 313 | // Decide which connection to use. |
||
| 314 | $connection = $connection === null ? $db_connection : $connection; |
||
| 315 | |||
| 316 | // Special queries that need processing. |
||
| 317 | $replacements = array( |
||
| 318 | 'consolidate_spider_stats' => array( |
||
| 319 | '~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))', |
||
| 320 | ), |
||
| 321 | 'attach_download_increase' => array( |
||
| 322 | '~LOW_PRIORITY~' => '', |
||
| 323 | ), |
||
| 324 | 'get_random_number' => array( |
||
| 325 | '~RAND~' => 'RANDOM', |
||
| 326 | ), |
||
| 327 | 'insert_log_search_topics' => array( |
||
| 328 | '~NOT RLIKE~' => '!~', |
||
| 329 | ), |
||
| 330 | 'insert_log_search_results_no_index' => array( |
||
| 331 | '~NOT RLIKE~' => '!~', |
||
| 332 | ), |
||
| 333 | 'insert_log_search_results_subject' => array( |
||
| 334 | '~NOT RLIKE~' => '!~', |
||
| 335 | ), |
||
| 336 | 'pm_conversation_list' => array( |
||
| 337 | '~ORDER\\s+BY\\s+\\{raw:sort\\}~' => 'ORDER BY ' . (isset($db_values['sort']) ? ($db_values['sort'] === 'pm.id_pm' ? 'MAX(pm.id_pm)' : $db_values['sort']) : ''), |
||
| 338 | ), |
||
| 339 | 'profile_board_stats' => array( |
||
| 340 | '~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)', |
||
| 341 | ), |
||
| 342 | ); |
||
| 343 | |||
| 344 | // Special optimizer Hints |
||
| 345 | $query_opt = array( |
||
| 346 | 'load_board_info' => array( |
||
| 347 | 'join_collapse_limit' => 1, |
||
| 348 | ), |
||
| 349 | 'calendar_get_events' => array( |
||
| 350 | 'enable_seqscan' => 'off', |
||
| 351 | ), |
||
| 352 | ); |
||
| 353 | |||
| 354 | View Code Duplication | if (isset($replacements[$identifier])) |
|
| 355 | $db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string); |
||
| 356 | |||
| 357 | // Limits need to be a little different. |
||
| 358 | $db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string); |
||
| 359 | |||
| 360 | if (trim($db_string) == '') |
||
| 361 | return false; |
||
| 362 | |||
| 363 | // Comments that are allowed in a query are preg_removed. |
||
| 364 | static $allowed_comments_from = array( |
||
| 365 | '~\s+~s', |
||
| 366 | '~/\*!40001 SQL_NO_CACHE \*/~', |
||
| 367 | '~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~', |
||
| 368 | '~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~', |
||
| 369 | ); |
||
| 370 | static $allowed_comments_to = array( |
||
| 371 | ' ', |
||
| 372 | '', |
||
| 373 | '', |
||
| 374 | '', |
||
| 375 | ); |
||
| 376 | |||
| 377 | // One more query.... |
||
| 378 | $db_count = !isset($db_count) ? 1 : $db_count + 1; |
||
| 379 | $db_replace_result = 0; |
||
| 380 | |||
| 381 | View Code Duplication | if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override'])) |
|
| 382 | smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__); |
||
| 383 | |||
| 384 | View Code Duplication | if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false)) |
|
| 385 | { |
||
| 386 | // Pass some values to the global space for use in the callback function. |
||
| 387 | $db_callback = array($db_values, $connection); |
||
| 388 | |||
| 389 | // Inject the values passed to this function. |
||
| 390 | $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); |
||
| 391 | |||
| 392 | // This shouldn't be residing in global space any longer. |
||
| 393 | $db_callback = array(); |
||
| 394 | } |
||
| 395 | |||
| 396 | // Debugging. |
||
| 397 | View Code Duplication | if (isset($db_show_debug) && $db_show_debug === true) |
|
| 398 | { |
||
| 399 | // Get the file and line number this function was called. |
||
| 400 | list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); |
||
| 401 | |||
| 402 | // Initialize $db_cache if not already initialized. |
||
| 403 | if (!isset($db_cache)) |
||
| 404 | $db_cache = array(); |
||
| 405 | |||
| 406 | if (!empty($_SESSION['debug_redirect'])) |
||
| 407 | { |
||
| 408 | $db_cache = array_merge($_SESSION['debug_redirect'], $db_cache); |
||
| 409 | $db_count = count($db_cache) + 1; |
||
| 410 | $_SESSION['debug_redirect'] = array(); |
||
| 411 | } |
||
| 412 | |||
| 413 | $st = microtime(); |
||
| 414 | // Don't overload it. |
||
| 415 | $db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...'; |
||
| 416 | $db_cache[$db_count]['f'] = $file; |
||
| 417 | $db_cache[$db_count]['l'] = $line; |
||
| 418 | $db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start)); |
||
| 419 | } |
||
| 420 | |||
| 421 | // First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over. |
||
| 422 | View Code Duplication | if (empty($modSettings['disableQueryCheck'])) |
|
| 423 | { |
||
| 424 | $clean = ''; |
||
| 425 | $old_pos = 0; |
||
| 426 | $pos = -1; |
||
| 427 | while (true) |
||
| 428 | { |
||
| 429 | $pos = strpos($db_string, '\'', $pos + 1); |
||
| 430 | if ($pos === false) |
||
| 431 | break; |
||
| 432 | $clean .= substr($db_string, $old_pos, $pos - $old_pos); |
||
| 433 | |||
| 434 | while (true) |
||
| 435 | { |
||
| 436 | $pos1 = strpos($db_string, '\'', $pos + 1); |
||
| 437 | $pos2 = strpos($db_string, '\\', $pos + 1); |
||
| 438 | if ($pos1 === false) |
||
| 439 | break; |
||
| 440 | elseif ($pos2 === false || $pos2 > $pos1) |
||
| 441 | { |
||
| 442 | $pos = $pos1; |
||
| 443 | break; |
||
| 444 | } |
||
| 445 | |||
| 446 | $pos = $pos2 + 1; |
||
| 447 | } |
||
| 448 | $clean .= ' %s '; |
||
| 449 | |||
| 450 | $old_pos = $pos + 1; |
||
| 451 | } |
||
| 452 | $clean .= substr($db_string, $old_pos); |
||
| 453 | $clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean))); |
||
| 454 | |||
| 455 | // Comments? We don't use comments in our queries, we leave 'em outside! |
||
| 456 | if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false) |
||
| 457 | $fail = true; |
||
| 458 | // Trying to change passwords, slow us down, or something? |
||
| 459 | elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0) |
||
| 460 | $fail = true; |
||
| 461 | elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0) |
||
| 462 | $fail = true; |
||
| 463 | |||
| 464 | if (!empty($fail) && function_exists('log_error')) |
||
| 465 | smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__); |
||
| 466 | } |
||
| 467 | |||
| 468 | // Set optimize stuff |
||
| 469 | if (isset($query_opt[$identifier])) |
||
| 470 | { |
||
| 471 | $query_hints = $query_opt[$identifier]; |
||
| 472 | $query_hints_set = ''; |
||
| 473 | if (isset($query_hints['join_collapse_limit'])) |
||
| 474 | { |
||
| 475 | $query_hints_set .= 'SET LOCAL join_collapse_limit = ' . $query_hints['join_collapse_limit'] . ';'; |
||
| 476 | } |
||
| 477 | if (isset($query_hints['enable_seqscan'])) |
||
| 478 | { |
||
| 479 | $query_hints_set .= 'SET LOCAL enable_seqscan = ' . $query_hints['enable_seqscan'] . ';'; |
||
| 480 | } |
||
| 481 | |||
| 482 | $db_string = $query_hints_set . $db_string; |
||
| 483 | |||
| 484 | if (isset($db_show_debug) && $db_show_debug === true && $db_cache[$db_count]['q'] != '...') |
||
| 485 | $db_cache[$db_count]['q'] = "\t\t" . $db_string; |
||
| 486 | } |
||
| 487 | |||
| 488 | $db_last_result = @pg_query($connection, $db_string); |
||
| 489 | |||
| 490 | View Code Duplication | if ($db_last_result === false && empty($db_values['db_error_skip'])) |
|
| 491 | $db_last_result = smf_db_error($db_string, $connection); |
||
| 492 | |||
| 493 | // Debugging. |
||
| 494 | View Code Duplication | if (isset($db_show_debug) && $db_show_debug === true) |
|
| 495 | $db_cache[$db_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st)); |
||
|
0 ignored issues
–
show
|
|||
| 496 | |||
| 497 | return $db_last_result; |
||
| 498 | } |
||
| 499 | |||
| 500 | /** |
||
| 501 | * affected_rows |
||
| 502 | * @param resource $connection |
||
| 503 | */ |
||
| 504 | function smf_db_affected_rows($result = null) |
||
| 505 | { |
||
| 506 | global $db_last_result, $db_replace_result; |
||
| 507 | |||
| 508 | if ($db_replace_result) |
||
| 509 | return $db_replace_result; |
||
| 510 | elseif ($result === null && !$db_last_result) |
||
| 511 | return 0; |
||
| 512 | |||
| 513 | return pg_affected_rows($result === null ? $db_last_result : $result); |
||
| 514 | } |
||
| 515 | |||
| 516 | /** |
||
| 517 | * Gets the ID of the most recently inserted row. |
||
| 518 | * |
||
| 519 | * @param string $table The table (only used for Postgres) |
||
| 520 | * @param string $field = null The specific field (not used here) |
||
| 521 | * @param resource $connection = null The connection (if null, $db_connection is used) |
||
| 522 | * @return int The ID of the most recently inserted row |
||
| 523 | */ |
||
| 524 | function smf_db_insert_id($table, $field = null, $connection = null) |
||
| 525 | { |
||
| 526 | global $smcFunc, $db_prefix; |
||
| 527 | |||
| 528 | $table = str_replace('{db_prefix}', $db_prefix, $table); |
||
| 529 | |||
| 530 | // Try get the last ID for the auto increment field. |
||
| 531 | $request = $smcFunc['db_query']('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID', |
||
| 532 | array( |
||
| 533 | ) |
||
| 534 | ); |
||
| 535 | if (!$request) |
||
| 536 | return false; |
||
| 537 | list ($lastID) = $smcFunc['db_fetch_row']($request); |
||
| 538 | $smcFunc['db_free_result']($request); |
||
| 539 | |||
| 540 | return $lastID; |
||
| 541 | } |
||
| 542 | |||
| 543 | /** |
||
| 544 | * Do a transaction. |
||
| 545 | * |
||
| 546 | * @param string $type The step to perform (i.e. 'begin', 'commit', 'rollback') |
||
| 547 | * @param resource $connection The connection to use (if null, $db_connection is used) |
||
| 548 | * @return bool True if successful, false otherwise |
||
| 549 | */ |
||
| 550 | View Code Duplication | function smf_db_transaction($type = 'commit', $connection = null) |
|
| 551 | { |
||
| 552 | global $db_connection; |
||
| 553 | |||
| 554 | // Decide which connection to use |
||
| 555 | $connection = $connection === null ? $db_connection : $connection; |
||
| 556 | |||
| 557 | if ($type == 'begin') |
||
| 558 | return @pg_query($connection, 'BEGIN'); |
||
| 559 | elseif ($type == 'rollback') |
||
| 560 | return @pg_query($connection, 'ROLLBACK'); |
||
| 561 | elseif ($type == 'commit') |
||
| 562 | return @pg_query($connection, 'COMMIT'); |
||
| 563 | |||
| 564 | return false; |
||
| 565 | } |
||
| 566 | |||
| 567 | /** |
||
| 568 | * Database error! |
||
| 569 | * Backtrace, log, try to fix. |
||
| 570 | * |
||
| 571 | * @param string $db_string The DB string |
||
| 572 | * @param resource $connection The connection to use (if null, $db_connection is used) |
||
| 573 | */ |
||
| 574 | function smf_db_error($db_string, $connection = null) |
||
| 575 | { |
||
| 576 | global $txt, $context, $modSettings; |
||
| 577 | global $db_connection; |
||
| 578 | global $db_show_debug; |
||
| 579 | |||
| 580 | // We'll try recovering the file and line number the original db query was called from. |
||
| 581 | list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); |
||
| 582 | |||
| 583 | // Decide which connection to use |
||
| 584 | $connection = $connection === null ? $db_connection : $connection; |
||
| 585 | |||
| 586 | // This is the error message... |
||
| 587 | $query_error = @pg_last_error($connection); |
||
| 588 | |||
| 589 | // Log the error. |
||
| 590 | if (function_exists('log_error')) |
||
| 591 | log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line); |
||
| 592 | |||
| 593 | // Nothing's defined yet... just die with it. |
||
| 594 | if (empty($context) || empty($txt)) |
||
| 595 | die($query_error); |
||
| 596 | |||
| 597 | // Show an error message, if possible. |
||
| 598 | $context['error_title'] = $txt['database_error']; |
||
| 599 | if (allowedTo('admin_forum')) |
||
| 600 | $context['error_message'] = nl2br($query_error) . '<br>' . $txt['file'] . ': ' . $file . '<br>' . $txt['line'] . ': ' . $line; |
||
| 601 | else |
||
| 602 | $context['error_message'] = $txt['try_again']; |
||
| 603 | |||
| 604 | if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) |
||
| 605 | { |
||
| 606 | $context['error_message'] .= '<br><br>' . nl2br($db_string); |
||
| 607 | } |
||
| 608 | |||
| 609 | // It's already been logged... don't log it again. |
||
| 610 | fatal_error($context['error_message'], false); |
||
| 611 | } |
||
| 612 | |||
| 613 | /** |
||
| 614 | * A PostgreSQL specific function for tracking the current row... |
||
| 615 | * |
||
| 616 | * @param resource $request A PostgreSQL result resource |
||
| 617 | * @param int $counter The row number in the result to fetch (false to fetch the next one) |
||
| 618 | * @return array The contents of the row that was fetched |
||
| 619 | */ |
||
| 620 | View Code Duplication | function smf_db_fetch_row($request, $counter = false) |
|
| 621 | { |
||
| 622 | global $db_row_count; |
||
| 623 | |||
| 624 | if ($counter !== false) |
||
| 625 | return pg_fetch_row($request, $counter); |
||
| 626 | |||
| 627 | // Reset the row counter... |
||
| 628 | if (!isset($db_row_count[(int) $request])) |
||
| 629 | $db_row_count[(int) $request] = 0; |
||
| 630 | |||
| 631 | // Return the right row. |
||
| 632 | return @pg_fetch_row($request, $db_row_count[(int) $request]++); |
||
| 633 | } |
||
| 634 | |||
| 635 | /** |
||
| 636 | * Get an associative array |
||
| 637 | * |
||
| 638 | * @param resource $request A PostgreSQL result resource |
||
| 639 | * @param int $counter The row to get. If false, returns the next row. |
||
| 640 | * @return array An associative array of row contents |
||
| 641 | */ |
||
| 642 | View Code Duplication | function smf_db_fetch_assoc($request, $counter = false) |
|
| 643 | { |
||
| 644 | global $db_row_count; |
||
| 645 | |||
| 646 | if ($counter !== false) |
||
| 647 | return pg_fetch_assoc($request, $counter); |
||
| 648 | |||
| 649 | // Reset the row counter... |
||
| 650 | if (!isset($db_row_count[(int) $request])) |
||
| 651 | $db_row_count[(int) $request] = 0; |
||
| 652 | |||
| 653 | // Return the right row. |
||
| 654 | return @pg_fetch_assoc($request, $db_row_count[(int) $request]++); |
||
| 655 | } |
||
| 656 | |||
| 657 | /** |
||
| 658 | * Reset the pointer... |
||
| 659 | * |
||
| 660 | * @param resource $request A PostgreSQL result resource |
||
| 661 | * @param int $counter The counter |
||
| 662 | * @return bool Always returns true |
||
| 663 | */ |
||
| 664 | function smf_db_data_seek($request, $counter) |
||
| 665 | { |
||
| 666 | global $db_row_count; |
||
| 667 | |||
| 668 | $db_row_count[(int) $request] = $counter; |
||
| 669 | |||
| 670 | return true; |
||
| 671 | } |
||
| 672 | |||
| 673 | /** |
||
| 674 | * Unescape an escaped string! |
||
| 675 | * |
||
| 676 | * @param string $string The string to unescape |
||
| 677 | * @return string The unescaped string |
||
| 678 | */ |
||
| 679 | function smf_db_unescape_string($string) |
||
| 680 | { |
||
| 681 | return strtr($string, array('\'\'' => '\'')); |
||
| 682 | } |
||
| 683 | |||
| 684 | /** |
||
| 685 | * Inserts data into a table |
||
| 686 | * |
||
| 687 | * @param string $method The insert method - can be 'replace', 'ignore' or 'insert' |
||
| 688 | * @param string $table The table we're inserting the data into |
||
| 689 | * @param array $columns An array of the columns we're inserting the data into. Should contain 'column' => 'datatype' pairs |
||
| 690 | * @param array $data The data to insert |
||
| 691 | * @param array $keys The keys for the table |
||
| 692 | * @param int returnmode 0 = nothing(default), 1 = last row id, 2 = all rows id as array; every mode runs only with method != 'ignore' |
||
| 693 | * @param resource $connection The connection to use (if null, $db_connection is used) |
||
| 694 | * @return mixed value of the first key, behavior based on returnmode. null if no data. |
||
| 695 | */ |
||
| 696 | function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $returnmode = 0, $connection = null) |
||
| 697 | { |
||
| 698 | global $smcFunc, $db_connection, $db_prefix; |
||
| 699 | |||
| 700 | $connection = $connection === null ? $db_connection : $connection; |
||
| 701 | |||
| 702 | $replace = ''; |
||
| 703 | |||
| 704 | if (empty($data)) |
||
| 705 | return; |
||
| 706 | |||
| 707 | if (!is_array($data[array_rand($data)])) |
||
| 708 | $data = array($data); |
||
| 709 | |||
| 710 | // Replace the prefix holder with the actual prefix. |
||
| 711 | $table = str_replace('{db_prefix}', $db_prefix, $table); |
||
| 712 | |||
| 713 | // PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead |
||
| 714 | if ($method == 'replace') |
||
| 715 | { |
||
| 716 | $key_str = ''; |
||
| 717 | $col_str = ''; |
||
| 718 | static $pg_version; |
||
| 719 | static $replace_support; |
||
| 720 | |||
| 721 | if (empty($pg_version)) |
||
| 722 | { |
||
| 723 | db_extend(); |
||
| 724 | //pg 9.5 got replace support |
||
| 725 | $pg_version = $smcFunc['db_get_version'](); |
||
| 726 | // if we got a Beta Version |
||
| 727 | View Code Duplication | if (stripos($pg_version, 'beta') !== false) |
|
| 728 | $pg_version = substr($pg_version, 0, stripos($pg_version, 'beta')) . '.0'; |
||
| 729 | // or RC |
||
| 730 | View Code Duplication | if (stripos($pg_version, 'rc') !== false) |
|
| 731 | $pg_version = substr($pg_version, 0, stripos($pg_version, 'rc')) . '.0'; |
||
| 732 | |||
| 733 | $replace_support = (version_compare($pg_version, '9.5.0', '>=') ? true : false); |
||
| 734 | } |
||
| 735 | |||
| 736 | $count = 0; |
||
| 737 | $where = ''; |
||
| 738 | $count_pk = 0; |
||
| 739 | |||
| 740 | If ($replace_support) |
||
| 741 | { |
||
| 742 | foreach ($columns as $columnName => $type) |
||
| 743 | { |
||
| 744 | //check pk fiel |
||
| 745 | IF (in_array($columnName, $keys)) |
||
| 746 | { |
||
| 747 | $key_str .= ($count_pk > 0 ? ',' : ''); |
||
| 748 | $key_str .= $columnName; |
||
| 749 | $count_pk++; |
||
| 750 | } |
||
| 751 | else //normal field |
||
| 752 | { |
||
| 753 | $col_str .= ($count > 0 ? ',' : ''); |
||
| 754 | $col_str .= $columnName . ' = EXCLUDED.' . $columnName; |
||
| 755 | $count++; |
||
| 756 | } |
||
| 757 | } |
||
| 758 | $replace = ' ON CONFLICT (' . $key_str . ') DO UPDATE SET ' . $col_str; |
||
| 759 | } |
||
| 760 | else |
||
| 761 | { |
||
| 762 | foreach ($columns as $columnName => $type) |
||
| 763 | { |
||
| 764 | // Are we restricting the length? |
||
| 765 | if (strpos($type, 'string-') !== false) |
||
| 766 | $actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count); |
||
| 767 | else |
||
| 768 | $actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count); |
||
| 769 | |||
| 770 | // A key? That's what we were looking for. |
||
| 771 | if (in_array($columnName, $keys)) |
||
| 772 | $where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2); |
||
| 773 | $count++; |
||
| 774 | } |
||
| 775 | |||
| 776 | // Make it so. |
||
| 777 | if (!empty($where) && !empty($data)) |
||
| 778 | { |
||
| 779 | foreach ($data as $k => $entry) |
||
| 780 | { |
||
| 781 | $smcFunc['db_query']('', ' |
||
| 782 | DELETE FROM ' . $table . |
||
| 783 | ' WHERE ' . $where, |
||
| 784 | $entry, $connection |
||
| 785 | ); |
||
| 786 | } |
||
| 787 | } |
||
| 788 | } |
||
| 789 | } |
||
| 790 | |||
| 791 | $returning = ''; |
||
| 792 | $with_returning = false; |
||
| 793 | // lets build the returning string, mysql allow only in normal mode |
||
| 794 | View Code Duplication | if (!empty($keys) && (count($keys) > 0) && $returnmode > 0) |
|
| 795 | { |
||
| 796 | // we only take the first key |
||
| 797 | $returning = ' RETURNING '.$keys[0]; |
||
| 798 | $with_returning = true; |
||
| 799 | } |
||
| 800 | |||
| 801 | if (!empty($data)) |
||
| 802 | { |
||
| 803 | // Create the mold for a single row insert. |
||
| 804 | $insertData = '('; |
||
| 805 | foreach ($columns as $columnName => $type) |
||
| 806 | { |
||
| 807 | // Are we restricting the length? |
||
| 808 | if (strpos($type, 'string-') !== false) |
||
| 809 | $insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName); |
||
| 810 | else |
||
| 811 | $insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName); |
||
| 812 | } |
||
| 813 | $insertData = substr($insertData, 0, -2) . ')'; |
||
| 814 | |||
| 815 | // Create an array consisting of only the columns. |
||
| 816 | $indexed_columns = array_keys($columns); |
||
| 817 | |||
| 818 | // Here's where the variables are injected to the query. |
||
| 819 | $insertRows = array(); |
||
| 820 | foreach ($data as $dataRow) |
||
| 821 | $insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection); |
||
| 822 | |||
| 823 | // Do the insert. |
||
| 824 | $request = $smcFunc['db_query']('', ' |
||
| 825 | INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '") |
||
| 826 | VALUES |
||
| 827 | ' . implode(', |
||
| 828 | ', $insertRows).$replace.$returning, |
||
| 829 | array( |
||
| 830 | 'security_override' => true, |
||
| 831 | 'db_error_skip' => $method == 'ignore' || $table === $db_prefix . 'log_errors', |
||
| 832 | ), |
||
| 833 | $connection |
||
| 834 | ); |
||
| 835 | |||
| 836 | if ($with_returning && $request !== false) |
||
| 837 | { |
||
| 838 | if ($returnmode === 2) |
||
| 839 | $return_var = array(); |
||
| 840 | |||
| 841 | while(($row = $smcFunc['db_fetch_row']($request)) && $with_returning) |
||
| 842 | { |
||
| 843 | if (is_numeric($row[0])) // try to emulate mysql limitation |
||
| 844 | { |
||
| 845 | if ($returnmode === 1) |
||
| 846 | $return_var = $row[0]; |
||
| 847 | elseif ($returnmode === 2) |
||
| 848 | $return_var[] = $row[0]; |
||
|
0 ignored issues
–
show
The variable
$return_var does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
Loading history...
|
|||
| 849 | } |
||
| 850 | else |
||
| 851 | { |
||
| 852 | $with_returning = false; |
||
| 853 | trigger_error('trying to returning ID Field which is not a Int field', E_USER_ERROR); |
||
| 854 | } |
||
| 855 | } |
||
| 856 | } |
||
| 857 | } |
||
| 858 | |||
| 859 | if ($with_returning && !empty($return_var)) |
||
| 860 | return $return_var; |
||
| 861 | } |
||
| 862 | |||
| 863 | /** |
||
| 864 | * Dummy function really. Doesn't do anything on PostgreSQL. |
||
| 865 | * |
||
| 866 | * @param string $db_name The database name |
||
| 867 | * @param resource $db_connection The database connection |
||
| 868 | * @return true Always returns true |
||
| 869 | */ |
||
| 870 | function smf_db_select_db($db_name, $db_connection) |
||
| 871 | { |
||
| 872 | return true; |
||
| 873 | } |
||
| 874 | |||
| 875 | /** |
||
| 876 | * Get the current version. |
||
| 877 | * @return string The client version |
||
| 878 | */ |
||
| 879 | function smf_db_version() |
||
| 880 | { |
||
| 881 | $version = pg_version(); |
||
| 882 | |||
| 883 | return $version['client']; |
||
| 884 | } |
||
| 885 | |||
| 886 | /** |
||
| 887 | * This function tries to work out additional error information from a back trace. |
||
| 888 | * |
||
| 889 | * @param string $error_message The error message |
||
| 890 | * @param string $log_message The message to log |
||
| 891 | * @param string|bool $error_type What type of error this is |
||
| 892 | * @param string $file The file the error occurred in |
||
| 893 | * @param int $line What line of $file the code which generated the error is on |
||
| 894 | * @return void|array Returns an array with the file and line if $error_type is 'return' |
||
| 895 | */ |
||
| 896 | View Code Duplication | function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null) |
|
| 897 | { |
||
| 898 | if (empty($log_message)) |
||
| 899 | $log_message = $error_message; |
||
| 900 | |||
| 901 | foreach (debug_backtrace() as $step) |
||
| 902 | { |
||
| 903 | // Found it? |
||
| 904 | if (strpos($step['function'], 'query') === false && !in_array(substr($step['function'], 0, 7), array('smf_db_', 'preg_re', 'db_erro', 'call_us')) && strpos($step['function'], '__') !== 0) |
||
| 905 | { |
||
| 906 | $log_message .= '<br>Function: ' . $step['function']; |
||
| 907 | break; |
||
| 908 | } |
||
| 909 | |||
| 910 | if (isset($step['line'])) |
||
| 911 | { |
||
| 912 | $file = $step['file']; |
||
| 913 | $line = $step['line']; |
||
| 914 | } |
||
| 915 | } |
||
| 916 | |||
| 917 | // A special case - we want the file and line numbers for debugging. |
||
| 918 | if ($error_type == 'return') |
||
| 919 | return array($file, $line); |
||
| 920 | |||
| 921 | // Is always a critical error. |
||
| 922 | if (function_exists('log_error')) |
||
| 923 | log_error($log_message, 'critical', $file, $line); |
||
| 924 | |||
| 925 | if (function_exists('fatal_error')) |
||
| 926 | { |
||
| 927 | fatal_error($error_message, $error_type); |
||
|
0 ignored issues
–
show
It seems like
$error_type defined by parameter $error_type on line 896 can also be of type boolean; however, fatal_error() does only seem to accept string, maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. Loading history...
|
|||
| 928 | |||
| 929 | // Cannot continue... |
||
| 930 | exit; |
||
| 931 | } |
||
| 932 | elseif ($error_type) |
||
| 933 | trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type); |
||
| 934 | else |
||
| 935 | trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : '')); |
||
| 936 | } |
||
| 937 | |||
| 938 | /** |
||
| 939 | * Escape the LIKE wildcards so that they match the character and not the wildcard. |
||
| 940 | * |
||
| 941 | * @param string $string The string to escape |
||
| 942 | * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards. |
||
| 943 | * @return string The escaped string |
||
| 944 | */ |
||
| 945 | View Code Duplication | function smf_db_escape_wildcard_string($string, $translate_human_wildcards = false) |
|
| 946 | { |
||
| 947 | $replacements = array( |
||
| 948 | '%' => '\%', |
||
| 949 | '_' => '\_', |
||
| 950 | '\\' => '\\\\', |
||
| 951 | ); |
||
| 952 | |||
| 953 | if ($translate_human_wildcards) |
||
| 954 | $replacements += array( |
||
| 955 | '*' => '%', |
||
| 956 | ); |
||
| 957 | |||
| 958 | return strtr($string, $replacements); |
||
| 959 | } |
||
| 960 | |||
| 961 | /** |
||
| 962 | * Fetches all rows from a result as an array |
||
| 963 | * |
||
| 964 | * @param resource $request A PostgreSQL result resource |
||
| 965 | * @return array An array that contains all rows (records) in the result resource |
||
| 966 | */ |
||
| 967 | function smf_db_fetch_all($request) |
||
| 968 | { |
||
| 969 | // Return the right row. |
||
| 970 | return @pg_fetch_all($request); |
||
| 971 | } |
||
| 972 | |||
| 973 | ?> |
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:
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
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: