Ticket #30006: extract_wpdb_API.patch
File extract_wpdb_API.patch, 30.7 KB (added by , 11 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 */ 25 class 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
1 1 <?php 2 3 require_once( ABSPATH . WPINC . '/wp-db-api.php' ); 4 2 5 /** 3 6 * WordPress DB Class 4 7 * … … 49 52 * @subpackage Database 50 53 * @since 0.71 51 54 */ 52 class wpdb {55 class wpdb extends wpdb_API { 53 56 54 57 /** 55 58 * Whether to show SQL/DB errors. … … 465 468 public $collate; 466 469 467 470 /** 468 * Database Username469 *470 * @since 2.9.0471 * @access protected472 * @var string473 */474 protected $dbuser;475 476 /**477 * Database Password478 *479 * @since 3.1.0480 * @access protected481 * @var string482 */483 protected $dbpassword;484 485 /**486 * Database Name487 *488 * @since 3.1.0489 * @access protected490 * @var string491 */492 protected $dbname;493 494 /**495 * Database Host496 *497 * @since 3.1.0498 * @access protected499 * @var string500 */501 protected $dbhost;502 503 /**504 * Database Handle505 *506 * @since 0.71507 * @access protected508 * @var string509 */510 protected $dbh;511 512 /**513 471 * A textual description of the last query/get_row/get_var call 514 472 * 515 473 * @since 3.0.0 … … 545 503 /** 546 504 * Whether to use mysqli over mysql. 547 505 * 506 * @deprecated 507 * @property-read 548 508 * @since 3.9.0 549 509 * @access private 550 510 * @var bool … … 554 514 /** 555 515 * Whether we've managed to successfully connect at some point 556 516 * 517 * @deprecated 518 * @property-read 557 519 * @since 3.9.0 558 520 * @access private 559 521 * @var bool … … 581 543 if ( WP_DEBUG && WP_DEBUG_DISPLAY ) 582 544 $this->show_errors(); 583 545 584 /* Use ext/mysqli if it exists and:585 * - WP_USE_EXT_MYSQL is defined as false, or586 * - We are a development version of WordPress, or587 * - We are running PHP 5.5 or greater, or588 * - 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 600 546 $this->init_charset(); 601 547 602 $this->dbuser = $dbuser; 603 $this->dbpassword = $dbpassword; 604 $this->dbname = $dbname; 605 $this->dbhost = $dbhost; 548 parent::__construct( $dbuser, $dbpassword, $dbname, $dbhost ); 606 549 607 550 // wp-config.php creation will manually connect when ready. 608 551 if ( defined( 'WP_SETUP_CONFIG' ) ) { … … 632 575 * @return mixed The private member 633 576 */ 634 577 public function __get( $name ) { 635 if ( 'col_info' == $name ) 578 if ( 'col_info' == $name ) { 636 579 $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 } 637 587 638 588 return $this->$name; 639 589 } … … 647 597 * @param mixed $value The value to set 648 598 */ 649 599 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 } 650 607 $this->$name = $value; 651 608 } 652 609 … … 660 617 * @return bool If the member is set or not 661 618 */ 662 619 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 663 628 return isset( $this->$name ); 664 629 } 665 630 … … 671 636 * @param string $name The private member to unset 672 637 */ 673 638 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 674 647 unset( $this->$name ); 675 648 } 676 649 … … 709 682 if ( ! isset( $collate ) ) 710 683 $collate = $this->collate; 711 684 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 ); 721 689 } 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 ); 730 694 } 731 695 } 732 696 } … … 743 707 */ 744 708 public function set_sql_mode( $modes = array() ) { 745 709 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(); 771 711 } 772 773 712 $modes = array_change_key_case( $modes, CASE_UPPER ); 774 713 775 714 /** … … 782 721 * @param array $incompatible_modes An array of incompatible modes. 783 722 */ 784 723 $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); 791 725 792 726 $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'" ); 799 728 } 800 729 801 730 /** … … 979 908 if ( is_null($dbh) ) 980 909 $dbh = $this->dbh; 981 910 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 ); 987 912 if ( ! $success ) { 988 913 $this->ready = false; 989 914 if ( ! did_action( 'template_redirect' ) ) { … … 1034 959 */ 1035 960 function _real_escape( $string ) { 1036 961 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 ); 1042 963 } 1043 964 1044 965 $class = get_class( $this ); … … 1216 1137 global $EZSQL_ERROR; 1217 1138 1218 1139 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 ); 1224 1141 } 1225 1142 $EZSQL_ERROR[] = array( 'query' => $this->last_query, 'error_str' => $str ); 1226 1143 … … 1325 1242 $this->last_error = ''; 1326 1243 1327 1244 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 ); 1333 1246 } 1334 1247 } 1335 1248 … … 1349 1262 1350 1263 $this->is_mysql = true; 1351 1264 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(); 1418 1266 1419 1267 if ( ! $this->dbh && $allow_bail ) { 1420 1268 wp_load_translations_early(); … … 1436 1284 <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> 1437 1285 " ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' ); 1438 1286 1439 return false;1440 1287 } else if ( $this->dbh ) { 1441 $this->has_connected = true; 1288 1442 1289 $this->set_charset( $this->dbh ); 1443 1290 $this->set_sql_mode(); 1444 1291 $this->ready = true; 1445 1292 $this->select( $this->dbname, $this->dbh ); 1446 1293 1447 return true;1448 1294 } 1449 1295 1450 return false;1296 return $result; 1451 1297 } 1452 1298 1453 1299 /** … … 1465 1311 * @return bool True if the connection is up. 1466 1312 */ 1467 1313 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; 1476 1316 } 1477 1317 1478 1318 $error_reporting = false; … … 1527 1367 } 1528 1368 1529 1369 /** 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 /** 1530 1392 * Perform a MySQL database query, using current database connection. 1531 1393 * 1532 1394 * More information can be found on the codex page. … … 1540 1402 if ( ! $this->ready ) 1541 1403 return false; 1542 1404 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 1543 1414 /** 1544 1415 * Filter the database query. 1545 1416 * … … 1551 1422 * @param string $query Database query. 1552 1423 */ 1553 1424 $query = apply_filters( 'query', $query ); 1425 $query = $this->filter_query($query); 1554 1426 1555 1427 $this->flush(); 1556 1428 … … 1563 1435 $this->_do_query( $query ); 1564 1436 1565 1437 // MySQL server has gone away, try to reconnect 1566 $ mysql_errno = 0;1438 $errno = 0; 1567 1439 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 ); 1573 1441 } 1574 1442 1575 if ( empty( $this->dbh ) || 2006 == $ mysql_errno ) {1443 if ( empty( $this->dbh ) || 2006 == $errno ) { 1576 1444 if ( $this->check_connection() ) { 1577 1445 $this->_do_query( $query ); 1578 1446 } else { … … 1582 1450 } 1583 1451 1584 1452 // 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 ); 1590 1454 1591 1455 if ( $this->last_error ) { 1592 1456 // Clear insert_id on a subsequent failed insert. … … 1600 1464 if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) { 1601 1465 $return_val = $this->result; 1602 1466 } 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 ); 1608 1468 // Take note of the insert_id 1609 1469 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 ); 1615 1471 } 1616 1472 // Return number of rows affected 1617 1473 $return_val = $this->rows_affected; 1618 1474 } else { 1619 1475 $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++; 1630 1480 } 1631 1481 1632 1482 // Log number of rows the query returned … … 1653 1503 $this->timer_start(); 1654 1504 } 1655 1505 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 ); 1661 1507 $this->num_queries++; 1662 1508 1663 1509 if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { … … 2001 1847 if ( $this->col_info ) 2002 1848 return; 2003 1849 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! 2012 1854 } 2013 1855 } 2014 1856 … … 2180 2022 * @return false|string false on failure, version number on success 2181 2023 */ 2182 2024 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 ); 2188 2026 return preg_replace( '/[^0-9.].*/', '', $server_info ); 2189 2027 } 2190 2028 } -
tests/phpunit/tests/db.php
50 50 $var = $wpdb->get_var( "SELECT ID FROM $wpdb->users LIMIT 1" ); 51 51 $this->assertGreaterThan( 0, $var ); 52 52 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 ); 58 54 unset( $wpdb->dbh ); 59 55 60 56 $var = $wpdb->get_var( "SELECT ID FROM $wpdb->users LIMIT 1" ); … … 145 141 * @dataProvider data_like_query 146 142 * @param $data string The haystack, raw. 147 143 * @param $like string The like phrase, raw. 148 144 * @param $result string The expected comparison result; '1' = true, '0' = false 149 145 */ 150 146 function test_like_query( $data, $like, $result ) { 151 147 global $wpdb; … … 458 454 $this->assertEquals( $expected2, $wpdb->last_query ); 459 455 $wpdb->suppress_errors( $suppress ); 460 456 } 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 } 461 477 }