'all', 'zone_priorities' => array( 'all' ) ); /** * Established connections * * @var array */ private $_connections; /** * After any SQL_CALC_FOUND_ROWS query, the query "SELECT FOUND_ROWS()" * is sent and the mysql result resource stored here. The next query * for FOUND_ROWS() will retrieve this. We do this to prevent any * intervening queries from making FOUND_ROWS() inaccessible. You may * prevent this by adding "NO_SELECT_FOUND_ROWS" in a comment. * * @var resource */ private $_last_found_rows_result; /** * The last table that was queried * * @var string */ private $_last_table; /** * Reject reason */ private $_reject_reason = null; /** * Send Reads To Masters. This disables slave connections while true. * Otherwise it is an array of written tables. * * @var array */ private $_send_reads_to_master = false; /** * Send all request to master db if user is in administration. * * @var bool */ public $use_master_in_backend = true; /** * Which charset to use for connections * * @var string */ public $charset = null; /** * Which collate to use for connections * * @var string */ public $collate = null; /** * Initializes object */ function initialize() { global $wpdb_cluster; $wpdb_cluster = $this; if ( !isset( $GLOBALS['w3tc_dbcluster_config'] ) && file_exists( WP_CONTENT_DIR . '/db-cluster-config.php' ) ) { // The config file resides in WP_CONTENT_DIR require WP_CONTENT_DIR . '/db-cluster-config.php'; } if ( isset( $GLOBALS['w3tc_dbcluster_config'] ) ) { $this->apply_configuration( $GLOBALS['w3tc_dbcluster_config'] ); } else { $this->_reject_reason = 'w3tc dbcluster configuration not found, ' . 'using single-server configuration'; $this->next_injection->initialize(); return; } if ( WP_DEBUG ) $this->wpdb_mixin->show_errors(); $this->wpdb_mixin->dbh = null; $this->wpdb_mixin->ready = true; $this->init_charset(); } /** * Applies configuration from array */ public function apply_configuration( $c ) { if ( isset( $c['persistent'] ) ) { $this->persistent = $c['persistent']; } if ( isset( $c['check_tcp_responsiveness'] ) ) { $this->persistent = $c['check_tcp_responsiveness']; } if ( isset( $c['use_master_in_backend'] ) ) { $this->persistent = $c['use_master_in_backend']; } if ( isset( $c['charset'] ) ) { $this->persistent = $c['charset']; } if ( isset( $c['collate'] ) ) { $this->persistent = $c['collate']; } if ( isset( $c['filters'] ) && is_array( $c['filters'] ) ) { foreach ($c['filters'] as $filter) { $this->add_callback( $filter['function_to_add'], $filter['tag'] ); } } if ( isset( $c['databases'] ) && is_array( $c['databases'] ) ) { foreach ($c['databases'] as $key => $db) { $this->add_database( $db ); } } if ( isset( $c['zones'] ) && is_array( $c['zones'] ) ) { foreach ($c['zones'] as $key => $zone) { $zone['name'] = $key; $this->add_zone( $zone ); } } } /** * Sets default charset and collate * If DB_CHARSET not set uses utf8 * If DB_COLLATE not set uses utf8_general_ci if multisite. */ function init_charset() { if ( function_exists( 'is_multisite' ) && is_multisite() ) { if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) $this->wpdb_mixin->collate = DB_COLLATE; else $this->wpdb_mixin->collate = 'utf8_general_ci'; } elseif ( defined( 'DB_COLLATE' ) ) { $this->wpdb_mixin->collate = DB_COLLATE; } if ( defined( 'DB_CHARSET' ) ) $this->wpdb_mixin->charset = DB_CHARSET; else $this->wpdb_mixin->charset = 'utf8'; } /** * Adds zone which defines priorities based on current application location * * @param array $zone * keys: * name * SERVER_NAME * zone_priorities array */ function add_zone( $zone ) { if ( $this->_is_current_zone( $zone ) ) { if ( !isset( $zone['zone_priorities'] ) || !is_array( $zone['zone_priorities'] ) ) { die( 'zone_priorities key must be defined' ); } $this->_current_zone = $zone; $this->_run_callbacks( 'current_zone_set', $zone ); } } /** * Add the connection parameters for a database * * @param array $db * keys: * name * dataset * read * write * host * user * password * connect_function * timeout * zone */ function add_database( $db ) { $dataset = ( isset( $db['dataset'] ) ? $db['dataset'] : 'global' ); unset( $db['dataset'] ); $read = ( isset( $db['read'] ) ? $db['read'] : true ); unset( $db['read'] ); $write = ( isset( $db['write'] ) ? $db['write'] : true ); unset( $db['write'] ); $zone = ( isset( $db['zone'] ) ? $db['zone'] : 'all' ); unset( $db['zone'] ); if ( !isset( $db['timeout'] ) ) $db['timeout'] = 0.2; if ( !isset( $db['name'] ) ) $db['name'] = $db['host']; if ( $read ) $this->_cluster_servers[$dataset]['read'][$zone][] = $db; if ( $write ) $this->_cluster_servers[$dataset]['write'][$zone][] = $db; } /** * Add a callback to a group of callbacks. * The default group is 'dataset', used to examine * queries and determine dataset. * * @param object $callback * @param string $group */ function add_callback( $callback, $group = 'dataset' ) { $this->_callbacks[$group][] = $callback; } /** * Set a flag to prevent reading from slaves which might be lagging after a write */ function send_reads_to_masters() { $this->_send_reads_to_master = true; } /** * If requests should be send to master or not. * * @return bool */ function send_to_masters() { return is_admin() && $this->use_master_in_backend; } /** * Figure out which database server should handle the query, and connect to it. * * @param string $query * @return resource mysql database connection */ function db_connect( $query = '', $use_master = null ) { if ( empty( $this->_cluster_servers ) ) return $this->_db_connect_fallback(); $this->wpdb_mixin->dbh = null; if ( empty( $query ) ) return false; $this->_last_table = $this->wpdb_mixin->table = $this->_get_table_from_query( $query ); $this->callback_result = $this->_run_callbacks( 'dataset', $query ); if ( !is_null( $this->callback_result ) ) $dataset = $this->callback_result; elseif ( isset( $this->_callbacks['blog_dataset'] ) ) { if ( preg_match( '/^' . $this->wpdb_mixin->base_prefix . '(\d+)_/i', $this->wpdb_mixin->table, $matches ) ) { $blog_id = $matches[1]; if ( isset( $this->_blog_to_dataset[$blog_id] ) ) $dataset = $this->_blog_to_dataset[$blog_id]; else { $this->callback_result = $this->_run_callbacks( 'blog_dataset', $blog_id ); if ( !is_null( $this->callback_result ) ) { $dataset = $this->callback_result; $this->_blog_to_dataset[$blog_id] = $dataset; } } } } $dataset = ( isset( $dataset ) ? $dataset : 'global' ); $this->dataset = $dataset; // Determine whether the query must be sent to the master (a writable server) if ( is_null( $use_master ) ) { if ( $this->_send_reads_to_master || $this->send_to_masters() ) { $use_master = true; } elseif ( defined( 'DONOTBCLUSTER' ) && DONOTBCLUSTER ) { $use_master = true; } elseif ( $is_write = $this->_is_write_query( $query ) ) { $use_master = true; } else { $use_master = false; } } if ( $use_master ) { $this->dbhname = $dataset . '__w'; $operation = 'write'; } else { $this->dbhname = $dataset . '__r'; $operation = 'read'; } // Try to reuse an existing connection $dbh = $this->_db_connect_reuse_connection(); if ( is_object( $dbh ) ) { $this->wpdb_mixin->dbh = $dbh; return $dbh; } if ( empty( $this->_cluster_servers[$dataset][$operation] ) ) return $this->wpdb_mixin->bail( "No databases available for dataset $dataset operation $operation" ); // Make a list of at least $this->min_tries connections to try, repeating as necessary. $servers = array(); do { foreach ( $this->_current_zone['zone_priorities'] as $zone ) { if ( isset( $this->_cluster_servers[$dataset][$operation][$zone] ) ) { $zone_servers = $this->_cluster_servers[$dataset][$operation][$zone]; if ( is_array( $zone_servers ) ) { $indexes = array_keys( $zone_servers ); shuffle( $indexes ); foreach ( $indexes as $index ) $servers[] = compact( 'zone', 'index' ); } } } } while ( count( $servers ) < $this->min_tries ); // Connect to a database server $success = false; $errno = 0; $dbhname = $this->dbhname; foreach ( $servers as $zone_index ) { // $zone, $index extract( $zone_index, EXTR_OVERWRITE ); // $host, $user, $password, $name, $read, $write, $timeout extract( $this->_cluster_servers[$dataset][$operation][$zone][$index], EXTR_OVERWRITE ); // Split host:port into $host and $port list( $host, $port ) = Util_Content::endpoint_to_host_port( $host, 3306 ); $this->wpdb_mixin->timer_start(); // Connect if necessary or possible $tcp_responded = null; if ( $this->check_tcp_responsiveness ) { $tcp_responded = $this->_check_tcp_responsiveness( $host, $port, $timeout ); } $dbh = null; if ( is_null( $tcp_responded ) || $tcp_responded ) { $dbh = mysqli_init(); $socket = null; $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0; if ( WP_DEBUG ) { mysqli_real_connect( $dbh, $host, $user, $password, null, $port, $socket, $client_flags ); } else { @mysqli_real_connect( $dbh, $host, $user, $password, null, $port, $socket, $client_flags ); } } $elapsed = $this->wpdb_mixin->timer_stop(); if ( !is_null( $dbh ) ) { if ( $dbh->connect_errno ) { $errno = $dbh->connect_errno; } elseif ( mysqli_select_db( $dbh, $name ) ) { $this->_connections[$dbhname] = array( 'dbh' => $dbh, 'database_name' => $name ); $success = true; break; } } } if ( !$success ) { if ( !$use_master ) { return $this->db_connect( $query, true ); } return $this->wpdb_mixin->bail( "Unable to connect to $host:$port to $operation table '{$this->wpdb_mixin->table}' ($dataset) with $errno" ); } $dbh = $this->_connections[$dbhname]['dbh']; $this->wpdb_mixin->dbh = $dbh; // needed by $wpdb->_real_escape() $this->set_charset( $dbh, $this->charset, $this->collate ); $this->set_sql_mode(); return $dbh; } /* * Checks if this is our zone * * @param $zone array * @return boolean */ function _is_current_zone( $zone ) { $server_name = isset( $_SERVER['SERVER_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_NAME'] ) ) : ''; // obsolete. if ( isset( $zone['SERVER_NAME'] ) ) { if ( $server_name === $zone['SERVER_NAME'] ) { return true; } } if ( isset( $zone['server_names'] ) ) { if ( ! is_array( $zone['server_names'] ) ) { die( 'server_names must be defined as array' ); } foreach ( $zone['server_names'] as $zone_server_name ) { if ( '*' === $zone_server_name ) { return true; } if ( $server_name === $zone_server_name ) { return true; } } } return false; } /* * Tries to reuse opened connection * * @return resource */ function _db_connect_reuse_connection() { $dbhname = $this->dbhname; if ( !isset( $this->_connections[$dbhname] ) ) return null; $connection = & $this->_connections[$dbhname]; $dbh = $connection['dbh']; if ( !is_object( $dbh ) ) return null; if ( !mysqli_ping( $dbh ) ) { // disconnect (ping failed) $this->_disconnect( $dbhname ); return null; } return $dbh; } /** * Sets the connection's character set. * * @param resource $dbh The resource given by mysqli_connect * @param string $charset The character set (optional) * @param string $collate The collation (optional) */ function set_charset( $dbh, $charset = null, $collate = null ) { if ( !isset( $charset ) ) $charset = $this->wpdb_mixin->charset; if ( !isset( $collate ) ) $collate = $this->wpdb_mixin->collate; if ( $this->has_cap( 'collation', $dbh ) && !empty( $charset ) ) { if ( function_exists( 'mysqli_set_charset' ) && $this->has_cap( 'set_charset', $dbh ) ) { mysqli_set_charset( $dbh, $charset ); $this->wpdb_mixin->real_escape = true; } else { $query = $this->wpdb_mixin->prepare( 'SET NAMES %s', $charset ); if ( !empty( $collate ) ) $query .= $this->wpdb_mixin->prepare( ' COLLATE %s', $collate ); mysqli_query( $dbh, $query ); } } } /** * Kill cached query results */ function flush() { $this->wpdb_mixin->last_error = ''; $this->num_rows = 0; $this->next_injection->flush(); } /** * Basic query. See docs for more details. * * @param string $query * @return int number of rows */ function query( $query ) { // some queries are made before the plugins have been loaded, and thus cannot be filtered with this method if ( function_exists( 'apply_filters' ) ) $query = apply_filters( 'query', $query ); $this->flush(); // Log how the function was called $this->wpdb_mixin->func_call = "\$db->query(\"$query\")"; // Keep track of the last query for debug.. $this->wpdb_mixin->last_query = $query; if ( preg_match( '/^\s*SELECT\s+FOUND_ROWS(\s*)/i', $query ) && is_object( $this->wpdb_mixin->_last_found_rows_result ) ) { $this->wpdb_mixin->result = $this->wpdb_mixin->_last_found_rows_result; $elapsed = 0; } else { $this->db_connect( $query ); if ( !is_object( $this->wpdb_mixin->dbh ) ) return false; $this->wpdb_mixin->timer_start(); $this->wpdb_mixin->result = mysqli_query( $this->wpdb_mixin->dbh, $query ); $elapsed = $this->wpdb_mixin->timer_stop(); ++$this->wpdb_mixin->num_queries; if ( preg_match( '/^\s*SELECT\s+SQL_CALC_FOUND_ROWS\s/i', $query ) ) { if ( false === strpos( $query, "NO_SELECT_FOUND_ROWS" ) ) { $this->wpdb_mixin->timer_start(); $this->wpdb_mixin->_last_found_rows_result = mysqli_query( $this->wpdb_mixin->dbh, "SELECT FOUND_ROWS()" ); $elapsed += $this->wpdb_mixin->timer_stop(); ++$this->wpdb_mixin->num_queries; $query .= "; SELECT FOUND_ROWS()"; } } else { $this->wpdb_mixin->_last_found_rows_result = null; } } if ( function_exists( 'apply_filters' ) ) apply_filters( 'after_query', $query ); // If there is an error then take note of it if ( $this->wpdb_mixin->last_error = mysqli_error( $this->wpdb_mixin->dbh ) ) { $this->wpdb_mixin->print_error( $this->wpdb_mixin->last_error ); return false; } if ( preg_match( "/^\\s*(insert|delete|update|replace|alter) /i", $query ) ) { $this->wpdb_mixin->rows_affected = mysqli_affected_rows( $this->wpdb_mixin->dbh ); // Take note of the insert_id if ( preg_match( "/^\\s*(insert|replace) /i", $query ) ) { $this->wpdb_mixin->insert_id = mysqli_insert_id( $this->wpdb_mixin->dbh ); } // Return number of rows affected return $this->wpdb_mixin->rows_affected; } else { $i = 0; $this->wpdb_mixin->col_info = array(); $col_info = array(); while ( $i < @mysqli_num_fields( $this->wpdb_mixin->result ) ) { $col_info[$i] = @mysqli_fetch_field( $this->wpdb_mixin->result ); $i++; } $this->wpdb_mixin->col_info = $col_info; $num_rows = 0; $this->wpdb_mixin->last_result = array(); while ( $row = @mysqli_fetch_object( $this->wpdb_mixin->result ) ) { $this->wpdb_mixin->last_result[$num_rows] = $row; $num_rows++; } // Log number of rows the query returned $this->num_rows = $num_rows; // Return number of rows selected return $num_rows; } } /** * Usually it's called from inside other higher-level function * so connection is available, but sometimes called directly. * So connection should be initialized **/ function _escape( $data ) { if ( !$this->wpdb_mixin->dbh ) { $this->db_connect( 'SELECT * FROM nothing' ); } return $this->wpdb_mixin->default__escape( $data ); } /** * Prepare calls escape, so database connection required **/ function prepare( $query, $args ) { if ( !$this->wpdb_mixin->dbh ) { $this->db_connect( $query ); } return $this->wpdb_mixin->default_prepare( $query, $args ); } /** * Whether or not MySQL database is at least the required minimum version. * The additional argument allows the caller to check a specific database. * * @param string $dbh_or_table * @return WP_Error */ function check_database_version( $dbh_or_table = false ) { global $wp_version; // Make sure the server has MySQL 4.1.2 $mysqli_version = preg_replace( '|[^0-9\.]|', '', $this->wpdb_mixin->db_version( $dbh_or_table ) ); if ( version_compare( $mysqli_version, '4.1.2', '<' ) ) return new \WP_Error( 'database_version', sprintf( __( 'ERROR: WordPress %s requires MySQL 4.1.2 or higher' ), $wp_version ) ); } /** * This function is called when WordPress is generating the table schema to determine wether or not the current database * supports or needs the collation statements. * The additional argument allows the caller to check a specific database. * * @param string $dbh_or_table * @return bool */ function supports_collation( $dbh_or_table = false ) { return $this->wpdb_mixin->has_cap( 'collation', $dbh_or_table ); } /** * Generic function to determine if a database supports a particular feature * The additional argument allows the caller to check a specific database. * * @param string $db_cap the feature * @param bool|string|resource $dbh_or_table the databaese (the current database, the database housing the specified table, or the database of the mysql resource) * @return bool */ function has_cap( $db_cap, $dbh_or_table = false ) { $version = $this->wpdb_mixin->db_version( $dbh_or_table ); switch ( strtolower( $db_cap ) ) : case 'collation' : case 'group_concat' : case 'subqueries' : return version_compare( $version, '4.1', '>=' ); case 'set_charset' : return version_compare( $version, '5.0.7', '>=' ); endswitch; return false; } /** * The database version number * * @param false|string|resource $dbh_or_table the databaese (the current database, the database housing the specified table, or the database of the mysql resource) * @return false|string false on failure, version number on success */ function db_version( $dbh_or_table = false ) { $dbh = null; if ( !$dbh_or_table && $this->wpdb_mixin->dbh ) $dbh = $this->wpdb_mixin->dbh; elseif ( is_object( $dbh_or_table ) ) $dbh = $dbh_or_table; else $dbh = $this->db_connect( "SELECT FROM $dbh_or_table $this->wpdb_mixin->users" ); if ( !is_null( $dbh ) ) return preg_replace( '/[^0-9.].*/', '', mysqli_get_server_info( $dbh ) ); return false; } /** * Disconnect and remove connection from open connections list * * @param string $dbhname */ function _disconnect( $dbhname ) { if ( isset( $this->_connections[$dbhname] ) ) { $dbh = $this->_connections[$dbhname]['dbh']; if ( is_object( $dbh ) ) mysqli_close( $dbh ); unset( $this->_connections[$dbhname] ); } } /** * Check the responsiveness of a tcp/ip daemon * * @return (bool) true when $host:$post responds within $float_timeout seconds, else (bool) false */ function _check_tcp_responsiveness( $host, $port, $float_timeout ) { $socket = @ fsockopen( $host, $port, $errno, $errstr, $float_timeout ); if ( $socket === false ) return "[ > $float_timeout ] ($errno) '$errstr'"; fclose( $socket ); return true; } /** * Find the first table name referenced in a query * * @param string $q * @return string table */ function _get_table_from_query( $q ) { // Remove characters that can legally trail the table name $q = rtrim( $q, ';/-#' ); // allow (select...) union [...] style queries. Use the first queries table name. $q = ltrim( $q, "\t (" ); // Quickly match most common queries if ( preg_match( '/^\s*(?:' . 'SELECT.*?\s+FROM' . '|INSERT(?:\s+IGNORE)?(?:\s+INTO)?' . '|REPLACE(?:\s+INTO)?' . '|UPDATE(?:\s+IGNORE)?' . '|DELETE(?:\s+IGNORE)?(?:\s+FROM)?' . ')\s+`?(\w+)`?/is', $q, $maybe ) ) return $maybe[1]; // Refer to the previous query if ( preg_match( '/^\s*SELECT.*?\s+FOUND_ROWS\(\)/is', $q ) ) return $this->_last_table; // SHOW TABLE STATUS LIKE and SHOW TABLE STATUS WHERE Name = if ( preg_match( '/^\s*' . 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)' . '\W(\w+)\W/is', $q, $maybe ) ) return $maybe[1]; // Big pattern for the rest of the table-related queries in MySQL 5.0 if ( preg_match( '/^\s*(?:' . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM' . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?' . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?' . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?' . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?' . '|DESCRIBE|DESC|EXPLAIN|HANDLER' . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?' . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|OPTIMIZE|REPAIR).*\s+TABLE' . '|TRUNCATE(?:\s+TABLE)?' . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?' . '|ALTER(?:\s+IGNORE)?\s+TABLE' . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?' . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON' . '|DROP\s+INDEX.*\s+ON' . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE' . '|(?:GRANT|REVOKE).*ON\s+TABLE' . '|SHOW\s+(?:.*FROM|.*TABLE)' . ')\s+`?(\w+)`?/is', $q, $maybe ) ) return $maybe[1]; } /** * Determine the likelihood that this query could alter anything * * @param string $q * @return bool */ function _is_write_query( $q ) { // Quick and dirty: only SELECT statements are considered read-only. $q = ltrim( $q, "\r\n\t (" ); return!preg_match( '/^(?:SELECT|SHOW|DESCRIBE|EXPLAIN)\s/i', $q ); } /** * Callbacks are executed in the order in which they are registered until one * of them returns something other than null. * * @param string $group * @param array $args */ function _run_callbacks( $group, $args = null ) { if ( !isset( $this->_callbacks[$group] ) || !is_array( $this->_callbacks[$group] ) ) return null; if ( !isset( $args ) ) { $args = array( $this ); } elseif ( is_array( $args ) ) { $args[] = $this; } else { $args = array( $args, $this ); } foreach ( $this->_callbacks[$group] as $func ) { $result = call_user_func_array( $func, $args ); if ( isset( $result ) ) return $result; } } /** * Connects to server using default configuration * * @return resource mysql database connection */ function _db_connect_fallback() { if ( is_object( $this->wpdb_mixin->dbh ) ) return $this->wpdb_mixin->dbh; if ( !defined( 'DB_HOST' ) || !defined( 'DB_USER' ) || !defined( 'DB_PASSWORD' ) || !defined( 'DB_NAME' ) ) return $this->wpdb_mixin->bail( "We were unable to query because there was no database defined." ); $this->wpdb_mixin->dbh = mysqli_init(); $port = null; $socket = null; $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0; if ( WP_DEBUG ) { mysqli_real_connect( $this->wpdb_mixin->dbh, DB_HOST, DB_USER, DB_PASSWORD, null, $port, $socket, $client_flags ); } else { @mysqli_real_connect( $this->wpdb_mixin->dbh, $host, $user, $password, null, $port, $socket, $client_flags ); } if ( !is_object( $this->wpdb_mixin->dbh ) ) return $this->wpdb_mixin->bail( "We were unable to connect to the database. (DB_HOST)" ); if ( !mysqli_select_db( $this->wpdb_mixin->dbh, DB_NAME ) ) return $this->wpdb_mixin->bail( "We were unable to select the database." ); if ( !empty( $this->wpdb_mixin->charset ) ) { $collation_query = "SET NAMES '{$this->wpdb_mixin->charset}'"; if ( !empty( $this->wpdb_mixin->collate ) ) $collation_query .= " COLLATE '{$this->wpdb_mixin->collate}'"; mysqli_query( $this->wpdb_mixin->dbh, $collation_query ); } return $this->wpdb_mixin->dbh; } function w3tc_footer_comment( $strings ) { $append = ''; if ( isset( $this->_current_zone['name'] ) && $this->_current_zone['name'] != 'all' ) $append .= ' with home zone ' . $this->_current_zone['name']; if ( !is_null( $this->_reject_reason ) ) $append .= ' (' . $this->_reject_reason . ')'; $strings[] = 'Database cluster enabled ' . $append; return $strings; } public function w3tc_usage_statistics_of_request( $storage ) { } }