+ * @param string $str The error to display
+ * @return false|void False if the showing of errors is disabled.
+ */
+ public function print_error( $str = '' ) {
+ global $EZSQL_ERROR;
+
+ if ( !$str ) {
+ if ( $this->use_mysqli ) {
+ $str = mysqli_error( $this->dbh );
+ } else {
+ $str = mysql_error( $this->dbh );
+ }
+ }
+ $EZSQL_ERROR[] = array( 'query' => $this->last_query, 'error_str' => $str );
+
+ if ( $this->suppress_errors )
+ return false;
+
+ wp_load_translations_early();
+
+ if ( $caller = $this->get_caller() )
+ $error_str = sprintf( __( 'WordPress database error %1$s for query %2$s made by %3$s' ), $str, $this->last_query, $caller );
+ else
+ $error_str = sprintf( __( 'WordPress database error %1$s for query %2$s' ), $str, $this->last_query );
+
+ error_log( $error_str );
+
+ // Are we showing errors?
+ if ( ! $this->show_errors )
+ return false;
+
+ // If there is an error then take note of it
+ if ( is_multisite() ) {
+ $msg = sprintf(
+ "%s [%s]\n%s\n",
+ __( 'WordPress database error:' ),
+ $str,
+ $this->last_query
+ );
+
+ if ( defined( 'ERRORLOGFILE' ) ) {
+ error_log( $msg, 3, ERRORLOGFILE );
+ }
+ if ( defined( 'DIEONDBERROR' ) ) {
+ wp_die( $msg );
+ }
+ } else {
+ $str = htmlspecialchars( $str, ENT_QUOTES );
+ $query = htmlspecialchars( $this->last_query, ENT_QUOTES );
+
+ printf(
+ '<div id="error"><p class="wpdberror"><strong>%s</strong> [%s]<br /><code>%s</code></p></div>',
+ __( 'WordPress database error:' ),
+ $str,
+ $query
+ );
+ }
+ }
+
+ /**
+ * Enables showing of database errors.
+ *
+ * This function should be used only to enable showing of errors.
+ * wpdb::hide_errors() should be used instead for hiding of errors. However,
+ * this function can be used to enable and disable showing of database
+ * errors.
+ *
+ * @since 0.71
+ * @see wpdb::hide_errors()
+ *
+ * @param bool $show Whether to show or hide errors
+ * @return bool Old value for showing errors.
+ */
+ public function show_errors( $show = true ) {
+ $errors = $this->show_errors;
+ $this->show_errors = $show;
+ return $errors;
+ }
+
+ /**
+ * Disables showing of database errors.
+ *
+ * By default database errors are not shown.
+ *
+ * @since 0.71
+ * @see wpdb::show_errors()
+ *
+ * @return bool Whether showing of errors was active
+ */
+ public function hide_errors() {
+ $show = $this->show_errors;
+ $this->show_errors = false;
+ return $show;
+ }
+
+ /**
+ * Whether to suppress database errors.
+ *
+ * By default database errors are suppressed, with a simple
+ * call to this function they can be enabled.
+ *
+ * @since 2.5.0
+ * @see wpdb::hide_errors()
+ * @param bool $suppress Optional. New value. Defaults to true.
+ * @return bool Old value
+ */
+ public function suppress_errors( $suppress = true ) {
+ $errors = $this->suppress_errors;
+ $this->suppress_errors = (bool) $suppress;
+ return $errors;
+ }
+
+ /**
+ * Kill cached query results.
+ *
+ * @since 0.71
+ */
+ public function flush() {
+ $this->last_result = array();
+ $this->col_info = null;
+ $this->last_query = null;
+ $this->rows_affected = $this->num_rows = 0;
+ $this->last_error = '';
+
+ if ( $this->use_mysqli && $this->result instanceof mysqli_result ) {
+ mysqli_free_result( $this->result );
+ $this->result = null;
+
+ // Sanity check before using the handle
+ if ( empty( $this->dbh ) || !( $this->dbh instanceof mysqli ) ) {
+ return;
+ }
+
+ // Clear out any results from a multi-query
+ while ( mysqli_more_results( $this->dbh ) ) {
+ mysqli_next_result( $this->dbh );
+ }
+ } elseif ( is_resource( $this->result ) ) {
+ mysql_free_result( $this->result );
+ }
+ }
+
+ /**
+ * Connect to and select database.
+ *
+ * If $allow_bail is false, the lack of database connection will need
+ * to be handled manually.
+ *
+ * @since 3.0.0
+ * @since 3.9.0 $allow_bail parameter added.
+ *
+ * @param bool $allow_bail Optional. Allows the function to bail. Default true.
+ * @return bool True with a successful connection, false on failure.
+ */
+ public function db_connect( $allow_bail = true ) {
+ $this->is_mysql = true;
+
+ /*
+ * Deprecated in 3.9+ when using MySQLi. No equivalent
+ * $new_link parameter exists for mysqli_* functions.
+ */
+ $new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
+ $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
+
+ if ( $this->use_mysqli ) {
+ $this->dbh = mysqli_init();
+
+ // mysqli_real_connect doesn't support the host param including a port or socket
+ // like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file.
+ $port = null;
+ $socket = null;
+ $host = $this->dbhost;
+ $port_or_socket = strstr( $host, ':' );
+ if ( ! empty( $port_or_socket ) ) {
+ $host = substr( $host, 0, strpos( $host, ':' ) );
+ $port_or_socket = substr( $port_or_socket, 1 );
+ if ( 0 !== strpos( $port_or_socket, '/' ) ) {
+ $port = intval( $port_or_socket );
+ $maybe_socket = strstr( $port_or_socket, ':' );
+ if ( ! empty( $maybe_socket ) ) {
+ $socket = substr( $maybe_socket, 1 );
+ }
+ } else {
+ $socket = $port_or_socket;
+ }
+ }
+
+ if ( WP_DEBUG ) {
+ mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
+ } else {
+ @mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
+ }
+
+ if ( $this->dbh->connect_errno ) {
+ $this->dbh = null;
+
+ /* It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
+ * - We haven't previously connected, and
+ * - WP_USE_EXT_MYSQL isn't set to false, and
+ * - ext/mysql is loaded.
+ */
+ $attempt_fallback = true;
+
+ if ( $this->has_connected ) {
+ $attempt_fallback = false;
+ } elseif ( defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL ) {
+ $attempt_fallback = false;
+ } elseif ( ! function_exists( 'mysql_connect' ) ) {
+ $attempt_fallback = false;
+ }
+
+ if ( $attempt_fallback ) {
+ $this->use_mysqli = false;
+ return $this->db_connect( $allow_bail );
+ }
+ }
+ } else {
+ if ( WP_DEBUG ) {
+ $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
+ } else {
+ $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
+ }
+ }
+
+ if ( ! $this->dbh && $allow_bail ) {
+ wp_load_translations_early();
+
+ // Load custom DB error template, if present.
+ if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
+ require_once( WP_CONTENT_DIR . '/db-error.php' );
+ die();
+ }
+
+ $message = '<h1>' . __( 'Error establishing a database connection' ) . "</h1>\n";
+
+ $message .= '<p>' . sprintf(
+ /* translators: 1: wp-config.php. 2: database host */
+ __( 'This either means that the username and password information in your %1$s file is incorrect or we can’t contact the database server at %2$s. This could mean your host’s database server is down.' ),
+ '<code>wp-config.php</code>',
+ '<code>' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '</code>'
+ ) . "</p>\n";
+
+ $message .= "<ul>\n";
+ $message .= '<li>' . __( 'Are you sure you have the correct username and password?' ) . "</li>\n";
+ $message .= '<li>' . __( 'Are you sure that you have typed the correct hostname?' ) . "</li>\n";
+ $message .= '<li>' . __( 'Are you sure that the database server is running?' ) . "</li>\n";
+ $message .= "</ul>\n";
+
+ $message .= '<p>' . sprintf(
+ /* translators: %s: support forums URL */
+ __( 'If you’re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href="%s">WordPress Support Forums</a>.' ),
+ __( 'https://wordpress.org/support/' )
+ ) . "</p>\n";
+
+ $this->bail( $message, 'db_connect_fail' );
+
+ return false;
+ } elseif ( $this->dbh ) {
+ if ( ! $this->has_connected ) {
+ $this->init_charset();
+ }
+
+ $this->has_connected = true;
+
+ $this->set_charset( $this->dbh );
+
+ $this->ready = true;
+ $this->set_sql_mode();
+ $this->select( $this->dbname, $this->dbh );
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks that the connection to the database is still up. If not, try to reconnect.
+ *
+ * If this function is unable to reconnect, it will forcibly die, or if after the
+ * the {@see 'template_redirect'} hook has been fired, return false instead.
+ *
+ * If $allow_bail is false, the lack of database connection will need
+ * to be handled manually.
+ *
+ * @since 3.9.0
+ *
+ * @param bool $allow_bail Optional. Allows the function to bail. Default true.
+ * @return bool|void True if the connection is up.
+ */
+ public function check_connection( $allow_bail = true ) {
+ if ( $this->use_mysqli ) {
+ if ( ! empty( $this->dbh ) && mysqli_ping( $this->dbh ) ) {
+ return true;
+ }
+ } else {
+ if ( ! empty( $this->dbh ) && mysql_ping( $this->dbh ) ) {
+ return true;
+ }
+ }
+
+ $error_reporting = false;
+
+ // Disable warnings, as we don't want to see a multitude of "unable to connect" messages
+ if ( WP_DEBUG ) {
+ $error_reporting = error_reporting();
+ error_reporting( $error_reporting & ~E_WARNING );
+ }
+
+ for ( $tries = 1; $tries <= $this->reconnect_retries; $tries++ ) {
+ // On the last try, re-enable warnings. We want to see a single instance of the
+ // "unable to connect" message on the bail() screen, if it appears.
+ if ( $this->reconnect_retries === $tries && WP_DEBUG ) {
+ error_reporting( $error_reporting );
+ }
+
+ if ( $this->db_connect( false ) ) {
+ if ( $error_reporting ) {
+ error_reporting( $error_reporting );
+ }
+
+ return true;
+ }
+
+ sleep( 1 );
+ }
+
+ // If template_redirect has already happened, it's too late for wp_die()/dead_db().
+ // Let's just return and hope for the best.
+ if ( did_action( 'template_redirect' ) ) {
+ return false;
+ }
+
+ if ( ! $allow_bail ) {
+ return false;
+ }
+
+ wp_load_translations_early();
+
+ $message = '<h1>' . __( 'Error reconnecting to the database' ) . "</h1>\n";
+
+ $message .= '<p>' . sprintf(
+ /* translators: %s: database host */
+ __( 'This means that we lost contact with the database server at %s. This could mean your host’s database server is down.' ),
+ '<code>' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '</code>'
+ ) . "</p>\n";
+
+ $message .= "<ul>\n";
+ $message .= '<li>' . __( 'Are you sure that the database server is running?' ) . "</li>\n";
+ $message .= '<li>' . __( 'Are you sure that the database server is not under particularly heavy load?' ) . "</li>\n";
+ $message .= "</ul>\n";
+
+ $message .= '<p>' . sprintf(
+ /* translators: %s: support forums URL */
+ __( 'If you’re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href="%s">WordPress Support Forums</a>.' ),
+ __( 'https://wordpress.org/support/' )
+ ) . "</p>\n";
+
+ // We weren't able to reconnect, so we better bail.
+ $this->bail( $message, 'db_connect_fail' );
+
+ // Call dead_db() if bail didn't die, because this database is no more. It has ceased to be (at least temporarily).
+ dead_db();
+ }
+
+ /**
+ * Perform a MySQL database query, using current database connection.
+ *
+ * More information can be found on the codex page.
+ *
+ * @since 0.71
+ *
+ * @param string $query Database query
+ * @return int|false Number of rows affected/selected or false on error
+ */
+ public function query( $query ) {
+ if ( ! $this->ready ) {
+ $this->check_current_query = true;
+ return false;
+ }
+
+ /**
+ * Filters the database query.
+ *
+ * Some queries are made before the plugins have been loaded,
+ * and thus cannot be filtered with this method.
+ *
+ * @since 2.1.0
+ *
+ * @param string $query Database query.
+ */
+ $query = apply_filters( 'query', $query );
+
+ $this->flush();
+
+ // Log how the function was called
+ $this->func_call = "\$db->query(\"$query\")";
+
+ // If we're writing to the database, make sure the query will write safely.
+ if ( $this->check_current_query && ! $this->check_ascii( $query ) ) {
+ $stripped_query = $this->strip_invalid_text_from_query( $query );
+ // strip_invalid_text_from_query() can perform queries, so we need
+ // to flush again, just to make sure everything is clear.
+ $this->flush();
+ if ( $stripped_query !== $query ) {
+ $this->insert_id = 0;
+ return false;
+ }
+ }
+
+ $this->check_current_query = true;
+
+ // Keep track of the last query for debug.
+ $this->last_query = $query;
+
+ $this->_do_query( $query );
+
+ // MySQL server has gone away, try to reconnect.
+ $mysql_errno = 0;
+ if ( ! empty( $this->dbh ) ) {
+ if ( $this->use_mysqli ) {
+ if ( $this->dbh instanceof mysqli ) {
+ $mysql_errno = mysqli_errno( $this->dbh );
+ } else {
+ // $dbh is defined, but isn't a real connection.
+ // Something has gone horribly wrong, let's try a reconnect.
+ $mysql_errno = 2006;
+ }
+ } else {
+ if ( is_resource( $this->dbh ) ) {
+ $mysql_errno = mysql_errno( $this->dbh );
+ } else {
+ $mysql_errno = 2006;
+ }
+ }
+ }
+
+ if ( empty( $this->dbh ) || 2006 == $mysql_errno ) {
+ if ( $this->check_connection() ) {
+ $this->_do_query( $query );
+ } else {
+ $this->insert_id = 0;
+ return false;
+ }
+ }
+
+ // If there is an error then take note of it.
+ if ( $this->use_mysqli ) {
+ if ( $this->dbh instanceof mysqli ) {
+ $this->last_error = mysqli_error( $this->dbh );
+ } else {
+ $this->last_error = __( 'Unable to retrieve the error message from MySQL' );
+ }
+ } else {
+ if ( is_resource( $this->dbh ) ) {
+ $this->last_error = mysql_error( $this->dbh );
+ } else {
+ $this->last_error = __( 'Unable to retrieve the error message from MySQL' );
+ }
+ }
+
+ if ( $this->last_error ) {
+ // Clear insert_id on a subsequent failed insert.
+ if ( $this->insert_id && preg_match( '/^\s*(insert|replace)\s/i', $query ) )
+ $this->insert_id = 0;
+
+ $this->print_error();
+ return false;
+ }
+
+ if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) {
+ $return_val = $this->result;
+ } elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) {
+ if ( $this->use_mysqli ) {
+ $this->rows_affected = mysqli_affected_rows( $this->dbh );
+ } else {
+ $this->rows_affected = mysql_affected_rows( $this->dbh );
+ }
+ // Take note of the insert_id
+ if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
+ if ( $this->use_mysqli ) {
+ $this->insert_id = mysqli_insert_id( $this->dbh );
+ } else {
+ $this->insert_id = mysql_insert_id( $this->dbh );
+ }
+ }
+ // Return number of rows affected
+ $return_val = $this->rows_affected;
+ } else {
+ $num_rows = 0;
+ if ( $this->use_mysqli && $this->result instanceof mysqli_result ) {
+ while ( $row = mysqli_fetch_object( $this->result ) ) {
+ $this->last_result[$num_rows] = $row;
+ $num_rows++;
+ }
+ } elseif ( is_resource( $this->result ) ) {
+ while ( $row = mysql_fetch_object( $this->result ) ) {
+ $this->last_result[$num_rows] = $row;
+ $num_rows++;
+ }
+ }
+
+ // Log number of rows the query returned
+ // and return number of rows selected
+ $this->num_rows = $num_rows;
+ $return_val = $num_rows;
+ }
+
+ return $return_val;
+ }
+
+ /**
+ * Internal function to perform the mysql_query() call.
+ *
+ * @since 3.9.0
+ *
+ * @access private
+ * @see wpdb::query()
+ *
+ * @param string $query The query to run.
+ */
+ private function _do_query( $query ) {
+ if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
+ $this->timer_start();
+ }
+
+ if ( ! empty( $this->dbh ) && $this->use_mysqli ) {
+ $this->result = mysqli_query( $this->dbh, $query );
+ } elseif ( ! empty( $this->dbh ) ) {
+ $this->result = mysql_query( $query, $this->dbh );
+ }
+ $this->num_queries++;
+
+ if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
+ $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() );
+ }
+ }
+
+ /**
+ * Insert a row into a table.
+ *
+ * wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 'bar' ) )
+ * wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) )
+ *
+ * @since 2.5.0
+ * @see wpdb::prepare()
+ * @see wpdb::$field_types
+ * @see wp_set_wpdb_vars()
+ *
+ * @param string $table Table name
+ * @param array $data Data to insert (in column => value pairs).
+ * Both $data columns and $data values should be "raw" (neither should be SQL escaped).
+ * Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
+ * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
+ * If string, that format will be used for all of the values in $data.
+ * A format is one of '%d', '%f', '%s' (integer, float, string).
+ * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
+ * @return int|false The number of rows inserted, or false on error.
+ */
+ public function insert( $table, $data, $format = null ) {
+ return $this->_insert_replace_helper( $table, $data, $format, 'INSERT' );
+ }
+
+ /**
+ * Replace a row into a table.
+ *
+ * wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 'bar' ) )
+ * wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) )
+ *
+ * @since 3.0.0
+ * @see wpdb::prepare()
+ * @see wpdb::$field_types
+ * @see wp_set_wpdb_vars()
+ *
+ * @param string $table Table name
+ * @param array $data Data to insert (in column => value pairs).
+ * Both $data columns and $data values should be "raw" (neither should be SQL escaped).
+ * Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
+ * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
+ * If string, that format will be used for all of the values in $data.
+ * A format is one of '%d', '%f', '%s' (integer, float, string).
+ * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
+ * @return int|false The number of rows affected, or false on error.
+ */
+ public function replace( $table, $data, $format = null ) {
+ return $this->_insert_replace_helper( $table, $data, $format, 'REPLACE' );
+ }
+
+ /**
+ * Helper function for insert and replace.
+ *
+ * Runs an insert or replace query based on $type argument.
+ *
+ * @access private
+ * @since 3.0.0
+ * @see wpdb::prepare()
+ * @see wpdb::$field_types
+ * @see wp_set_wpdb_vars()
+ *
+ * @param string $table Table name
+ * @param array $data Data to insert (in column => value pairs).
+ * Both $data columns and $data values should be "raw" (neither should be SQL escaped).
+ * Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
+ * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
+ * If string, that format will be used for all of the values in $data.
+ * A format is one of '%d', '%f', '%s' (integer, float, string).
+ * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
+ * @param string $type Optional. What type of operation is this? INSERT or REPLACE. Defaults to INSERT.
+ * @return int|false The number of rows affected, or false on error.
+ */
+ function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) {
+ $this->insert_id = 0;
+
+ if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) {
+ return false;
+ }
+
+ $data = $this->process_fields( $table, $data, $format );
+ if ( false === $data ) {
+ return false;
+ }
+
+ $formats = $values = array();
+ foreach ( $data as $value ) {
+ if ( is_null( $value['value'] ) ) {
+ $formats[] = 'NULL';
+ continue;
+ }
+
+ $formats[] = $value['format'];
+ $values[] = $value['value'];
+ }
+
+ $fields = '`' . implode( '`, `', array_keys( $data ) ) . '`';
+ $formats = implode( ', ', $formats );
+
+ $sql = "$type INTO `$table` ($fields) VALUES ($formats)";
+
+ $this->check_current_query = false;
+ return $this->query( $this->prepare( $sql, $values ) );
+ }
+
+ /**
+ * Update a row in the table
+ *
+ * wpdb::update( 'table', array( 'column' => 'foo', 'field' => 'bar' ), array( 'ID' => 1 ) )
+ * wpdb::update( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( 'ID' => 1 ), array( '%s', '%d' ), array( '%d' ) )
+ *
+ * @since 2.5.0
+ * @see wpdb::prepare()
+ * @see wpdb::$field_types
+ * @see wp_set_wpdb_vars()
+ *
+ * @param string $table Table name
+ * @param array $data Data to update (in column => value pairs).
+ * Both $data columns and $data values should be "raw" (neither should be SQL escaped).
+ * Sending a null value will cause the column to be set to NULL - the corresponding
+ * format is ignored in this case.
+ * @param array $where A named array of WHERE clauses (in column => value pairs).
+ * Multiple clauses will be joined with ANDs.
+ * Both $where columns and $where values should be "raw".
+ * Sending a null value will create an IS NULL comparison - the corresponding format will be ignored in this case.
+ * @param array|string $format Optional. An array of formats to be mapped to each of the values in $data.
+ * If string, that format will be used for all of the values in $data.
+ * A format is one of '%d', '%f', '%s' (integer, float, string).
+ * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
+ * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where.
+ * If string, that format will be used for all of the items in $where.
+ * A format is one of '%d', '%f', '%s' (integer, float, string).
+ * If omitted, all values in $where will be treated as strings.
+ * @return int|false The number of rows updated, or false on error.