Make WordPress Core

Ticket #30006: extract_wpdb_API.patch

File extract_wpdb_API.patch, 30.7 KB (added by aercolino, 7 years ago)
  • src/wp-includes/wp-db-api.php

     
     1<?php
     2/**
     3 * WordPress DB API Class
     4 *
     5 * @since X4.2
     6 * @package WordPress
     7 * @subpackage Database
     8 */
     9
     10/**
     11 * WordPress Database API
     12 *
     13 * This class is a collection of low level methods for accessing the database without taking WordPress into account.
     14 * All code that requires a different approach according to the currently used API should be put here too.
     15 *
     16 * api_*    methods provide a  generic interface to API functions that        have corresponding functionality.
     17 * db_*     methods provide a specific interface to API functions that do not have corresponding functionality.
     18 * mysqli_* methods provide a doctored access to mysqli_* functions.
     19 * mysql_*  methods provide a doctored access to mysql_*  functions.
     20 *
     21 * @since X4.2
     22 * @package WordPress
     23 * @subpackage Database
     24 */
     25class wpdb_API {
     26
     27        /**
     28         * API Name
     29         *
     30         * One of these two values:
     31         * - 'mysqli'
     32         * - 'mysql'
     33         *
     34         * @since X4.2
     35         * @var string
     36         */
     37        protected $api = '';
     38
     39        /**
     40         * Fallback State
     41         *
     42         * One of these four values:
     43         *
     44         * - 'impossible' (a fallback is not an option)
     45         * - 'possible'   (can attempt a fallback)
     46         * - 'success'    (a fallback allowed to connect)
     47         * - 'failure'    (out of luck for connecting)
     48         *
     49         * @since X4.2
     50         * @var string
     51         */
     52        protected $fallback;
     53
     54        /**
     55         * Database Username
     56         *
     57         * @since 2.9.0
     58         * @var string
     59         */
     60        protected $dbuser;
     61
     62        /**
     63         * Database Password
     64         *
     65         * @since 3.1.0
     66         * @var string
     67         */
     68        protected $dbpassword;
     69
     70        /**
     71         * Database Name
     72         *
     73         * @since 3.1.0
     74         * @var string
     75         */
     76        protected $dbname;
     77
     78        /**
     79         * Database Host
     80         *
     81         * @since 3.1.0
     82         * @var string
     83         */
     84        protected $dbhost;
     85
     86        /**
     87         * Database Handle
     88         *
     89         * @since 0.71
     90         * @var string
     91         */
     92        protected $dbh = null;
     93
     94        /**
     95         * Store connection data.
     96         *
     97         * @param string $dbuser     MySQL database user
     98         * @param string $dbpassword MySQL database password
     99         * @param string $dbname     MySQL database name
     100         * @param string $dbhost     MySQL database host
     101         */
     102        protected function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
     103                $this->dbuser     = $dbuser;
     104                $this->dbpassword = $dbpassword;
     105                $this->dbname     = $dbname;
     106                $this->dbhost     = $dbhost;
     107
     108                $this->init_correspondence();
     109                $this->init_api_to_use();
     110        }
     111
     112        /**
     113         * Detect the API to use
     114         *
     115         * @since X4.2
     116         */
     117        protected function init_api_to_use() {
     118                $got_mysqli  = function_exists( 'mysqli_connect' );
     119                $got_mysql   = function_exists( 'mysql_connect' );
     120                $not_mysql   = defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL;
     121                $PHP_5_5_min = version_compare( phpversion(), '5.5', '>=' );
     122                $core_dev    = false !== strpos( $GLOBALS['wp_version'], '-' );
     123
     124                $this->api = $got_mysqli && ( $not_mysql || $PHP_5_5_min || ! $got_mysql || $core_dev )
     125                        ? 'mysqli'
     126                        : 'mysql';
     127
     128                $this->fallback = ( $this->api == 'mysqli' ) && ! $not_mysql && $got_mysql
     129                        ? 'possible'
     130                        : 'impossible';
     131        }
     132
     133        /**
     134         * These mysql_* functions and mysqli_* counterparts have the same parameters.
     135         * The value has no meaning, we only use the key.
     136         *
     137         * @since X4.2
     138         * @var array
     139         */
     140        private $same_arguments;
     141
     142        /**
     143         * These mysql_* functions have parameters in a certain order, mysqli_* counterparts have them in another order.
     144         * The value has no meaning, we only use the key.
     145         *
     146         * @since X4.2
     147         * @var array
     148         */
     149        private $swapped_arguments;
     150
     151        /**
     152         * These mysql_* functions have some parameters, mysqli_* counterparts have some other.
     153         * The value is the number of same initial parameters.
     154         *
     155         * @since X4.2
     156         * @var array
     157         */
     158        private $different_arguments;
     159
     160        /**
     161         * These mysql_* functions exist, mysqli_* counterparts do not.
     162         * The value has no meaning, we only use the key.
     163         *
     164         * @since X4.2
     165         * @var array
     166         */
     167        private $defunct_functions;
     168
     169        /**
     170         * Categorize mysql_* and mysqli_* functions.
     171         *
     172         * @since X4.2
     173         */
     174        protected function init_correspondence() {
     175                // these arrays use keys for speed (isset VS in_array)
     176
     177                $this->same_arguments = array_flip( explode( ' ', 'affected_rows client_encoding close data_seek errno error'
     178                        . ' fetch_array fetch_assoc fetch_field fetch_lengths fetch_object fetch_row field_seek free_result'
     179                        . ' get_client_info get_host_info get_proto_info get_server_info info insert_id num_rows ping stat'
     180                        . ' thread_id' ) );
     181                        // mysql_fetch_field accepts one additional parameter, but mostly useless
     182                        // mysqli_get_client_info accepts one additional parameter
     183
     184                $this->swapped_arguments = array_flip( explode( ' ', 'query real_escape_string select_db set_charset' ) );
     185                        // mysqli_query accepts an additional parameter
     186
     187                // use db_connect instead
     188                $this->different_arguments = array( 'connect' => 3 );
     189                        // mysql additionally accepts $new_link, $client_flags; mysqli additionally accepts $dbname, $port, $socket
     190
     191                // if needed, these functions should be implemented along the lines of what done for connect in db_connect
     192                $this->defunct_functions = array_flip( explode( ' ', 'create_db db_name db_query drop_db escape_string'
     193                        . ' field_flags field_len field_name field_table field_type list_dbs list_fields list_processes list_tables'
     194                        . ' num_fields pconnect result tablename unbuffered_query' ) );
     195                        // mysql_escape_string highly discouraged, mysqli_escape_string alias of mysqli_real_escape_string
     196                        // mysql_result is a function, mysqli_result is a class
     197        }
     198
     199        /**
     200         * Extract the term from $name.
     201         *
     202         * Example:
     203         *
     204         *    api_term('mysqli_close') == 'close';  // true
     205         *    api_term('mysql_close')  == 'close';  // true
     206         *    api_term('close')        == 'close';  // true
     207         *
     208         * @since X4.2
     209         * @param  string $name The name of an API function
     210         * @return string       The term contained in $name
     211         */
     212        protected function api_term($name) {
     213                $result = $name;
     214                if (strpos($name, $this->api . '_') === 0) {
     215                        $result = substr($name, strlen($this->api . '_'));
     216                }
     217                return $result;
     218        }
     219
     220        /**
     221         * Reorder arguments of mysqli_* functions when needed for calling mysql_* counterparts.
     222         *
     223         * @since X4.2
     224         * @param  string $name The mysqli_* function
     225         * @param  array  $args The arguments of the mysqli_* function
     226         * @return mixed        The arguments of the corresponding mysql_* function
     227         * @throws Exception
     228         */
     229        protected function api_args($name, $args) {
     230                // when using mysqli we can short-circuit right here
     231                if ( 'mysqli' == $this->api ) {
     232                        return $args;
     233                }
     234
     235                $term = $this->api_term( $name );
     236
     237                if ( isset( $this->same_arguments[$term] ) ) {
     238                        return $args;
     239                }
     240                if ( isset( $this->swapped_arguments[$term] ) ) {
     241                        array_splice( $args, 0, 2, array( $args[1], $args[0] ));
     242                        return $args;
     243                }
     244                if ( isset( $this->different_arguments[$term] ) ) {
     245                        if ( count($args) <= $this->different_arguments[$term] ) {
     246                                return $args;
     247                        }
     248                }
     249                if ( isset( $this->defunct_functions[$term] ) ) {
     250                        // we should never get here, except due to developer's misunderstanding
     251                        throw new Exception('Expected a mysqli compatible function call.');
     252                }
     253                // we should never get here, except due to developer's misunderstanding
     254                throw new Exception('Expected a mysql function call.');
     255        }
     256
     257        /**
     258         * Make an array with mysqli_* keys and either mysqli_* or mysql_* values, according to the current API.
     259         *
     260         * Use extract() to define API function variables with same name and arguments (in the same order).
     261         *
     262         * Example:
     263         *
     264         *    extract( $this->api_vars( 'close' ) );
     265         *    // or extract( $this->api_vars( 'mysqli_close' ) );
     266         *    // or extract( $this->api_vars( 'mysql_close' ) );
     267         *    $mysqli_close( $this->dbh );
     268         *
     269         * Prefer mysqli_* names for making it easier to find where a given API is called.
     270         *
     271         * @since X4.2
     272         * @var    array ... Names of API functions
     273         * @return array     Array of variable names to API names
     274         */
     275        protected function api_vars() {
     276                $names = func_get_args();
     277                $result = array();
     278                foreach ($names as $name) {
     279                        $term = $this->api_term($name);
     280                        $result["mysqli_$term"] = $this->api . '_' . $term;
     281                }
     282                return $result;
     283        }
     284
     285        /**
     286         * Call the current API function with the given name and additional arguments.
     287         *
     288         * This method allows to call API functions with the same name and arguments (possibly in a different order).
     289         *
     290         * Example:
     291         *
     292         *    $this->api_call( 'close' );
     293         *    // or $this->api_call( 'mysqli_close' );
     294         *    // or $this->api_call( 'mysql_close' );
     295         *
     296         * Prefer mysqli_* names for making it easier to find where a given API is called.
     297         *
     298         * @since X4.2
     299         * @param  $name The name of the API function to call
     300         * @param  ...   All the arguments for calling the API function
     301         * @return mixed The value returned by the API call
     302         */
     303        public function api_call($name) {
     304                $term = $this->api_term($name);
     305                $args = func_get_args();
     306                array_shift($args); // discard $name
     307                $args = $this->api_args($term, $args);
     308                $name = $this->api . '_' . $term;
     309                return call_user_func_array($name, $args);
     310        }
     311
     312        /**
     313         * Connect to the database using mysql
     314         *
     315         * @since X4.2
     316         */
     317        private function mysql_connect() {
     318                /*
     319                 * Deprecated in 3.9+ when using MySQLi. No equivalent
     320                 * $new_link parameter exists for mysqli_* functions.
     321                 */
     322                $new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
     323                $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
     324
     325                if ( WP_DEBUG ) {
     326                        $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
     327                } else {
     328                        $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
     329                }
     330        }
     331
     332        /**
     333         * Connect to the database using mysqli
     334         *
     335         * @since X4.2
     336         */
     337        private function mysqli_connect() {
     338                $this->dbh = mysqli_init();
     339
     340                // mysqli_real_connect doesn't support the host param including a port or socket
     341                // like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file.
     342                $port = null;
     343                $socket = null;
     344                $host = $this->dbhost;
     345                $port_or_socket = strstr( $host, ':' );
     346                if ( ! empty( $port_or_socket ) ) {
     347                        $host = substr( $host, 0, strpos( $host, ':' ) );
     348                        $port_or_socket = substr( $port_or_socket, 1 );
     349                        if ( 0 !== strpos( $port_or_socket, '/' ) ) {
     350                                $port = intval( $port_or_socket );
     351                                $maybe_socket = strstr( $port_or_socket, ':' );
     352                                if ( ! empty( $maybe_socket ) ) {
     353                                        $socket = substr( $maybe_socket, 1 );
     354                                }
     355                        } else {
     356                                $socket = $port_or_socket;
     357                        }
     358                }
     359
     360                $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
     361
     362                if ( WP_DEBUG ) {
     363                        mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
     364                } else {
     365                        @mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
     366                }
     367        }
     368
     369        /**
     370         * Connect to the database using the current API.
     371         * This method COULD change the current API from mysqli to mysql in case of a fallback.
     372         *
     373         * mysqli_connect works quite differently from mysql_connect: we need a specific method for each and a this one for
     374         * orchestrating. Notice that (public) wpdb::db_connect() overrides (protected) wpdb_API::db_connect().
     375         *
     376         * @since X4.2
     377         * @return boolean TRUE if connected
     378         */
     379        protected function db_connect() {
     380                if ( 'mysqli' == $this->api ) {
     381                        $this->mysqli_connect();
     382
     383                        if ( $this->dbh->connect_errno ) {
     384                                $this->dbh = null;
     385                                if ( 'possible' == $this->fallback ) {
     386                                        $this->api = 'mysql';
     387                                        $this->mysql_connect();
     388                                        $this->fallback = $this->dbh ? 'success' : 'failure';
     389                                }
     390                        }
     391                } else {
     392                        $this->mysql_connect();
     393                }
     394
     395                return (bool) $this->dbh;
     396        }
     397
     398        /**
     399         * List SQL modes.
     400         *
     401         * @since X4.2
     402         * @return array The set of SQL modes
     403         */
     404        protected function db_modes() {
     405                $result = array();
     406
     407                $res = $this->api_call( 'query', $this->dbh, 'SELECT @@SESSION.sql_mode' );
     408                if ( empty( $res ) ) {
     409                        return $result;
     410                }
     411
     412                if ( 'mysqli' == $this->api ) {
     413                        $modes_array = mysqli_fetch_array( $res );
     414                        if ( empty( $modes_array[0] ) ) {
     415                                return $result;
     416                        }
     417                        $modes_str = $modes_array[0];
     418                } else {
     419                        $modes_str = mysql_result( $res, 0 );
     420                }
     421                if ( empty( $modes_str ) ) {
     422                        return $result;
     423                }
     424
     425                $result = explode( ',', $modes_str );
     426                return $result;
     427        }
     428}
  • src/wp-includes/wp-db.php

     
    11<?php
     2
     3require_once( ABSPATH . WPINC . '/wp-db-api.php' );
     4
    25/**
    36 * WordPress DB Class
    47 *
     
    4952 * @subpackage Database
    5053 * @since 0.71
    5154 */
    52 class wpdb {
     55class wpdb extends wpdb_API {
    5356
    5457        /**
    5558         * Whether to show SQL/DB errors.
     
    465468        public $collate;
    466469
    467470        /**
    468          * Database Username
    469          *
    470          * @since 2.9.0
    471          * @access protected
    472          * @var string
    473          */
    474         protected $dbuser;
    475 
    476         /**
    477          * Database Password
    478          *
    479          * @since 3.1.0
    480          * @access protected
    481          * @var string
    482          */
    483         protected $dbpassword;
    484 
    485         /**
    486          * Database Name
    487          *
    488          * @since 3.1.0
    489          * @access protected
    490          * @var string
    491          */
    492         protected $dbname;
    493 
    494         /**
    495          * Database Host
    496          *
    497          * @since 3.1.0
    498          * @access protected
    499          * @var string
    500          */
    501         protected $dbhost;
    502 
    503         /**
    504          * Database Handle
    505          *
    506          * @since 0.71
    507          * @access protected
    508          * @var string
    509          */
    510         protected $dbh;
    511 
    512         /**
    513471         * A textual description of the last query/get_row/get_var call
    514472         *
    515473         * @since 3.0.0
     
    545503        /**
    546504         * Whether to use mysqli over mysql.
    547505         *
     506         * @deprecated
     507         * @property-read
    548508         * @since 3.9.0
    549509         * @access private
    550510         * @var bool
     
    554514        /**
    555515         * Whether we've managed to successfully connect at some point
    556516         *
     517         * @deprecated
     518         * @property-read
    557519         * @since 3.9.0
    558520         * @access private
    559521         * @var bool
     
    581543                if ( WP_DEBUG && WP_DEBUG_DISPLAY )
    582544                        $this->show_errors();
    583545
    584                 /* Use ext/mysqli if it exists and:
    585                  *  - WP_USE_EXT_MYSQL is defined as false, or
    586                  *  - We are a development version of WordPress, or
    587                  *  - We are running PHP 5.5 or greater, or
    588                  *  - ext/mysql is not loaded.
    589                  */
    590                 if ( function_exists( 'mysqli_connect' ) ) {
    591                         if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
    592                                 $this->use_mysqli = ! WP_USE_EXT_MYSQL;
    593                         } elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) {
    594                                 $this->use_mysqli = true;
    595                         } elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
    596                                 $this->use_mysqli = true;
    597                         }
    598                 }
    599 
    600546                $this->init_charset();
    601547
    602                 $this->dbuser = $dbuser;
    603                 $this->dbpassword = $dbpassword;
    604                 $this->dbname = $dbname;
    605                 $this->dbhost = $dbhost;
     548                parent::__construct( $dbuser, $dbpassword, $dbname, $dbhost );
    606549
    607550                // wp-config.php creation will manually connect when ready.
    608551                if ( defined( 'WP_SETUP_CONFIG' ) ) {
     
    632575         * @return mixed The private member
    633576         */
    634577        public function __get( $name ) {
    635                 if ( 'col_info' == $name )
     578                if ( 'col_info' == $name ) {
    636579                        $this->load_col_info();
     580                } elseif ( 'use_mysqli' == $name ) {
     581                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2', "Try wpdb_API::api_call('mysqli_*')." );
     582                        return 'mysqli' == $this->api;
     583                } elseif ( 'has_connected' == $name ) {
     584                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2' );
     585                        return (bool) $this->dbh;
     586                }
    637587
    638588                return $this->$name;
    639589        }
     
    647597         * @param mixed  $value The value to set
    648598         */
    649599        public function __set( $name, $value ) {
     600                if ( 'use_mysqli' == $name ) {
     601                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2', "Try wpdb_API::api_call('mysqli_*')." );
     602                        return;
     603                } elseif ( 'has_connected' == $name ) {
     604                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2' );
     605                        return;
     606                }
    650607                $this->$name = $value;
    651608        }
    652609
     
    660617         * @return bool If the member is set or not
    661618         */
    662619        public function __isset( $name ) {
     620                if ( 'use_mysqli' == $name ) {
     621                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2', "Try wpdb_API::api_call('mysqli_*')." );
     622                        return true;
     623                } elseif ( 'has_connected' == $name ) {
     624                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2' );
     625                        return true;
     626                }
     627
    663628                return isset( $this->$name );
    664629        }
    665630
     
    671636         * @param string $name  The private member to unset
    672637         */
    673638        public function __unset( $name ) {
     639                if ( 'use_mysqli' == $name ) {
     640                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2', "Try wpdb_API::api_call('mysqli_*')." );
     641                        return;
     642                } elseif ( 'has_connected' == $name ) {
     643                        _deprecated_property( __CLASS__ . '::$' . $name, 'X4.2' );
     644                        return;
     645                }
     646
    674647                unset( $this->$name );
    675648        }
    676649
     
    709682                if ( ! isset( $collate ) )
    710683                        $collate = $this->collate;
    711684                if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) {
    712                         if ( $this->use_mysqli ) {
    713                                 if ( function_exists( 'mysqli_set_charset' ) && $this->has_cap( 'set_charset' ) ) {
    714                                         mysqli_set_charset( $dbh, $charset );
    715                                 } else {
    716                                         $query = $this->prepare( 'SET NAMES %s', $charset );
    717                                         if ( ! empty( $collate ) )
    718                                                 $query .= $this->prepare( ' COLLATE %s', $collate );
    719                                         mysqli_query( $query, $dbh );
    720                                 }
     685                        extract( $this->api_vars( 'set_charset' ) ); /** @var $mysqli_set_charset */
     686
     687                        if ( function_exists( $mysqli_set_charset ) && $this->has_cap( 'set_charset' ) ) {
     688                                $this->api_call( 'mysqli_set_charset', $dbh, $charset );
    721689                        } else {
    722                                 if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset' ) ) {
    723                                         mysql_set_charset( $charset, $dbh );
    724                                 } else {
    725                                         $query = $this->prepare( 'SET NAMES %s', $charset );
    726                                         if ( ! empty( $collate ) )
    727                                                 $query .= $this->prepare( ' COLLATE %s', $collate );
    728                                         mysql_query( $query, $dbh );
    729                                 }
     690                                $query = $this->prepare( 'SET NAMES %s', $charset );
     691                                if ( ! empty( $collate ) )
     692                                        $query .= $this->prepare( ' COLLATE %s', $collate );
     693                                $this->api_call( 'mysqli_query', $dbh, $query );
    730694                        }
    731695                }
    732696        }
     
    743707         */
    744708        public function set_sql_mode( $modes = array() ) {
    745709                if ( empty( $modes ) ) {
    746                         if ( $this->use_mysqli ) {
    747                                 $res = mysqli_query( $this->dbh, 'SELECT @@SESSION.sql_mode' );
    748                         } else {
    749                                 $res = mysql_query( 'SELECT @@SESSION.sql_mode', $this->dbh );
    750                         }
    751 
    752                         if ( empty( $res ) ) {
    753                                 return;
    754                         }
    755 
    756                         if ( $this->use_mysqli ) {
    757                                 $modes_array = mysqli_fetch_array( $res );
    758                                 if ( empty( $modes_array[0] ) ) {
    759                                         return;
    760                                 }
    761                                 $modes_str = $modes_array[0];
    762                         } else {
    763                                 $modes_str = mysql_result( $res, 0 );
    764                         }
    765 
    766                         if ( empty( $modes_str ) ) {
    767                                 return;
    768                         }
    769 
    770                         $modes = explode( ',', $modes_str );
     710                        $modes = $this->db_modes();
    771711                }
    772 
    773712                $modes = array_change_key_case( $modes, CASE_UPPER );
    774713
    775714                /**
     
    782721                 * @param array $incompatible_modes An array of incompatible modes.
    783722                 */
    784723                $incompatible_modes = (array) apply_filters( 'incompatible_sql_modes', $this->incompatible_modes );
    785 
    786                 foreach( $modes as $i => $mode ) {
    787                         if ( in_array( $mode, $incompatible_modes ) ) {
    788                                 unset( $modes[ $i ] );
    789                         }
    790                 }
     724                $modes = array_diff($modes, $incompatible_modes);
    791725
    792726                $modes_str = implode( ',', $modes );
    793 
    794                 if ( $this->use_mysqli ) {
    795                         mysqli_query( $this->dbh, "SET SESSION sql_mode='$modes_str'" );
    796                 } else {
    797                         mysql_query( "SET SESSION sql_mode='$modes_str'", $this->dbh );
    798                 }
     727                $this->api_call( 'mysqli_query', $this->dbh, "SET SESSION sql_mode='$modes_str'" );
    799728        }
    800729
    801730        /**
     
    979908                if ( is_null($dbh) )
    980909                        $dbh = $this->dbh;
    981910
    982                 if ( $this->use_mysqli ) {
    983                         $success = @mysqli_select_db( $dbh, $db );
    984                 } else {
    985                         $success = @mysql_select_db( $db, $dbh );
    986                 }
     911                $success = @$this->api_call('mysqli_select_db', $dbh, $db );
    987912                if ( ! $success ) {
    988913                        $this->ready = false;
    989914                        if ( ! did_action( 'template_redirect' ) ) {
     
    1034959         */
    1035960        function _real_escape( $string ) {
    1036961                if ( $this->dbh ) {
    1037                         if ( $this->use_mysqli ) {
    1038                                 return mysqli_real_escape_string( $this->dbh, $string );
    1039                         } else {
    1040                                 return mysql_real_escape_string( $string, $this->dbh );
    1041                         }
     962                        return $this->api_call( 'mysqli_real_escape_string', $this->dbh, $string );
    1042963                }
    1043964
    1044965                $class = get_class( $this );
     
    12161137                global $EZSQL_ERROR;
    12171138
    12181139                if ( !$str ) {
    1219                         if ( $this->use_mysqli ) {
    1220                                 $str = mysqli_error( $this->dbh );
    1221                         } else {
    1222                                 $str = mysql_error( $this->dbh );
    1223                         }
     1140                        $str = $this->api_call( 'mysqli_error', $this->dbh );
    12241141                }
    12251142                $EZSQL_ERROR[] = array( 'query' => $this->last_query, 'error_str' => $str );
    12261143
     
    13251242                $this->last_error  = '';
    13261243
    13271244                if ( is_resource( $this->result ) ) {
    1328                         if ( $this->use_mysqli ) {
    1329                                 mysqli_free_result( $this->result );
    1330                         } else {
    1331                                 mysql_free_result( $this->result );
    1332                         }
     1245                        $this->api_call( 'mysqli_free_result', $this->result );
    13331246                }
    13341247        }
    13351248
     
    13491262
    13501263                $this->is_mysql = true;
    13511264
    1352                 /*
    1353                  * Deprecated in 3.9+ when using MySQLi. No equivalent
    1354                  * $new_link parameter exists for mysqli_* functions.
    1355                  */
    1356                 $new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
    1357                 $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
    1358 
    1359                 if ( $this->use_mysqli ) {
    1360                         $this->dbh = mysqli_init();
    1361 
    1362                         // mysqli_real_connect doesn't support the host param including a port or socket
    1363                         // like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file.
    1364                         $port = null;
    1365                         $socket = null;
    1366                         $host = $this->dbhost;
    1367                         $port_or_socket = strstr( $host, ':' );
    1368                         if ( ! empty( $port_or_socket ) ) {
    1369                                 $host = substr( $host, 0, strpos( $host, ':' ) );
    1370                                 $port_or_socket = substr( $port_or_socket, 1 );
    1371                                 if ( 0 !== strpos( $port_or_socket, '/' ) ) {
    1372                                         $port = intval( $port_or_socket );
    1373                                         $maybe_socket = strstr( $port_or_socket, ':' );
    1374                                         if ( ! empty( $maybe_socket ) ) {
    1375                                                 $socket = substr( $maybe_socket, 1 );
    1376                                         }
    1377                                 } else {
    1378                                         $socket = $port_or_socket;
    1379                                 }
    1380                         }
    1381 
    1382                         if ( WP_DEBUG ) {
    1383                                 mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
    1384                         } else {
    1385                                 @mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
    1386                         }
    1387 
    1388                         if ( $this->dbh->connect_errno ) {
    1389                                 $this->dbh = null;
    1390 
    1391                                 /* It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
    1392                                  *  - We haven't previously connected, and
    1393                                  *  - WP_USE_EXT_MYSQL isn't set to false, and
    1394                                  *  - ext/mysql is loaded.
    1395                                  */
    1396                                 $attempt_fallback = true;
    1397 
    1398                                 if ( $this->has_connected ) {
    1399                                         $attempt_fallback = false;
    1400                                 } else if ( defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL ) {
    1401                                         $attempt_fallback = false;
    1402                                 } else if ( ! function_exists( 'mysql_connect' ) ) {
    1403                                         $attempt_fallback = false;
    1404                                 }
    1405 
    1406                                 if ( $attempt_fallback ) {
    1407                                         $this->use_mysqli = false;
    1408                                         $this->db_connect();
    1409                                 }
    1410                         }
    1411                 } else {
    1412                         if ( WP_DEBUG ) {
    1413                                 $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
    1414                         } else {
    1415                                 $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
    1416                         }
    1417                 }
     1265                $result = parent::db_connect();
    14181266
    14191267                if ( ! $this->dbh && $allow_bail ) {
    14201268                        wp_load_translations_early();
     
    14361284<p>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='https://wordpress.org/support/'>WordPress Support Forums</a>.</p>
    14371285" ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' );
    14381286
    1439                         return false;
    14401287                } else if ( $this->dbh ) {
    1441                         $this->has_connected = true;
     1288
    14421289                        $this->set_charset( $this->dbh );
    14431290                        $this->set_sql_mode();
    14441291                        $this->ready = true;
    14451292                        $this->select( $this->dbname, $this->dbh );
    14461293
    1447                         return true;
    14481294                }
    14491295
    1450                 return false;
     1296                return $result;
    14511297        }
    14521298
    14531299        /**
     
    14651311         * @return bool True if the connection is up.
    14661312         */
    14671313        public function check_connection( $allow_bail = true ) {
    1468                 if ( $this->use_mysqli ) {
    1469                         if ( @mysqli_ping( $this->dbh ) ) {
    1470                                 return true;
    1471                         }
    1472                 } else {
    1473                         if ( @mysql_ping( $this->dbh ) ) {
    1474                                 return true;
    1475                         }
     1314                if ( @$this->api_call( 'mysqli_ping', $this->dbh ) ) {
     1315                        return true;
    14761316                }
    14771317
    14781318                $error_reporting = false;
     
    15271367        }
    15281368
    15291369        /**
     1370         * Called right before issuing the query, can be used when apply_filters( 'query', $query ) does not work.
     1371         *
     1372         * @since X4.2
     1373         * @param string $query
     1374         * @return string mixed
     1375         */
     1376        protected function filter_query($query) {
     1377                return $query;
     1378        }
     1379
     1380        /**
     1381         * Called on each results row right after fetching it.
     1382         *
     1383         * @since X4.2
     1384         * @param object $row
     1385         * @return object mixed
     1386         */
     1387        protected function filter_row($row) {
     1388                return $row;
     1389        }
     1390
     1391        /**
    15301392         * Perform a MySQL database query, using current database connection.
    15311393         *
    15321394         * More information can be found on the codex page.
     
    15401402                if ( ! $this->ready )
    15411403                        return false;
    15421404
     1405                extract( $this->api_vars( 'errno', 'error', 'affected_rows', 'insert_id', 'fetch_object' ) );
     1406                /**
     1407                 * @var $mysqli_errno
     1408                 * @var $mysqli_error
     1409                 * @var $mysqli_affected_rows
     1410                 * @var $mysqli_insert_id
     1411                 * @var $mysqli_fetch_object'
     1412                 */
     1413
    15431414                /**
    15441415                 * Filter the database query.
    15451416                 *
     
    15511422                 * @param string $query Database query.
    15521423                 */
    15531424                $query = apply_filters( 'query', $query );
     1425                $query = $this->filter_query($query);
    15541426
    15551427                $this->flush();
    15561428
     
    15631435                $this->_do_query( $query );
    15641436
    15651437                // MySQL server has gone away, try to reconnect
    1566                 $mysql_errno = 0;
     1438                $errno = 0;
    15671439                if ( ! empty( $this->dbh ) ) {
    1568                         if ( $this->use_mysqli ) {
    1569                                 $mysql_errno = mysqli_errno( $this->dbh );
    1570                         } else {
    1571                                 $mysql_errno = mysql_errno( $this->dbh );
    1572                         }
     1440                        $errno = $mysqli_errno( $this->dbh );
    15731441                }
    15741442
    1575                 if ( empty( $this->dbh ) || 2006 == $mysql_errno ) {
     1443                if ( empty( $this->dbh ) || 2006 == $errno ) {
    15761444                        if ( $this->check_connection() ) {
    15771445                                $this->_do_query( $query );
    15781446                        } else {
     
    15821450                }
    15831451
    15841452                // If there is an error then take note of it..
    1585                 if ( $this->use_mysqli ) {
    1586                         $this->last_error = mysqli_error( $this->dbh );
    1587                 } else {
    1588                         $this->last_error = mysql_error( $this->dbh );
    1589                 }
     1453                $this->last_error = $mysqli_error( $this->dbh );
    15901454
    15911455                if ( $this->last_error ) {
    15921456                        // Clear insert_id on a subsequent failed insert.
     
    16001464                if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) {
    16011465                        $return_val = $this->result;
    16021466                } elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) {
    1603                         if ( $this->use_mysqli ) {
    1604                                 $this->rows_affected = mysqli_affected_rows( $this->dbh );
    1605                         } else {
    1606                                 $this->rows_affected = mysql_affected_rows( $this->dbh );
    1607                         }
     1467                        $this->rows_affected = $mysqli_affected_rows( $this->dbh );
    16081468                        // Take note of the insert_id
    16091469                        if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
    1610                                 if ( $this->use_mysqli ) {
    1611                                         $this->insert_id = mysqli_insert_id( $this->dbh );
    1612                                 } else {
    1613                                         $this->insert_id = mysql_insert_id( $this->dbh );
    1614                                 }
     1470                                $this->insert_id = $mysqli_insert_id( $this->dbh );
    16151471                        }
    16161472                        // Return number of rows affected
    16171473                        $return_val = $this->rows_affected;
    16181474                } else {
    16191475                        $num_rows = 0;
    1620                         if ( $this->use_mysqli ) {
    1621                                 while ( $row = @mysqli_fetch_object( $this->result ) ) {
    1622                                         $this->last_result[$num_rows] = $row;
    1623                                         $num_rows++;
    1624                                 }
    1625                         } else {
    1626                                 while ( $row = @mysql_fetch_object( $this->result ) ) {
    1627                                         $this->last_result[$num_rows] = $row;
    1628                                         $num_rows++;
    1629                                 }
     1476                        while ( $row = @$mysqli_fetch_object( $this->result ) ) {
     1477                                $row = $this->filter_row($row);
     1478                                $this->last_result[$num_rows] = $row;
     1479                                $num_rows++;
    16301480                        }
    16311481
    16321482                        // Log number of rows the query returned
     
    16531503                        $this->timer_start();
    16541504                }
    16551505
    1656                 if ( $this->use_mysqli ) {
    1657                         $this->result = @mysqli_query( $this->dbh, $query );
    1658                 } else {
    1659                         $this->result = @mysql_query( $query, $this->dbh );
    1660                 }
     1506                $this->result = @$this->api_call( 'mysqli_query', $this->dbh, $query );
    16611507                $this->num_queries++;
    16621508
    16631509                if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
     
    20011847                if ( $this->col_info )
    20021848                        return;
    20031849
    2004                 if ( $this->use_mysqli ) {
    2005                         for ( $i = 0; $i < @mysqli_num_fields( $this->result ); $i++ ) {
    2006                                 $this->col_info[ $i ] = @mysqli_fetch_field( $this->result );
    2007                         }
    2008                 } else {
    2009                         for ( $i = 0; $i < @mysql_num_fields( $this->result ); $i++ ) {
    2010                                 $this->col_info[ $i ] = @mysql_fetch_field( $this->result, $i );
    2011                         }
     1850                extract( $this->api_vars( 'num_fields', 'fetch_field' ) ); /** @var $mysqli_num_fields *//** @var $mysqli_fetch_field */
     1851
     1852                for ( $i = 0; $i < @$mysqli_num_fields( $this->result ); $i++ ) {
     1853                        $this->col_info[ $i ] = @$mysqli_fetch_field( $this->result ); // no need to use field_offset here!
    20121854                }
    20131855        }
    20141856
     
    21802022         * @return false|string false on failure, version number on success
    21812023         */
    21822024        public function db_version() {
    2183                 if ( $this->use_mysqli ) {
    2184                         $server_info = mysqli_get_server_info( $this->dbh );
    2185                 } else {
    2186                         $server_info = mysql_get_server_info( $this->dbh );
    2187                 }
     2025                $server_info = $this->api_call( 'mysqli_get_server_info', $this->dbh );
    21882026                return preg_replace( '/[^0-9.].*/', '', $server_info );
    21892027        }
    21902028}
  • tests/phpunit/tests/db.php

     
    5050                $var = $wpdb->get_var( "SELECT ID FROM $wpdb->users LIMIT 1" );
    5151                $this->assertGreaterThan( 0, $var );
    5252
    53                 if ( $wpdb->use_mysqli ) {
    54                         mysqli_close( $wpdb->dbh );
    55                 } else {
    56                         mysql_close( $wpdb->dbh );
    57                 }
     53                $wpdb->api_call( 'close', $wpdb->dbh );
    5854                unset( $wpdb->dbh );
    5955
    6056                $var = $wpdb->get_var( "SELECT ID FROM $wpdb->users LIMIT 1" );
     
    145141         * @dataProvider data_like_query
    146142         * @param $data string The haystack, raw.
    147143         * @param $like string The like phrase, raw.
    148         * @param $result string The expected comparison result; '1' = true, '0' = false
     144        * @param $result string The expected comparison result; '1' = true, '0' = false
    149145         */
    150146        function test_like_query( $data, $like, $result ) {
    151147                global $wpdb;
     
    458454                $this->assertEquals( $expected2, $wpdb->last_query );
    459455                $wpdb->suppress_errors( $suppress );
    460456        }
     457
     458        /**
     459         * @expectedDeprecated wpdb::$use_mysqli
     460         * @expectedDeprecated wpdb::$has_connected
     461         *
     462         * @ticket
     463         */
     464        function test_deprecated_properties() {
     465                global $wpdb;
     466
     467                $before = $wpdb->use_mysqli;
     468                $wpdb->use_mysqli = ! $before;
     469                $after = $wpdb->use_mysqli;
     470                $this->assertEquals( $before, $after );  // readonly
     471
     472                $before = $wpdb->has_connected;
     473                $wpdb->has_connected = ! $before;
     474                $after = $wpdb->has_connected;
     475                $this->assertEquals( $before, $after );  // readonly
     476        }
    461477}