connection = @mysqli_connect($server, $user, $password, $name); // Give up if we have a connection error. if (mysqli_connect_error()) { header('HTTP/1.1 503 Service Temporarily Unavailable'); echo '
Our software could not connect to the database. We apologise for any inconvenience and ask you to check back later.
'; exit; } $this->query('SET NAMES {string:utf8mb4}', ['utf8mb4' => 'utf8mb4']); } public function getQueryCount() { return $this->query_count; } public function getLoggedQueries() { return $this->logged_queries; } /** * Fetches a row from a given recordset, using field names as keys. */ public function fetch_assoc($resource) { return mysqli_fetch_assoc($resource); } /** * Fetches a row from a given recordset, encapsulating into an object. */ public function fetch_object($resource, $class) { return mysqli_fetch_object($resource, $class); } /** * Fetches a row from a given recordset, using numeric keys. */ public function fetch_row($resource) { return mysqli_fetch_row($resource); } /** * Destroys a given recordset. */ public function free_result($resource) { return mysqli_free_result($resource); } public function data_seek($result, $row_num) { return mysqli_data_seek($result, $row_num); } /** * Returns the amount of rows in a given recordset. */ public function num_rows($resource) { return mysqli_num_rows($resource); } /** * Returns the amount of fields in a given recordset. */ public function num_fields($resource) { return mysqli_num_fields($resource); } /** * Escapes a string. */ public function escape_string($string) { return mysqli_real_escape_string($this->connection, $string); } /** * Unescapes a string. */ public function unescape_string($string) { return stripslashes($string); } /** * Returns the last MySQL error. */ public function error() { return mysqli_error($this->connection); } public function server_info() { return mysqli_get_server_info($this->connection); } /** * Selects a database on a given connection. */ public function select_db($database) { return mysqli_select_db($database, $this->connection); } /** * Returns the amount of rows affected by the previous query. */ public function affected_rows() { return mysqli_affected_rows($this->connection); } /** * Returns the id of the row created by a previous query. */ public function insert_id() { return mysqli_insert_id($this->connection); } /** * Do a MySQL transaction. */ public function transaction($operation = 'commit') { switch ($operation) { case 'begin': case 'rollback': case 'commit': return @mysqli_query($this->connection, strtoupper($operation)); default: return false; } } /** * Function used as a callback for the preg_match function that parses variables into database queries. */ private function replacement_callback($matches) { list ($values, $connection) = $this->db_callback; if (!isset($matches[2])) trigger_error('Invalid value inserted or no type specified.', E_USER_ERROR); if (!isset($values[$matches[2]])) trigger_error('The database value you\'re trying to insert does not exist: ' . htmlspecialchars($matches[2]), E_USER_ERROR); $replacement = $values[$matches[2]]; switch ($matches[1]) { case 'int': if ((!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement) && $replacement !== 'NULL') trigger_error('Wrong value type sent to the database for field: ' . $matches[2] . '. Integer expected.', E_USER_ERROR); return $replacement !== 'NULL' ? (string) (int) $replacement : 'NULL'; break; case 'string': case 'text': return $replacement !== 'NULL' ? sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $replacement)) : 'NULL'; break; case 'array_int': if (is_array($replacement)) { if (empty($replacement)) trigger_error('Database error, given array of integer values is empty.', E_USER_ERROR); foreach ($replacement as $key => $value) { if (!is_numeric($value) || (string) $value !== (string) (int) $value) trigger_error('Wrong value type sent to the database for field: ' . $matches[2] . '. Array of integers expected.', E_USER_ERROR); $replacement[$key] = (string) (int) $value; } return implode(', ', $replacement); } else trigger_error('Wrong value type sent to the database for field: ' . $matches[2] . '. Array of integers expected.', E_USER_ERROR); break; case 'array_string': if (is_array($replacement)) { if (empty($replacement)) trigger_error('Database error, given array of string values is empty.', E_USER_ERROR); foreach ($replacement as $key => $value) $replacement[$key] = sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $value)); return implode(', ', $replacement); } else trigger_error('Wrong value type sent to the database for field: ' . $matches[2] . '. Array of strings expected.', E_USER_ERROR); break; case 'date': if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1) return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]); elseif ($replacement === 'NULL') return 'NULL'; else trigger_error('Wrong value type sent to the database for field: ' . $matches[2] . '. Date expected.', E_USER_ERROR); break; case 'datetime': if (is_a($replacement, 'DateTime')) return $replacement->format('\'Y-m-d H:i:s\''); elseif (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d) (\d{2}):(\d{2}):(\d{2})$~', $replacement, $date_matches) === 1) return sprintf('\'%04d-%02d-%02d %02d:%02d:%02d\'', $date_matches[1], $date_matches[2], $date_matches[3], $date_matches[4], $date_matches[5], $date_matches[6]); elseif ($replacement === 'NULL') return 'NULL'; else trigger_error('Wrong value type sent to the database for field: ' . $matches[2] . '. DateTime expected.', E_USER_ERROR); break; case 'float': if (!is_numeric($replacement) && $replacement !== 'NULL') trigger_error('Wrong value type sent to the database for field: ' . $matches[2] . '. Floating point number expected.', E_USER_ERROR); return $replacement !== 'NULL' ? (string) (float) $replacement : 'NULL'; break; case 'identifier': // Backticks inside identifiers are supported as of MySQL 4.1. We don't need them here. return '`' . strtr($replacement, ['`' => '', '.' => '']) . '`'; break; case 'raw': return $replacement; break; case 'bool': case 'boolean': // In mysql this is a synonym for tinyint(1) return (bool)$replacement ? 1 : 0; break; default: trigger_error('Undefined type ' . $matches[1] . ' used in the database query', E_USER_ERROR); break; } } /** * Escapes and quotes a string using values passed, and executes the query. */ public function query($db_string, $db_values = []) { // One more query.... $this->query_count ++; // Overriding security? This is evil! $security_override = $db_values === 'security_override' || !empty($db_values['security_override']); // Please, just use new style queries. if (strpos($db_string, '\'') !== false && !$security_override) trigger_error('Hack attempt!', 'Illegal character (\') used in query.', E_USER_ERROR); if (!$security_override && !empty($db_values)) { // Set some values for use in the callback function. $this->db_callback = [$db_values, $this->connection]; // Insert the values passed to this function. $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', [&$this, 'replacement_callback'], $db_string); // Save some memory. $this->db_callback = []; } if (defined("DB_LOG_QUERIES") && DB_LOG_QUERIES) $this->logged_queries[] = $db_string; try { $return = @mysqli_query($this->connection, $db_string, empty($this->unbuffered) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); } catch (Exception $e) { $clean_sql = implode("\n", array_map('trim', explode("\n", $db_string))); trigger_error($this->error() . '