X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/00dbffaf1593b0ac719d98f00839221a9ca52133..9e77185fafaf4e60e2b73821e0e4b9b1a11fb85f:/wp-includes/wp-db.php?ds=sidebyside diff --git a/wp-includes/wp-db.php b/wp-includes/wp-db.php index b3aa8f6d..f30ad791 100644 --- a/wp-includes/wp-db.php +++ b/wp-includes/wp-db.php @@ -17,7 +17,8 @@ define( 'EZSQL_VERSION', 'WP1.25' ); /** * @since 0.71 */ -define( 'OBJECT', 'OBJECT', true ); +define( 'OBJECT', 'OBJECT' ); +define( 'object', 'OBJECT' ); // Back compat. /** * @since 2.5.0 @@ -51,7 +52,10 @@ define( 'ARRAY_N', 'ARRAY_N' ); class wpdb { /** - * Whether to show SQL/DB errors + * Whether to show SQL/DB errors. + * + * Default behavior is to show errors if both WP_DEBUG and WP_DEBUG_DISPLAY + * evaluated to true. * * @since 0.71 * @access private @@ -74,7 +78,7 @@ class wpdb { * @since 2.5.0 * @var string */ - var $last_error = ''; + public $last_error = ''; /** * Amount of queries made @@ -157,6 +161,16 @@ class wpdb { */ var $queries; + /** + * The number of times to retry reconnecting before dying. + * + * @since 3.9.0 + * @access protected + * @see wpdb::check_connection() + * @var int + */ + protected $reconnect_retries = 5; + /** * WordPress table prefix * @@ -170,6 +184,15 @@ class wpdb { */ var $prefix = ''; + /** + * WordPress base table prefix. + * + * @since 3.0.0 + * @access public + * @var string + */ + public $base_prefix; + /** * Whether the database queries are ready to start executing. * @@ -186,7 +209,7 @@ class wpdb { * @access public * @var int */ - var $blogid = 0; + public $blogid = 0; /** * {@internal Missing Description}} @@ -195,7 +218,7 @@ class wpdb { * @access public * @var int */ - var $siteid = 0; + public $siteid = 0; /** * List of WordPress per-blog tables @@ -248,7 +271,7 @@ class wpdb { * @access public * @var string */ - var $comments; + public $comments; /** * WordPress Comment Metadata table @@ -257,7 +280,7 @@ class wpdb { * @access public * @var string */ - var $commentmeta; + public $commentmeta; /** * WordPress Links table @@ -266,7 +289,7 @@ class wpdb { * @access public * @var string */ - var $links; + public $links; /** * WordPress Options table @@ -275,7 +298,7 @@ class wpdb { * @access public * @var string */ - var $options; + public $options; /** * WordPress Post Metadata table @@ -284,7 +307,7 @@ class wpdb { * @access public * @var string */ - var $postmeta; + public $postmeta; /** * WordPress Posts table @@ -293,7 +316,7 @@ class wpdb { * @access public * @var string */ - var $posts; + public $posts; /** * WordPress Terms table @@ -302,7 +325,7 @@ class wpdb { * @access public * @var string */ - var $terms; + public $terms; /** * WordPress Term Relationships table @@ -311,7 +334,7 @@ class wpdb { * @access public * @var string */ - var $term_relationships; + public $term_relationships; /** * WordPress Term Taxonomy table @@ -320,7 +343,7 @@ class wpdb { * @access public * @var string */ - var $term_taxonomy; + public $term_taxonomy; /* * Global and Multisite tables @@ -333,7 +356,7 @@ class wpdb { * @access public * @var string */ - var $usermeta; + public $usermeta; /** * WordPress Users table @@ -342,7 +365,7 @@ class wpdb { * @access public * @var string */ - var $users; + public $users; /** * Multisite Blogs table @@ -351,7 +374,7 @@ class wpdb { * @access public * @var string */ - var $blogs; + public $blogs; /** * Multisite Blog Versions table @@ -360,7 +383,7 @@ class wpdb { * @access public * @var string */ - var $blog_versions; + public $blog_versions; /** * Multisite Registration Log table @@ -369,7 +392,7 @@ class wpdb { * @access public * @var string */ - var $registration_log; + public $registration_log; /** * Multisite Signups table @@ -378,7 +401,7 @@ class wpdb { * @access public * @var string */ - var $signups; + public $signups; /** * Multisite Sites table @@ -387,7 +410,7 @@ class wpdb { * @access public * @var string */ - var $site; + public $site; /** * Multisite Sitewide Terms table @@ -396,7 +419,7 @@ class wpdb { * @access public * @var string */ - var $sitecategories; + public $sitecategories; /** * Multisite Site Metadata table @@ -405,7 +428,7 @@ class wpdb { * @access public * @var string */ - var $sitemeta; + public $sitemeta; /** * Format specifiers for DB columns. Columns not listed here default to %s. Initialized during WP load. @@ -421,7 +444,7 @@ class wpdb { * @access public * @var array */ - var $field_types = array(); + public $field_types = array(); /** * Database table columns charset @@ -430,7 +453,7 @@ class wpdb { * @access public * @var string */ - var $charset; + public $charset; /** * Database table columns collate @@ -439,16 +462,7 @@ class wpdb { * @access public * @var string */ - var $collate; - - /** - * Whether to use mysql_real_escape_string - * - * @since 2.8.0 - * @access public - * @var bool - */ - var $real_escape = false; + public $collate; /** * Database Username @@ -502,7 +516,7 @@ class wpdb { * @access public * @var string */ - var $func_call; + public $func_call; /** * Whether MySQL is used as the database engine. @@ -518,6 +532,34 @@ class wpdb { */ public $is_mysql = null; + /** + * A list of incompatible SQL modes. + * + * @since 3.9.0 + * @access protected + * @var array + */ + protected $incompatible_modes = array( 'NO_ZERO_DATE', 'ONLY_FULL_GROUP_BY', + 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'TRADITIONAL' ); + + /** + * Whether to use mysqli over mysql. + * + * @since 3.9.0 + * @access private + * @var bool + */ + private $use_mysqli = false; + + /** + * Whether we've managed to successfully connect at some point + * + * @since 3.9.0 + * @access private + * @var bool + */ + private $has_connected = false; + /** * Connects to the database server and selects a database * @@ -533,12 +575,28 @@ class wpdb { * @param string $dbname MySQL database name * @param string $dbhost MySQL database host */ - function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) { + public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) { register_shutdown_function( array( $this, '__destruct' ) ); - if ( WP_DEBUG ) + if ( WP_DEBUG && WP_DEBUG_DISPLAY ) $this->show_errors(); + /* Use ext/mysqli if it exists and: + * - WP_USE_EXT_MYSQL is defined as false, or + * - We are a development version of WordPress, or + * - We are running PHP 5.5 or greater, or + * - ext/mysql is not loaded. + */ + if ( function_exists( 'mysqli_connect' ) ) { + if ( defined( 'WP_USE_EXT_MYSQL' ) ) { + $this->use_mysqli = ! WP_USE_EXT_MYSQL; + } elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) { + $this->use_mysqli = true; + } elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) { + $this->use_mysqli = true; + } + } + $this->init_charset(); $this->dbuser = $dbuser; @@ -546,6 +604,11 @@ class wpdb { $this->dbname = $dbname; $this->dbhost = $dbhost; + // wp-config.php creation will manually connect when ready. + if ( defined( 'WP_SETUP_CONFIG' ) ) { + return; + } + $this->db_connect(); } @@ -556,7 +619,7 @@ class wpdb { * @since 2.0.8 * @return bool true */ - function __destruct() { + public function __destruct() { return true; } @@ -568,7 +631,7 @@ class wpdb { * @param string $name The private member to get, and optionally process * @return mixed The private member */ - function __get( $name ) { + public function __get( $name ) { if ( 'col_info' == $name ) $this->load_col_info(); @@ -576,19 +639,19 @@ class wpdb { } /** - * Magic function, for backwards compatibility + * Magic function, for backwards compatibility. * * @since 3.5.0 * * @param string $name The private member to set * @param mixed $value The value to set */ - function __set( $name, $value ) { + public function __set( $name, $value ) { $this->$name = $value; } /** - * Magic function, for backwards compatibility + * Magic function, for backwards compatibility. * * @since 3.5.0 * @@ -596,18 +659,18 @@ class wpdb { * * @return bool If the member is set or not */ - function __isset( $name ) { + public function __isset( $name ) { return isset( $this->$name ); } /** - * Magic function, for backwards compatibility + * Magic function, for backwards compatibility. * * @since 3.5.0 * * @param string $name The private member to unset */ - function __unset( $name ) { + public function __unset( $name ) { unset( $this->$name ); } @@ -616,7 +679,7 @@ class wpdb { * * @since 3.1.0 */ - function init_charset() { + public function init_charset() { if ( function_exists('is_multisite') && is_multisite() ) { $this->charset = 'utf8'; if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) @@ -640,21 +703,98 @@ class wpdb { * @param string $charset The character set (optional) * @param string $collate The collation (optional) */ - function set_charset($dbh, $charset = null, $collate = null) { - if ( !isset($charset) ) + public function set_charset( $dbh, $charset = null, $collate = null ) { + if ( ! isset( $charset ) ) $charset = $this->charset; - if ( !isset($collate) ) + if ( ! isset( $collate ) ) $collate = $this->collate; - if ( $this->has_cap( 'collation', $dbh ) && !empty( $charset ) ) { - if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset', $dbh ) ) { - mysql_set_charset( $charset, $dbh ); - $this->real_escape = true; + if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) { + if ( $this->use_mysqli ) { + if ( function_exists( 'mysqli_set_charset' ) && $this->has_cap( 'set_charset' ) ) { + mysqli_set_charset( $dbh, $charset ); + } else { + $query = $this->prepare( 'SET NAMES %s', $charset ); + if ( ! empty( $collate ) ) + $query .= $this->prepare( ' COLLATE %s', $collate ); + mysqli_query( $query, $dbh ); + } + } else { + if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset' ) ) { + mysql_set_charset( $charset, $dbh ); + } else { + $query = $this->prepare( 'SET NAMES %s', $charset ); + if ( ! empty( $collate ) ) + $query .= $this->prepare( ' COLLATE %s', $collate ); + mysql_query( $query, $dbh ); + } + } + } + } + + /** + * Change the current SQL mode, and ensure its WordPress compatibility. + * + * If no modes are passed, it will ensure the current MySQL server + * modes are compatible. + * + * @since 3.9.0 + * + * @param array $modes Optional. A list of SQL modes to set. + */ + public function set_sql_mode( $modes = array() ) { + if ( empty( $modes ) ) { + if ( $this->use_mysqli ) { + $res = mysqli_query( $this->dbh, 'SELECT @@SESSION.sql_mode' ); } else { - $query = $this->prepare( 'SET NAMES %s', $charset ); - if ( ! empty( $collate ) ) - $query .= $this->prepare( ' COLLATE %s', $collate ); - mysql_query( $query, $dbh ); + $res = mysql_query( 'SELECT @@SESSION.sql_mode', $this->dbh ); + } + + if ( empty( $res ) ) { + return; + } + + if ( $this->use_mysqli ) { + $modes_array = mysqli_fetch_array( $res ); + if ( empty( $modes_array[0] ) ) { + return; + } + $modes_str = $modes_array[0]; + } else { + $modes_str = mysql_result( $res, 0 ); + } + + if ( empty( $modes_str ) ) { + return; } + + $modes = explode( ',', $modes_str ); + } + + $modes = array_change_key_case( $modes, CASE_UPPER ); + + /** + * Filter the list of incompatible SQL modes to exclude. + * + * @since 3.9.0 + * + * @see wpdb::$incompatible_modes + * + * @param array $incompatible_modes An array of incompatible modes. + */ + $incompatible_modes = (array) apply_filters( 'incompatible_sql_modes', $this->incompatible_modes ); + + foreach( $modes as $i => $mode ) { + if ( in_array( $mode, $incompatible_modes ) ) { + unset( $modes[ $i ] ); + } + } + + $modes_str = implode( ',', $modes ); + + if ( $this->use_mysqli ) { + mysqli_query( $this->dbh, "SET SESSION sql_mode='$modes_str'" ); + } else { + mysql_query( "SET SESSION sql_mode='$modes_str'", $this->dbh ); } } @@ -667,7 +807,7 @@ class wpdb { * @param bool $set_table_names Optional. Whether the table names, e.g. wpdb::$posts, should be updated or not. * @return string|WP_Error Old prefix or WP_Error on error */ - function set_prefix( $prefix, $set_table_names = true ) { + public function set_prefix( $prefix, $set_table_names = true ) { if ( preg_match( '|[^a-z0-9_]|i', $prefix ) ) return new WP_Error('invalid_db_prefix', 'Invalid database prefix' ); @@ -706,7 +846,7 @@ class wpdb { * @param int $site_id Optional. * @return string previous blog id */ - function set_blog_id( $blog_id, $site_id = 0 ) { + public function set_blog_id( $blog_id, $site_id = 0 ) { if ( ! empty( $site_id ) ) $this->siteid = $site_id; @@ -732,7 +872,7 @@ class wpdb { * @param int $blog_id Optional. * @return string Blog prefix. */ - function get_blog_prefix( $blog_id = null ) { + public function get_blog_prefix( $blog_id = null ) { if ( is_multisite() ) { if ( null === $blog_id ) $blog_id = $this->blogid; @@ -774,7 +914,7 @@ class wpdb { * @param int $blog_id Optional. The blog_id to prefix. Defaults to wpdb::$blogid. Used only when prefix is requested. * @return array Table names. When a prefix is requested, the key is the unprefixed table name. */ - function tables( $scope = 'all', $prefix = true, $blog_id = 0 ) { + public function tables( $scope = 'all', $prefix = true, $blog_id = 0 ) { switch ( $scope ) { case 'all' : $tables = array_merge( $this->global_tables, $this->tables ); @@ -797,7 +937,6 @@ class wpdb { break; default : return array(); - break; } if ( $prefix ) { @@ -836,44 +975,57 @@ class wpdb { * @param resource $dbh Optional link identifier. * @return null Always null. */ - function select( $db, $dbh = null ) { + public function select( $db, $dbh = null ) { if ( is_null($dbh) ) $dbh = $this->dbh; - if ( !@mysql_select_db( $db, $dbh ) ) { + if ( $this->use_mysqli ) { + $success = @mysqli_select_db( $dbh, $db ); + } else { + $success = @mysql_select_db( $db, $dbh ); + } + if ( ! $success ) { $this->ready = false; - wp_load_translations_early(); - $this->bail( sprintf( __( '
We were able to connect to the database server (which means your username and password is okay) but not able to select the %1$s
database.
%2$s
have permission to use the %1$s
database?username_%1$s
. Could that be the problem?If you don\'t know how to set up a database you should contact your host. If all else fails you may find help at the WordPress Support Forums.
' ), htmlspecialchars( $db, ENT_QUOTES ), htmlspecialchars( $this->dbuser, ENT_QUOTES ) ), 'db_select_fail' ); +If you don\'t know how to set up a database you should contact your host. If all else fails you may find help at the WordPress Support Forums.
' ), htmlspecialchars( $db, ENT_QUOTES ), htmlspecialchars( $this->dbuser, ENT_QUOTES ) ), 'db_select_fail' ); + } return; } } /** - * Weak escape, using addslashes() + * Do not use, deprecated. + * + * Use esc_sql() or wpdb::prepare() instead. * - * @see addslashes() * @since 2.8.0 + * @deprecated 3.6.0 + * @see wpdb::prepare + * @see esc_sql() * @access private * * @param string $string * @return string */ function _weak_escape( $string ) { + if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) ) + _deprecated_function( __METHOD__, '3.6', 'wpdb::prepare() or esc_sql()' ); return addslashes( $string ); } /** - * Real escape, using mysql_real_escape_string() or addslashes() + * Real escape, using mysqli_real_escape_string() or mysql_real_escape_string() * + * @see mysqli_real_escape_string() * @see mysql_real_escape_string() - * @see addslashes() * @since 2.8.0 * @access private * @@ -881,16 +1033,22 @@ class wpdb { * @return string escaped */ function _real_escape( $string ) { - if ( $this->dbh && $this->real_escape ) - return mysql_real_escape_string( $string, $this->dbh ); - else - return addslashes( $string ); + if ( $this->dbh ) { + if ( $this->use_mysqli ) { + return mysqli_real_escape_string( $this->dbh, $string ); + } else { + return mysql_real_escape_string( $string, $this->dbh ); + } + } + + $class = get_class( $this ); + _doing_it_wrong( $class, "$class must set a database connection for use with escaping.", E_USER_NOTICE ); + return addslashes( $string ); } /** * Escape data. Works on arrays. * - * @uses wpdb::_escape() * @uses wpdb::_real_escape() * @since 2.8.0 * @access private @@ -900,7 +1058,7 @@ class wpdb { */ function _escape( $data ) { if ( is_array( $data ) ) { - foreach ( (array) $data as $k => $v ) { + foreach ( $data as $k => $v ) { if ( is_array($v) ) $data[$k] = $this->_escape( $v ); else @@ -914,24 +1072,30 @@ class wpdb { } /** - * Escapes content for insertion into the database using addslashes(), for security. + * Do not use, deprecated. * - * Works on arrays. + * Use esc_sql() or wpdb::prepare() instead. * * @since 0.71 - * @param string|array $data to escape - * @return string|array escaped as query safe string + * @deprecated 3.6.0 + * @see wpdb::prepare() + * @see esc_sql() + * + * @param mixed $data + * @return mixed */ - function escape( $data ) { + public function escape( $data ) { + if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) ) + _deprecated_function( __METHOD__, '3.6', 'wpdb::prepare() or esc_sql()' ); if ( is_array( $data ) ) { - foreach ( (array) $data as $k => $v ) { + foreach ( $data as $k => $v ) { if ( is_array( $v ) ) - $data[$k] = $this->escape( $v ); + $data[$k] = $this->escape( $v, 'recursive' ); else - $data[$k] = $this->_weak_escape( $v ); + $data[$k] = $this->_weak_escape( $v, 'internal' ); } } else { - $data = $this->_weak_escape( $data ); + $data = $this->_weak_escape( $data, 'internal' ); } return $data; @@ -945,7 +1109,7 @@ class wpdb { * @param string $string to escape * @return void */ - function escape_by_ref( &$string ) { + public function escape_by_ref( &$string ) { if ( ! is_float( $string ) ) $string = $this->_real_escape( $string ); } @@ -987,12 +1151,14 @@ class wpdb { * @return null|false|string Sanitized query string, null if there is no query, false if there is an error and string * if there was something to prepare */ - function prepare( $query, $args = null ) { + public function prepare( $query, $args ) { if ( is_null( $query ) ) return; - if ( func_num_args() < 2 ) - _doing_it_wrong( 'wpdb::prepare', 'wpdb::prepare() requires at least two arguments.', '3.5' ); + // This is not meant to be foolproof -- but it will catch obviously incorrect usage. + if ( strpos( $query, '%' ) === false ) { + _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9' ); + } $args = func_get_args(); array_shift( $args ); @@ -1007,6 +1173,32 @@ class wpdb { return @vsprintf( $query, $args ); } + /** + * First half of escaping for LIKE special characters % and _ before preparing for MySQL. + * + * Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security. + * + * Example Prepared Statement: + * $wild = '%'; + * $find = 'only 43% of planets'; + * $like = $wild . $wpdb->esc_like( $find ) . $wild; + * $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE %s", $like ); + * + * Example Escape Chain: + * $sql = esc_sql( $wpdb->esc_like( $input ) ); + * + * @since 4.0.0 + * @access public + * + * @param string $text The raw text to be escaped. The input typed by the user should have no + * extra or deleted slashes. + * @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare() + * or real_escape next. + */ + public function esc_like( $text ) { + return addcslashes( $text, '_%\\' ); + } + /** * Print SQL/DB error. * @@ -1016,11 +1208,16 @@ class wpdb { * @param string $str The error to display * @return bool False if the showing of errors is disabled. */ - function print_error( $str = '' ) { + public function print_error( $str = '' ) { global $EZSQL_ERROR; - if ( !$str ) - $str = mysql_error( $this->dbh ); + 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 ) @@ -1071,7 +1268,7 @@ class wpdb { * @param bool $show Whether to show or hide errors * @return bool Old value for showing errors. */ - function show_errors( $show = true ) { + public function show_errors( $show = true ) { $errors = $this->show_errors; $this->show_errors = $show; return $errors; @@ -1087,7 +1284,7 @@ class wpdb { * * @return bool Whether showing of errors was active */ - function hide_errors() { + public function hide_errors() { $show = $this->show_errors; $this->show_errors = false; return $show; @@ -1104,7 +1301,7 @@ class wpdb { * @param bool $suppress Optional. New value. Defaults to true. * @return bool Old value */ - function suppress_errors( $suppress = true ) { + public function suppress_errors( $suppress = true ) { $errors = $this->suppress_errors; $this->suppress_errors = (bool) $suppress; return $errors; @@ -1116,35 +1313,114 @@ class wpdb { * @since 0.71 * @return void */ - function flush() { + 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 ( is_resource( $this->result ) ) - mysql_free_result( $this->result ); + if ( is_resource( $this->result ) ) { + if ( $this->use_mysqli ) { + mysqli_free_result( $this->result ); + } else { + mysql_free_result( $this->result ); + } + } } /** - * Connect to and select database + * 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. */ - function db_connect() { + 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 ( WP_DEBUG ) { - $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags ); + 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; + } else if ( defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL ) { + $attempt_fallback = false; + } else if ( ! function_exists( 'mysql_connect' ) ) { + $attempt_fallback = false; + } + + if ( $attempt_fallback ) { + $this->use_mysqli = false; + $this->db_connect(); + } + } } else { - $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags ); + 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 ) { + 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(); + } + $this->bail( sprintf( __( "This either means that the username and password information in your wp-config.php
file is incorrect or we can't contact the database server at %s
. This could mean your host's database server is down.
If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.
+If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.
" ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' ); - return; + return false; + } else if ( $this->dbh ) { + $this->has_connected = true; + $this->set_charset( $this->dbh ); + $this->set_sql_mode(); + $this->ready = true; + $this->select( $this->dbname, $this->dbh ); + + return true; + } + + return false; + } + + /** + * Check 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 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 True if the connection is up. + */ + public function check_connection( $allow_bail = true ) { + if ( $this->use_mysqli ) { + if ( @mysqli_ping( $this->dbh ) ) { + return true; + } + } else { + if ( @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; } - $this->set_charset( $this->dbh ); + if ( ! $allow_bail ) { + return false; + } - $this->ready = true; + // We weren't able to reconnect, so we better bail. + $this->bail( sprintf( ( " +This means that we lost contact with the database server at %s
. This could mean your host's database server is down.
If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.
+" ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' ); - $this->select( $this->dbname, $this->dbh ); + // Call dead_db() if bail didn't die, because this database is no more. It has ceased to be (at least temporarily). + dead_db(); } /** @@ -1176,14 +1532,22 @@ class wpdb { * @param string $query Database query * @return int|false Number of rows affected/selected or false on error */ - function query( $query ) { + public function query( $query ) { if ( ! $this->ready ) return false; - // some queries are made before the plugins have been loaded, and thus cannot be filtered with this method + /** + * Filter 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 ); - $return_val = 0; $this->flush(); // Log how the function was called @@ -1192,17 +1556,39 @@ class wpdb { // Keep track of the last query for debug.. $this->last_query = $query; - if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) - $this->timer_start(); + $this->_do_query( $query ); - $this->result = @mysql_query( $query, $this->dbh ); - $this->num_queries++; + // MySQL server has gone away, try to reconnect + $mysql_errno = 0; + if ( ! empty( $this->dbh ) ) { + if ( $this->use_mysqli ) { + $mysql_errno = mysqli_errno( $this->dbh ); + } else { + $mysql_errno = mysql_errno( $this->dbh ); + } + } - if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) - $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() ); + 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->last_error = mysql_error( $this->dbh ) ) { + if ( $this->use_mysqli ) { + $this->last_error = mysqli_error( $this->dbh ); + } else { + $this->last_error = mysql_error( $this->dbh ); + } + + 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; } @@ -1210,18 +1596,33 @@ class wpdb { 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 ) ) { - $this->rows_affected = mysql_affected_rows( $this->dbh ); + 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 ) ) { - $this->insert_id = mysql_insert_id($this->dbh); + 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; - while ( $row = @mysql_fetch_object( $this->result ) ) { - $this->last_result[$num_rows] = $row; - $num_rows++; + if ( $this->use_mysqli ) { + while ( $row = @mysqli_fetch_object( $this->result ) ) { + $this->last_result[$num_rows] = $row; + $num_rows++; + } + } else { + while ( $row = @mysql_fetch_object( $this->result ) ) { + $this->last_result[$num_rows] = $row; + $num_rows++; + } } // Log number of rows the query returned @@ -1233,6 +1634,33 @@ class wpdb { 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 ( $this->use_mysqli ) { + $this->result = @mysqli_query( $this->dbh, $query ); + } else { + $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. * @@ -1252,7 +1680,7 @@ class wpdb { * 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. */ - function insert( $table, $data, $format = null ) { + public function insert( $table, $data, $format = null ) { return $this->_insert_replace_helper( $table, $data, $format, 'INSERT' ); } @@ -1275,7 +1703,7 @@ class wpdb { * 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. */ - function replace( $table, $data, $format = null ) { + public function replace( $table, $data, $format = null ) { return $this->_insert_replace_helper( $table, $data, $format, 'REPLACE' ); } @@ -1300,6 +1728,7 @@ class wpdb { function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) { if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) return false; + $this->insert_id = 0; $formats = $format = (array) $format; $fields = array_keys( $data ); $formatted_fields = array(); @@ -1337,7 +1766,7 @@ class wpdb { * @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. */ - function update( $table, $data, $where, $format = null, $where_format = null ) { + public function update( $table, $data, $where, $format = null, $where_format = null ) { if ( ! is_array( $data ) || ! is_array( $where ) ) return false; @@ -1386,11 +1815,11 @@ class wpdb { * @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 unless otherwise specified in wpdb::$field_types. * @return int|false The number of rows updated, or false on error. */ - function delete( $table, $where, $where_format = null ) { + public function delete( $table, $where, $where_format = null ) { if ( ! is_array( $where ) ) return false; - $bits = $wheres = array(); + $wheres = array(); $where_formats = $where_format = (array) $where_format; @@ -1425,7 +1854,7 @@ class wpdb { * @param int $y Optional. Row of value to return. Indexed from 0. * @return string|null Database query result (as string), or null on failure */ - function get_var( $query = null, $x = 0, $y = 0 ) { + public function get_var( $query = null, $x = 0, $y = 0 ) { $this->func_call = "\$db->get_var(\"$query\", $x, $y)"; if ( $query ) $this->query( $query ); @@ -1452,7 +1881,7 @@ class wpdb { * @param int $y Optional. Row to return. Indexed from 0. * @return mixed Database query result in format specified by $output or null on failure */ - function get_row( $query = null, $output = OBJECT, $y = 0 ) { + public function get_row( $query = null, $output = OBJECT, $y = 0 ) { $this->func_call = "\$db->get_row(\"$query\",$output,$y)"; if ( $query ) $this->query( $query ); @@ -1468,6 +1897,9 @@ class wpdb { return $this->last_result[$y] ? get_object_vars( $this->last_result[$y] ) : null; } elseif ( $output == ARRAY_N ) { return $this->last_result[$y] ? array_values( get_object_vars( $this->last_result[$y] ) ) : null; + } elseif ( strtoupper( $output ) === OBJECT ) { + // Back compat for OBJECT being previously case insensitive. + return $this->last_result[$y] ? $this->last_result[$y] : null; } else { $this->print_error( " \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N" ); } @@ -1486,7 +1918,7 @@ class wpdb { * @param int $x Optional. Column to return. Indexed from 0. * @return array Database query result. Array indexed from 0 by SQL result row number. */ - function get_col( $query = null , $x = 0 ) { + public function get_col( $query = null , $x = 0 ) { if ( $query ) $this->query( $query ); @@ -1511,7 +1943,7 @@ class wpdb { * With OBJECT_K, return an associative array of row objects keyed by the value of each row's first column's value. Duplicate keys are discarded. * @return mixed Database query results */ - function get_results( $query = null, $output = OBJECT ) { + public function get_results( $query = null, $output = OBJECT ) { $this->func_call = "\$db->get_results(\"$query\", $output)"; if ( $query ) @@ -1547,6 +1979,9 @@ class wpdb { } } return $new_array; + } elseif ( strtoupper( $output ) === OBJECT ) { + // Back compat for OBJECT being previously case insensitive. + return $this->last_result; } return null; } @@ -1562,8 +1997,14 @@ class wpdb { if ( $this->col_info ) return; - for ( $i = 0; $i < @mysql_num_fields( $this->result ); $i++ ) { - $this->col_info[ $i ] = @mysql_fetch_field( $this->result, $i ); + if ( $this->use_mysqli ) { + for ( $i = 0; $i < @mysqli_num_fields( $this->result ); $i++ ) { + $this->col_info[ $i ] = @mysqli_fetch_field( $this->result ); + } + } else { + for ( $i = 0; $i < @mysql_num_fields( $this->result ); $i++ ) { + $this->col_info[ $i ] = @mysql_fetch_field( $this->result, $i ); + } } } @@ -1576,7 +2017,7 @@ class wpdb { * @param int $col_offset Optional. 0: col name. 1: which table the col's in. 2: col's max length. 3: if the col is numeric. 4: col's type * @return mixed Column Results */ - function get_col_info( $info_type = 'name', $col_offset = -1 ) { + public function get_col_info( $info_type = 'name', $col_offset = -1 ) { $this->load_col_info(); if ( $this->col_info ) { @@ -1601,7 +2042,7 @@ class wpdb { * * @return true */ - function timer_start() { + public function timer_start() { $this->time_start = microtime( true ); return true; } @@ -1613,7 +2054,7 @@ class wpdb { * * @return float Total time spent on the query, in seconds */ - function timer_stop() { + public function timer_stop() { return ( microtime( true ) - $this->time_start ); } @@ -1628,7 +2069,7 @@ class wpdb { * @param string $error_code Optional. A Computer readable string to identify the error. * @return false|void */ - function bail( $message, $error_code = '500' ) { + public function bail( $message, $error_code = '500' ) { if ( !$this->show_errors ) { if ( class_exists( 'WP_Error' ) ) $this->error = new WP_Error($error_code, $message); @@ -1648,7 +2089,7 @@ class wpdb { * * @return WP_Error */ - function check_database_version() { + public function check_database_version() { global $wp_version, $required_mysql_version; // Make sure the server has the required MySQL version if ( version_compare($this->db_version(), $required_mysql_version, '<') ) @@ -1666,7 +2107,7 @@ class wpdb { * * @return bool True if collation is supported, false if version does not */ - function supports_collation() { + public function supports_collation() { _deprecated_function( __FUNCTION__, '3.5', 'wpdb::has_cap( \'collation\' )' ); return $this->has_cap( 'collation' ); } @@ -1690,24 +2131,24 @@ class wpdb { } /** - * Determine if a database supports a particular feature + * Determine if a database supports a particular feature. * * @since 2.7.0 - * @see wpdb::db_version() + * @see wpdb::db_version() * - * @param string $db_cap the feature + * @param string $db_cap The feature to check for. * @return bool */ - function has_cap( $db_cap ) { + public function has_cap( $db_cap ) { $version = $this->db_version(); switch ( strtolower( $db_cap ) ) { case 'collation' : // @since 2.5.0 - case 'group_concat' : // @since 2.7 - case 'subqueries' : // @since 2.7 + case 'group_concat' : // @since 2.7.0 + case 'subqueries' : // @since 2.7.0 return version_compare( $version, '4.1', '>=' ); case 'set_charset' : - return version_compare($version, '5.0.7', '>='); + return version_compare( $version, '5.0.7', '>=' ); }; return false; @@ -1723,7 +2164,7 @@ class wpdb { * * @return string The name of the calling function */ - function get_caller() { + public function get_caller() { return wp_debug_backtrace_summary( __CLASS__ ); } @@ -1734,7 +2175,12 @@ class wpdb { * * @return false|string false on failure, version number on success */ - function db_version() { - return preg_replace( '/[^0-9.].*/', '', mysql_get_server_info( $this->dbh ) ); + public function db_version() { + if ( $this->use_mysqli ) { + $server_info = mysqli_get_server_info( $this->dbh ); + } else { + $server_info = mysql_get_server_info( $this->dbh ); + } + return preg_replace( '/[^0-9.].*/', '', $server_info ); } }