$val) { if (strpos($val, '_TRANS') !== false) { $TABLE_LANG_FIELDS_CACHE[$only_table][$key] = $val; } } if (function_exists('persistent_cache_set')) { persistent_cache_set('TABLE_LANG_FIELDS_CACHE', $TABLE_LANG_FIELDS_CACHE); } return; } $msn_running = (is_on_multi_site_network()) && (get_forum_type() == 'cns') && (isset($GLOBALS['FORUM_DB'])); if (multi_lang_content() || $full) { // We need to know about any kind of translated fields in these cases $like = db_string_equal_to('m_type', 'SHORT_TRANS__COMCODE') . ' OR ' . db_string_equal_to('m_type', 'LONG_TRANS__COMCODE') . ' OR ' . db_string_equal_to('m_type', 'SHORT_TRANS') . ' OR ' . db_string_equal_to('m_type', 'LONG_TRANS') . ' OR ' . db_string_equal_to('m_type', '?SHORT_TRANS__COMCODE') . ' OR ' . db_string_equal_to('m_type', '?LONG_TRANS__COMCODE') . ' OR ' . db_string_equal_to('m_type', '?SHORT_TRANS') . ' OR ' . db_string_equal_to('m_type', '?LONG_TRANS'); } else { // In this case we only really need to know about Comcode fields $like = db_string_equal_to('m_type', 'SHORT_TRANS__COMCODE') . ' OR ' . db_string_equal_to('m_type', 'LONG_TRANS__COMCODE') . ' OR ' . db_string_equal_to('m_type', '?SHORT_TRANS__COMCODE') . ' OR ' . db_string_equal_to('m_type', '?LONG_TRANS__COMCODE'); } $sql = 'SELECT m_name,m_table,m_type FROM ' . get_table_prefix() . 'db_meta WHERE (' . $like . ')'; if ($only_table !== null) { $sql .= ' AND ' . db_string_equal_to('m_table', $only_table); } if (($msn_running) && ($only_table !== null) && (substr($only_table, 0, 2) === 'f_')) { $_table_lang_fields = []; // Optimisation, as it'll get overwritten anyway } else { $_table_lang_fields = $GLOBALS['SITE_DB']->query($sql, null, 0, true); // Suppress errors in case table does not exist yet } if ($_table_lang_fields !== null) { // Load in our data foreach ($_table_lang_fields as $lang_field) { if (!isset($TABLE_LANG_FIELDS_CACHE[$lang_field['m_table']])) { $TABLE_LANG_FIELDS_CACHE[$lang_field['m_table']] = []; } $TABLE_LANG_FIELDS_CACHE[$lang_field['m_table']][$lang_field['m_name']] = $lang_field['m_type']; } // Get correct forum DB metadata from central site if (($msn_running) && (($only_table === null) || (substr($only_table, 0, 2) === 'f_'))) { if ($only_table !== null) { unset($TABLE_LANG_FIELDS_CACHE[$only_table]); } unset($TABLE_LANG_FIELDS_CACHE['f_member_custom_fields']); // This may vary between sites in undefined ways $sql .= ' AND m_table LIKE \'' . db_encode_like('f_%') . '\''; $_table_lang_fields_forum = $GLOBALS['FORUM_DB']->query($sql, null, 0, true); if ($_table_lang_fields_forum !== null) { // Load in our data foreach ($_table_lang_fields_forum as $lang_field) { if (!isset($TABLE_LANG_FIELDS_CACHE[$lang_field['m_table']])) { $TABLE_LANG_FIELDS_CACHE[$lang_field['m_table']] = []; } $TABLE_LANG_FIELDS_CACHE[$lang_field['m_table']][$lang_field['m_name']] = $lang_field['m_type']; } } } } if (function_exists('persistent_cache_set')) { persistent_cache_set('TABLE_LANG_FIELDS_CACHE', $TABLE_LANG_FIELDS_CACHE); } } /** * Find lang fields to load within a query. Usually used when JOINs are involved in a query and hence the software cannot automatically determine what the fields will be. * * @param string $table Table name * @param ?string $alias Table alias (null: none) * @return array Map of fields */ function find_lang_fields(string $table, ?string $alias = null) : array { global $TABLE_LANG_FIELDS_CACHE; $lang_fields = isset($TABLE_LANG_FIELDS_CACHE[$table]) ? $TABLE_LANG_FIELDS_CACHE[$table] : []; if ($alias !== null) { foreach ($lang_fields as $lang_field => $lang_field_type) { unset($lang_fields[$lang_field]); $lang_fields[$alias . '.' . $lang_field] = $lang_field_type; } } return $lang_fields; } /** * Get the ID of the first row in an auto-increment table (used whenever we need to reference the first). * * @return integer First ID used */ function db_get_first_id() : int { return $GLOBALS['DB_DRIVER']->get_first_id(); } /** * Encode an SQL statement fragment for a conditional to see if two strings are equal. * * @param ID_TEXT $attribute The attribute * @param string $compare The comparison * @return string The SQL */ function db_string_equal_to(string $attribute, string $compare) : string { return $GLOBALS['DB_DRIVER']->string_equal_to($attribute, $compare); } /** * Encode an SQL statement fragment for a conditional to see if two strings are not equal. * * @param ID_TEXT $attribute The attribute * @param string $compare The comparison * @return string The SQL */ function db_string_not_equal_to(string $attribute, string $compare) : string { return $GLOBALS['DB_DRIVER']->string_not_equal_to($attribute, $compare); } /** * Encode a LIKE string comparison fragment for the database system. The pattern is a mixture of characters and _ and % wildcard symbols. * Regular string escaping is also applied so that you can put the output directly between quotes. * * @param string $pattern The pattern * @return string The encoded pattern */ function db_encode_like(string $pattern) : string { $ret = $GLOBALS['DB_DRIVER']->encode_like($pattern); if (($GLOBALS['DEV_MODE']) || (!function_exists('has_solemnly_declared')) || (!has_solemnly_declared(I_UNDERSTAND_SQL_INJECTION))) { require_code('database_security_filter'); $GLOBALS['DB_ESCAPE_STRING_LIST'][$ret] = true; $GLOBALS['DB_ESCAPE_STRING_LIST'][trim($ret, ' %')] = true; } return $ret; } /** * Encode a WHERE query part for performing a comparison on a BINARY type field. * * @param ID_TEXT $column The column name being compared * @param ID_TEXT $operator The operation to be performed * @set < > = != <= >= * @param string $value The value to compare, in binary string format * @return string The encoded WHERE part */ function db_encode_binary_compare(string $column, string $operator, string $value) : string { return $GLOBALS['DB_DRIVER']->encode_binary_compare($column, $operator, $value); } /** * Escape a string so it may be inserted into a query. If SQL statements are being built up and passed using db_query then it is essential that this is used for security reasons. Otherwise, the abstraction layer deals with the situation. * * @param string $string The string * @return string The escaped string */ function db_escape_string(string $string) : string { if (function_exists('has_solemnly_declared')) { if (($GLOBALS['DEV_MODE']) || (!has_solemnly_declared(I_UNDERSTAND_SQL_INJECTION))) { require_code('database_security_filter'); $GLOBALS['DB_ESCAPE_STRING_LIST'][trim($GLOBALS['DB_DRIVER']->escape_string($string), ' %')] = true; } } return $GLOBALS['DB_DRIVER']->escape_string($string); } /** * Call a database function that may be different on different database drivers. * We are using MySQL syntax as a de facto standard. SQL does not standardise this stuff well. * Basic arithmetic and inequality operators are assumed supported without needing a function. * * @param string $function Function name * @set CONCAT REPLACE SUBSTR LENGTH RAND COALESCE LEAST GREATEST MOD GROUP_CONCAT * @param array $args List of string arguments, assumed already quoted/escaped correctly for the particular database * @return string SQL fragment */ function db_function(string $function, array $args = []) : string { return $GLOBALS['DB_DRIVER']->db_function($function, $args); } /** * Extract certain fields, including any Tempcode details for them, from a DB table row array. * * @param array $row DB table row * @param array $fields List of fields to copy through * @param array $remap Remapping of fields, if we need to do some kind of substitution as well (usually this will be because the row came from a join and we had to rename fields) * @return array Map of fields */ function db_map_restrict(array $row, array $fields, array $remap = []) : array { $out = []; foreach ($fields as $field) { $out[$field] = $row[(array_key_exists($field, $remap) && array_key_exists($remap[$field], $row)) ? $remap[$field] : $field]; if (isset($row[$field . '__text_parsed'])) { $out[$field . '__text_parsed'] = $row[$field . '__text_parsed']; } if (array_key_exists($field . '__source_user', $row)) { $out[$field . '__source_user'] = $row[$field . '__source_user']; } } return $out; } /** * Create an SQL cast. * Note if using this in a SELECT clause you should use 'AS', as you can't predict what the CAST is going to set the field name as. * * @param string $field The field identifier * @param string $type The type wanted * @set CHAR INT FLOAT * @return string The database type */ function db_cast(string $field, string $type) : string { return $GLOBALS['DB_DRIVER']->cast($field, $type); } /** * Find whether we are on a multi-site-network. * We will check to see that the specification for the forum database and site database differ. * Also see: get_db_for, is_forum_db. * * @return boolean Whether we are */ function is_on_multi_site_network() : bool { static $cache = null; if ($cache !== null) { return $cache; } if (get_forum_type() == 'none') { $cache = false; return false; } $cache = ( (get_db_site_host() != get_db_forums_host()) || (get_db_site() != get_db_forums()) || (get_db_site_user() != get_db_forums_user()) || (isset($GLOBALS['FORUM_DRIVER'])) && ($GLOBALS['FORUM_DRIVER']->get_drivered_table_prefix() != get_table_prefix()) ); return $cache; } /** * Find the correct database connection for a particular table. i.e. site connection or forum connection. * This only works with site/Conversr tables, not third-party forums. * If modifying this function, search for other cases in the code for 'f_welcome_emails', as similar logic is used elsewhere. * Also see: is_forum_db, is_on_multi_site_network. * * @param ID_TEXT $table Database table * @param boolean $force_site_db Whether to force use of the site connection * @return object Database connection */ function get_db_for(string $table, bool $force_site_db = false) : object { $table = preg_replace('# .*$#', '', $table); // Strip alias $use_forum_db = ( (substr($table, 0, 2) == 'f_') && ($table != 'f_welcome_emails') && (!$force_site_db) && (get_forum_type() == 'cns') && (isset($GLOBALS['FORUM_DB'])) ); $db = $GLOBALS[$use_forum_db ? 'FORUM_DB' : 'SITE_DB']; return $db; } /** * Get the type of database installed, such as MySQL, or Oracle. * * @return string The database type */ function get_db_type() : string { global $SITE_INFO; if (isset($SITE_INFO['db_type'])) { $ret = $SITE_INFO['db_type']; } else { $ret = 'mysqli'; } if ($ret === 'mysql') { // LEGACY: mysql extension was removed in PHP 7; force mysqli because our minimum is PHP 7.2. $ret = 'mysqli'; } return $ret; } /** * Find whether the software was installed to use persistent database connections or not. * * @return boolean Whether to use persistent database connections */ function get_use_persistent_database() : bool { global $SITE_INFO; if (isset($SITE_INFO['use_persistent'])) { // LEGACY return !empty($SITE_INFO['use_persistent']); } return !empty($SITE_INFO['use_persistent_database']); } /** * Get the table prefixes used for all software tables, commonly used when you are installing the software in the same database as your forums. * The default table prefix is 'cms_'. Note that anything that might write to an arbitrary db, must ask that db for its table prefix (if it needs it of course... the db abstracts away most needs for it). * * @return string The table prefix */ function get_table_prefix() : string { global $SITE_INFO; if (!isset($SITE_INFO['table_prefix'])) { return 'cms' . strval(cms_version()) . '_'; } return $SITE_INFO['table_prefix']; } /** * Get the host of the database ('localhost', for example). * * @return string The database host */ function get_db_site_host() : string { global $SITE_INFO; return (!empty($SITE_INFO['db_site_host'])) ? $SITE_INFO['db_site_host'] : 'localhost'; } /** * Get the name of the database. * * @return string The database site */ function get_db_site() : string { global $SITE_INFO; if (empty($SITE_INFO['db_site'])) { return basename(get_file_base()); } return $SITE_INFO['db_site'] . (($GLOBALS['CURRENT_SHARE_USER'] === null) ? '' : ('_' . $GLOBALS['CURRENT_SHARE_USER'])); } /** * Get the database username. * * @return string The database username */ function get_db_site_user() : string { global $SITE_INFO; if ($GLOBALS['CURRENT_SHARE_USER'] !== null) { return substr(md5($SITE_INFO['db_site_user'] . '_' . $GLOBALS['CURRENT_SHARE_USER']), 0, 16); } return (!empty($SITE_INFO['db_site_user'])) ? $SITE_INFO['db_site_user'] : 'root'; } /** * Get the database password. * * @return string The database password */ function get_db_site_password() : string { global $SITE_INFO; return array_key_exists('db_site_password', $SITE_INFO) ? $SITE_INFO['db_site_password'] : ''; } /** * Get the host of the forum database ('localhost', for example). * * @return string The database host */ function get_db_forums_host() : string { global $SITE_INFO; return (!empty($SITE_INFO['db_forums_host'])) ? $SITE_INFO['db_forums_host'] : (!empty($SITE_INFO['db_site_host']) ? $SITE_INFO['db_site_host'] : 'localhost'); } /** * Get the name of the forum database. * * @return string The forum database site */ function get_db_forums() : string { global $SITE_INFO; if (empty($SITE_INFO['db_forums'])) { return get_db_site(); } return $SITE_INFO['db_forums'] . (($GLOBALS['CURRENT_SHARE_USER'] === null) ? '' : ('_' . $GLOBALS['CURRENT_SHARE_USER'])); } /** * Get the forum database username. * * @return string The forum database username */ function get_db_forums_user() : string { global $SITE_INFO; if (empty($SITE_INFO['db_forums_user'])) { return get_db_site_user(); } if ($GLOBALS['CURRENT_SHARE_USER'] !== null) { return substr(md5($SITE_INFO['db_forums_user'] . '_' . $GLOBALS['CURRENT_SHARE_USER']), 0, 16); } return $SITE_INFO['db_forums_user']; } /** * Get the forum database password. * * @return string The forum database password */ function get_db_forums_password() : string { global $SITE_INFO; if (!array_key_exists('db_forums_password', $SITE_INFO)) { return get_db_site_password(); } return $SITE_INFO['db_forums_password']; } /** * Find out if we are using the InnoDB storage engine. * * @return boolean Whether we are using InnoDB */ function db_is_innodb() : bool { if (strpos(get_db_type(), 'mysql') === false) { return false; } // Used by the installer since the values table does not exist yet global $USE_INNODB; if ($USE_INNODB) { return true; } if (function_exists('get_value') && (get_value('innodb') == '1')) { return true; } return false; }