[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> wp-db.php (source)

   1  <?php
   2  /**
   3   * WordPress DB Class
   4   *
   5   * Original code from {@link http://php.justinvincent.com Justin Vincent (justin@visunet.ie)}
   6   *
   7   * @package WordPress
   8   * @subpackage Database
   9   * @since 0.71
  10   */
  11  
  12  /**
  13   * @since 0.71
  14   */
  15  define( 'EZSQL_VERSION', 'WP1.25' );
  16  
  17  /**
  18   * @since 0.71
  19   */
  20  define( 'OBJECT', 'OBJECT' );
  21  // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase
  22  define( 'object', 'OBJECT' ); // Back compat.
  23  
  24  /**
  25   * @since 2.5.0
  26   */
  27  define( 'OBJECT_K', 'OBJECT_K' );
  28  
  29  /**
  30   * @since 0.71
  31   */
  32  define( 'ARRAY_A', 'ARRAY_A' );
  33  
  34  /**
  35   * @since 0.71
  36   */
  37  define( 'ARRAY_N', 'ARRAY_N' );
  38  
  39  /**
  40   * WordPress Database Access Abstraction Object
  41   *
  42   * It is possible to replace this class with your own
  43   * by setting the $wpdb global variable in wp-content/db.php
  44   * file to your class. The wpdb class will still be included,
  45   * so you can extend it or simply use your own.
  46   *
  47   * @link https://codex.wordpress.org/Function_Reference/wpdb_Class
  48   *
  49   * @since 0.71
  50   */
  51  class wpdb {
  52  
  53      /**
  54       * Whether to show SQL/DB errors.
  55       *
  56       * Default behavior is to show errors if both WP_DEBUG and WP_DEBUG_DISPLAY
  57       * evaluated to true.
  58       *
  59       * @since 0.71
  60       * @var bool
  61       */
  62      var $show_errors = false;
  63  
  64      /**
  65       * Whether to suppress errors during the DB bootstrapping.
  66       *
  67       * @since 2.5.0
  68       * @var bool
  69       */
  70      var $suppress_errors = false;
  71  
  72      /**
  73       * The last error during query.
  74       *
  75       * @since 2.5.0
  76       * @var string
  77       */
  78      public $last_error = '';
  79  
  80      /**
  81       * Amount of queries made
  82       *
  83       * @since 1.2.0
  84       * @var int
  85       */
  86      public $num_queries = 0;
  87  
  88      /**
  89       * Count of rows returned by previous query
  90       *
  91       * @since 0.71
  92       * @var int
  93       */
  94      public $num_rows = 0;
  95  
  96      /**
  97       * Count of affected rows by previous query
  98       *
  99       * @since 0.71
 100       * @var int
 101       */
 102      var $rows_affected = 0;
 103  
 104      /**
 105       * The ID generated for an AUTO_INCREMENT column by the previous query (usually INSERT).
 106       *
 107       * @since 0.71
 108       * @var int
 109       */
 110      public $insert_id = 0;
 111  
 112      /**
 113       * Last query made
 114       *
 115       * @since 0.71
 116       * @var string
 117       */
 118      var $last_query;
 119  
 120      /**
 121       * Results of the last query made
 122       *
 123       * @since 0.71
 124       * @var array|null
 125       */
 126      var $last_result;
 127  
 128      /**
 129       * MySQL result, which is either a resource or boolean.
 130       *
 131       * @since 0.71
 132       * @var mixed
 133       */
 134      protected $result;
 135  
 136      /**
 137       * Cached column info, for sanity checking data before inserting
 138       *
 139       * @since 4.2.0
 140       * @var array
 141       */
 142      protected $col_meta = array();
 143  
 144      /**
 145       * Calculated character sets on tables
 146       *
 147       * @since 4.2.0
 148       * @var array
 149       */
 150      protected $table_charset = array();
 151  
 152      /**
 153       * Whether text fields in the current query need to be sanity checked.
 154       *
 155       * @since 4.2.0
 156       * @var bool
 157       */
 158      protected $check_current_query = true;
 159  
 160      /**
 161       * Flag to ensure we don't run into recursion problems when checking the collation.
 162       *
 163       * @since 4.2.0
 164       * @see wpdb::check_safe_collation()
 165       * @var bool
 166       */
 167      private $checking_collation = false;
 168  
 169      /**
 170       * Saved info on the table column
 171       *
 172       * @since 0.71
 173       * @var array
 174       */
 175      protected $col_info;
 176  
 177      /**
 178       * Log of queries that were executed, for debugging purposes.
 179       *
 180       * @since 1.5.0
 181       * @since 2.5.0 The third element in each query log was added to record the calling functions.
 182       * @since 5.1.0 The fourth element in each query log was added to record the start time.
 183       * @since 5.3.0 The fifth element in each query log was added to record custom data.
 184       *
 185       * @var array[] {
 186       *     Array of queries that were executed.
 187       *
 188       *     @type array ...$0 {
 189       *         Data for each query.
 190       *
 191       *         @type string $0 The query's SQL.
 192       *         @type float  $1 Total time spent on the query, in seconds.
 193       *         @type string $2 Comma separated list of the calling functions.
 194       *         @type float  $3 Unix timestamp of the time at the start of the query.
 195       *         @type array  $4 Custom query data.
 196       *     }
 197       * }
 198       */
 199      var $queries;
 200  
 201      /**
 202       * The number of times to retry reconnecting before dying.
 203       *
 204       * @since 3.9.0
 205       * @see wpdb::check_connection()
 206       * @var int
 207       */
 208      protected $reconnect_retries = 5;
 209  
 210      /**
 211       * WordPress table prefix
 212       *
 213       * You can set this to have multiple WordPress installations
 214       * in a single database. The second reason is for possible
 215       * security precautions.
 216       *
 217       * @since 2.5.0
 218       * @var string
 219       */
 220      public $prefix = '';
 221  
 222      /**
 223       * WordPress base table prefix.
 224       *
 225       * @since 3.0.0
 226       * @var string
 227       */
 228      public $base_prefix;
 229  
 230      /**
 231       * Whether the database queries are ready to start executing.
 232       *
 233       * @since 2.3.2
 234       * @var bool
 235       */
 236      var $ready = false;
 237  
 238      /**
 239       * Blog ID.
 240       *
 241       * @since 3.0.0
 242       * @var int
 243       */
 244      public $blogid = 0;
 245  
 246      /**
 247       * Site ID.
 248       *
 249       * @since 3.0.0
 250       * @var int
 251       */
 252      public $siteid = 0;
 253  
 254      /**
 255       * List of WordPress per-blog tables
 256       *
 257       * @since 2.5.0
 258       * @see wpdb::tables()
 259       * @var array
 260       */
 261      var $tables = array(
 262          'posts',
 263          'comments',
 264          'links',
 265          'options',
 266          'postmeta',
 267          'terms',
 268          'term_taxonomy',
 269          'term_relationships',
 270          'termmeta',
 271          'commentmeta',
 272      );
 273  
 274      /**
 275       * List of deprecated WordPress tables
 276       *
 277       * categories, post2cat, and link2cat were deprecated in 2.3.0, db version 5539
 278       *
 279       * @since 2.9.0
 280       * @see wpdb::tables()
 281       * @var array
 282       */
 283      var $old_tables = array( 'categories', 'post2cat', 'link2cat' );
 284  
 285      /**
 286       * List of WordPress global tables
 287       *
 288       * @since 3.0.0
 289       * @see wpdb::tables()
 290       * @var array
 291       */
 292      var $global_tables = array( 'users', 'usermeta' );
 293  
 294      /**
 295       * List of Multisite global tables
 296       *
 297       * @since 3.0.0
 298       * @see wpdb::tables()
 299       * @var array
 300       */
 301      var $ms_global_tables = array(
 302          'blogs',
 303          'blogmeta',
 304          'signups',
 305          'site',
 306          'sitemeta',
 307          'sitecategories',
 308          'registration_log',
 309          'blog_versions',
 310      );
 311  
 312      /**
 313       * WordPress Comments table
 314       *
 315       * @since 1.5.0
 316       * @var string
 317       */
 318      public $comments;
 319  
 320      /**
 321       * WordPress Comment Metadata table
 322       *
 323       * @since 2.9.0
 324       * @var string
 325       */
 326      public $commentmeta;
 327  
 328      /**
 329       * WordPress Links table
 330       *
 331       * @since 1.5.0
 332       * @var string
 333       */
 334      public $links;
 335  
 336      /**
 337       * WordPress Options table
 338       *
 339       * @since 1.5.0
 340       * @var string
 341       */
 342      public $options;
 343  
 344      /**
 345       * WordPress Post Metadata table
 346       *
 347       * @since 1.5.0
 348       * @var string
 349       */
 350      public $postmeta;
 351  
 352      /**
 353       * WordPress Posts table
 354       *
 355       * @since 1.5.0
 356       * @var string
 357       */
 358      public $posts;
 359  
 360      /**
 361       * WordPress Terms table
 362       *
 363       * @since 2.3.0
 364       * @var string
 365       */
 366      public $terms;
 367  
 368      /**
 369       * WordPress Term Relationships table
 370       *
 371       * @since 2.3.0
 372       * @var string
 373       */
 374      public $term_relationships;
 375  
 376      /**
 377       * WordPress Term Taxonomy table
 378       *
 379       * @since 2.3.0
 380       * @var string
 381       */
 382      public $term_taxonomy;
 383  
 384      /**
 385       * WordPress Term Meta table.
 386       *
 387       * @since 4.4.0
 388       * @var string
 389       */
 390      public $termmeta;
 391  
 392      //
 393      // Global and Multisite tables
 394      //
 395  
 396      /**
 397       * WordPress User Metadata table
 398       *
 399       * @since 2.3.0
 400       * @var string
 401       */
 402      public $usermeta;
 403  
 404      /**
 405       * WordPress Users table
 406       *
 407       * @since 1.5.0
 408       * @var string
 409       */
 410      public $users;
 411  
 412      /**
 413       * Multisite Blogs table
 414       *
 415       * @since 3.0.0
 416       * @var string
 417       */
 418      public $blogs;
 419  
 420      /**
 421       * Multisite Blog Metadata table
 422       *
 423       * @since 5.1.0
 424       * @var string
 425       */
 426      public $blogmeta;
 427  
 428      /**
 429       * Multisite Blog Versions table
 430       *
 431       * @since 3.0.0
 432       * @var string
 433       */
 434      public $blog_versions;
 435  
 436      /**
 437       * Multisite Registration Log table
 438       *
 439       * @since 3.0.0
 440       * @var string
 441       */
 442      public $registration_log;
 443  
 444      /**
 445       * Multisite Signups table
 446       *
 447       * @since 3.0.0
 448       * @var string
 449       */
 450      public $signups;
 451  
 452      /**
 453       * Multisite Sites table
 454       *
 455       * @since 3.0.0
 456       * @var string
 457       */
 458      public $site;
 459  
 460      /**
 461       * Multisite Sitewide Terms table
 462       *
 463       * @since 3.0.0
 464       * @var string
 465       */
 466      public $sitecategories;
 467  
 468      /**
 469       * Multisite Site Metadata table
 470       *
 471       * @since 3.0.0
 472       * @var string
 473       */
 474      public $sitemeta;
 475  
 476      /**
 477       * Format specifiers for DB columns. Columns not listed here default to %s. Initialized during WP load.
 478       *
 479       * Keys are column names, values are format types: 'ID' => '%d'
 480       *
 481       * @since 2.8.0
 482       * @see wpdb::prepare()
 483       * @see wpdb::insert()
 484       * @see wpdb::update()
 485       * @see wpdb::delete()
 486       * @see wp_set_wpdb_vars()
 487       * @var array
 488       */
 489      public $field_types = array();
 490  
 491      /**
 492       * Database table columns charset
 493       *
 494       * @since 2.2.0
 495       * @var string
 496       */
 497      public $charset;
 498  
 499      /**
 500       * Database table columns collate
 501       *
 502       * @since 2.2.0
 503       * @var string
 504       */
 505      public $collate;
 506  
 507      /**
 508       * Database Username
 509       *
 510       * @since 2.9.0
 511       * @var string
 512       */
 513      protected $dbuser;
 514  
 515      /**
 516       * Database Password
 517       *
 518       * @since 3.1.0
 519       * @var string
 520       */
 521      protected $dbpassword;
 522  
 523      /**
 524       * Database Name
 525       *
 526       * @since 3.1.0
 527       * @var string
 528       */
 529      protected $dbname;
 530  
 531      /**
 532       * Database Host
 533       *
 534       * @since 3.1.0
 535       * @var string
 536       */
 537      protected $dbhost;
 538  
 539      /**
 540       * Database Handle
 541       *
 542       * @since 0.71
 543       * @var string
 544       */
 545      protected $dbh;
 546  
 547      /**
 548       * A textual description of the last query/get_row/get_var call
 549       *
 550       * @since 3.0.0
 551       * @var string
 552       */
 553      public $func_call;
 554  
 555      /**
 556       * Whether MySQL is used as the database engine.
 557       *
 558       * Set in WPDB::db_connect() to true, by default. This is used when checking
 559       * against the required MySQL version for WordPress. Normally, a replacement
 560       * database drop-in (db.php) will skip these checks, but setting this to true
 561       * will force the checks to occur.
 562       *
 563       * @since 3.3.0
 564       * @var bool
 565       */
 566      public $is_mysql = null;
 567  
 568      /**
 569       * A list of incompatible SQL modes.
 570       *
 571       * @since 3.9.0
 572       * @var array
 573       */
 574      protected $incompatible_modes = array(
 575          'NO_ZERO_DATE',
 576          'ONLY_FULL_GROUP_BY',
 577          'STRICT_TRANS_TABLES',
 578          'STRICT_ALL_TABLES',
 579          'TRADITIONAL',
 580      );
 581  
 582      /**
 583       * Whether to use mysqli over mysql.
 584       *
 585       * @since 3.9.0
 586       * @var bool
 587       */
 588      private $use_mysqli = false;
 589  
 590      /**
 591       * Whether we've managed to successfully connect at some point
 592       *
 593       * @since 3.9.0
 594       * @var bool
 595       */
 596      private $has_connected = false;
 597  
 598      /**
 599       * Connects to the database server and selects a database
 600       *
 601       * PHP5 style constructor for compatibility with PHP5. Does
 602       * the actual setting up of the class properties and connection
 603       * to the database.
 604       *
 605       * @link https://core.trac.wordpress.org/ticket/3354
 606       * @since 2.0.8
 607       *
 608       * @global string $wp_version
 609       *
 610       * @param string $dbuser     MySQL database user
 611       * @param string $dbpassword MySQL database password
 612       * @param string $dbname     MySQL database name
 613       * @param string $dbhost     MySQL database host
 614       */
 615  	public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
 616          register_shutdown_function( array( $this, '__destruct' ) );
 617  
 618          if ( WP_DEBUG && WP_DEBUG_DISPLAY ) {
 619              $this->show_errors();
 620          }
 621  
 622          // Use ext/mysqli if it exists unless WP_USE_EXT_MYSQL is defined as true
 623          if ( function_exists( 'mysqli_connect' ) ) {
 624              $this->use_mysqli = true;
 625  
 626              if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
 627                  $this->use_mysqli = ! WP_USE_EXT_MYSQL;
 628              }
 629          }
 630  
 631          $this->dbuser     = $dbuser;
 632          $this->dbpassword = $dbpassword;
 633          $this->dbname     = $dbname;
 634          $this->dbhost     = $dbhost;
 635  
 636          // wp-config.php creation will manually connect when ready.
 637          if ( defined( 'WP_SETUP_CONFIG' ) ) {
 638              return;
 639          }
 640  
 641          $this->db_connect();
 642      }
 643  
 644      /**
 645       * PHP5 style destructor and will run when database object is destroyed.
 646       *
 647       * @see wpdb::__construct()
 648       * @since 2.0.8
 649       * @return true
 650       */
 651  	public function __destruct() {
 652          return true;
 653      }
 654  
 655      /**
 656       * Makes private properties readable for backward compatibility.
 657       *
 658       * @since 3.5.0
 659       *
 660       * @param string $name The private member to get, and optionally process
 661       * @return mixed The private member
 662       */
 663  	public function __get( $name ) {
 664          if ( 'col_info' === $name ) {
 665              $this->load_col_info();
 666          }
 667  
 668          return $this->$name;
 669      }
 670  
 671      /**
 672       * Makes private properties settable for backward compatibility.
 673       *
 674       * @since 3.5.0
 675       *
 676       * @param string $name  The private member to set
 677       * @param mixed  $value The value to set
 678       */
 679  	public function __set( $name, $value ) {
 680          $protected_members = array(
 681              'col_meta',
 682              'table_charset',
 683              'check_current_query',
 684          );
 685          if ( in_array( $name, $protected_members, true ) ) {
 686              return;
 687          }
 688          $this->$name = $value;
 689      }
 690  
 691      /**
 692       * Makes private properties check-able for backward compatibility.
 693       *
 694       * @since 3.5.0
 695       *
 696       * @param string $name  The private member to check
 697       *
 698       * @return bool If the member is set or not
 699       */
 700  	public function __isset( $name ) {
 701          return isset( $this->$name );
 702      }
 703  
 704      /**
 705       * Makes private properties un-settable for backward compatibility.
 706       *
 707       * @since 3.5.0
 708       *
 709       * @param string $name  The private member to unset
 710       */
 711  	public function __unset( $name ) {
 712          unset( $this->$name );
 713      }
 714  
 715      /**
 716       * Set $this->charset and $this->collate
 717       *
 718       * @since 3.1.0
 719       */
 720  	public function init_charset() {
 721          $charset = '';
 722          $collate = '';
 723  
 724          if ( function_exists( 'is_multisite' ) && is_multisite() ) {
 725              $charset = 'utf8';
 726              if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) {
 727                  $collate = DB_COLLATE;
 728              } else {
 729                  $collate = 'utf8_general_ci';
 730              }
 731          } elseif ( defined( 'DB_COLLATE' ) ) {
 732              $collate = DB_COLLATE;
 733          }
 734  
 735          if ( defined( 'DB_CHARSET' ) ) {
 736              $charset = DB_CHARSET;
 737          }
 738  
 739          $charset_collate = $this->determine_charset( $charset, $collate );
 740  
 741          $this->charset = $charset_collate['charset'];
 742          $this->collate = $charset_collate['collate'];
 743      }
 744  
 745      /**
 746       * Determines the best charset and collation to use given a charset and collation.
 747       *
 748       * For example, when able, utf8mb4 should be used instead of utf8.
 749       *
 750       * @since 4.6.0
 751       *
 752       * @param string $charset The character set to check.
 753       * @param string $collate The collation to check.
 754       * @return array The most appropriate character set and collation to use.
 755       */
 756  	public function determine_charset( $charset, $collate ) {
 757          if ( ( $this->use_mysqli && ! ( $this->dbh instanceof mysqli ) ) || empty( $this->dbh ) ) {
 758              return compact( 'charset', 'collate' );
 759          }
 760  
 761          if ( 'utf8' === $charset && $this->has_cap( 'utf8mb4' ) ) {
 762              $charset = 'utf8mb4';
 763          }
 764  
 765          if ( 'utf8mb4' === $charset && ! $this->has_cap( 'utf8mb4' ) ) {
 766              $charset = 'utf8';
 767              $collate = str_replace( 'utf8mb4_', 'utf8_', $collate );
 768          }
 769  
 770          if ( 'utf8mb4' === $charset ) {
 771              // _general_ is outdated, so we can upgrade it to _unicode_, instead.
 772              if ( ! $collate || 'utf8_general_ci' === $collate ) {
 773                  $collate = 'utf8mb4_unicode_ci';
 774              } else {
 775                  $collate = str_replace( 'utf8_', 'utf8mb4_', $collate );
 776              }
 777          }
 778  
 779          // _unicode_520_ is a better collation, we should use that when it's available.
 780          if ( $this->has_cap( 'utf8mb4_520' ) && 'utf8mb4_unicode_ci' === $collate ) {
 781              $collate = 'utf8mb4_unicode_520_ci';
 782          }
 783  
 784          return compact( 'charset', 'collate' );
 785      }
 786  
 787      /**
 788       * Sets the connection's character set.
 789       *
 790       * @since 3.1.0
 791       *
 792       * @param resource $dbh     The resource given by mysql_connect
 793       * @param string   $charset Optional. The character set. Default null.
 794       * @param string   $collate Optional. The collation. Default null.
 795       */
 796  	public function set_charset( $dbh, $charset = null, $collate = null ) {
 797          if ( ! isset( $charset ) ) {
 798              $charset = $this->charset;
 799          }
 800          if ( ! isset( $collate ) ) {
 801              $collate = $this->collate;
 802          }
 803          if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) {
 804              $set_charset_succeeded = true;
 805  
 806              if ( $this->use_mysqli ) {
 807                  if ( function_exists( 'mysqli_set_charset' ) && $this->has_cap( 'set_charset' ) ) {
 808                      $set_charset_succeeded = mysqli_set_charset( $dbh, $charset );
 809                  }
 810  
 811                  if ( $set_charset_succeeded ) {
 812                      $query = $this->prepare( 'SET NAMES %s', $charset );
 813                      if ( ! empty( $collate ) ) {
 814                          $query .= $this->prepare( ' COLLATE %s', $collate );
 815                      }
 816                      mysqli_query( $dbh, $query );
 817                  }
 818              } else {
 819                  if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset' ) ) {
 820                      $set_charset_succeeded = mysql_set_charset( $charset, $dbh );
 821                  }
 822                  if ( $set_charset_succeeded ) {
 823                      $query = $this->prepare( 'SET NAMES %s', $charset );
 824                      if ( ! empty( $collate ) ) {
 825                          $query .= $this->prepare( ' COLLATE %s', $collate );
 826                      }
 827                      mysql_query( $query, $dbh );
 828                  }
 829              }
 830          }
 831      }
 832  
 833      /**
 834       * Change the current SQL mode, and ensure its WordPress compatibility.
 835       *
 836       * If no modes are passed, it will ensure the current MySQL server
 837       * modes are compatible.
 838       *
 839       * @since 3.9.0
 840       *
 841       * @param array $modes Optional. A list of SQL modes to set.
 842       */
 843  	public function set_sql_mode( $modes = array() ) {
 844          if ( empty( $modes ) ) {
 845              if ( $this->use_mysqli ) {
 846                  $res = mysqli_query( $this->dbh, 'SELECT @@SESSION.sql_mode' );
 847              } else {
 848                  $res = mysql_query( 'SELECT @@SESSION.sql_mode', $this->dbh );
 849              }
 850  
 851              if ( empty( $res ) ) {
 852                  return;
 853              }
 854  
 855              if ( $this->use_mysqli ) {
 856                  $modes_array = mysqli_fetch_array( $res );
 857                  if ( empty( $modes_array[0] ) ) {
 858                      return;
 859                  }
 860                  $modes_str = $modes_array[0];
 861              } else {
 862                  $modes_str = mysql_result( $res, 0 );
 863              }
 864  
 865              if ( empty( $modes_str ) ) {
 866                  return;
 867              }
 868  
 869              $modes = explode( ',', $modes_str );
 870          }
 871  
 872          $modes = array_change_key_case( $modes, CASE_UPPER );
 873  
 874          /**
 875           * Filters the list of incompatible SQL modes to exclude.
 876           *
 877           * @since 3.9.0
 878           *
 879           * @param array $incompatible_modes An array of incompatible modes.
 880           */
 881          $incompatible_modes = (array) apply_filters( 'incompatible_sql_modes', $this->incompatible_modes );
 882  
 883          foreach ( $modes as $i => $mode ) {
 884              if ( in_array( $mode, $incompatible_modes ) ) {
 885                  unset( $modes[ $i ] );
 886              }
 887          }
 888  
 889          $modes_str = implode( ',', $modes );
 890  
 891          if ( $this->use_mysqli ) {
 892              mysqli_query( $this->dbh, "SET SESSION sql_mode='$modes_str'" );
 893          } else {
 894              mysql_query( "SET SESSION sql_mode='$modes_str'", $this->dbh );
 895          }
 896      }
 897  
 898      /**
 899       * Sets the table prefix for the WordPress tables.
 900       *
 901       * @since 2.5.0
 902       *
 903       * @param string $prefix          Alphanumeric name for the new prefix.
 904       * @param bool   $set_table_names Optional. Whether the table names, e.g. wpdb::$posts, should be updated or not.
 905       * @return string|WP_Error Old prefix or WP_Error on error
 906       */
 907  	public function set_prefix( $prefix, $set_table_names = true ) {
 908  
 909          if ( preg_match( '|[^a-z0-9_]|i', $prefix ) ) {
 910              return new WP_Error( 'invalid_db_prefix', 'Invalid database prefix' );
 911          }
 912  
 913          $old_prefix = is_multisite() ? '' : $prefix;
 914  
 915          if ( isset( $this->base_prefix ) ) {
 916              $old_prefix = $this->base_prefix;
 917          }
 918  
 919          $this->base_prefix = $prefix;
 920  
 921          if ( $set_table_names ) {
 922              foreach ( $this->tables( 'global' ) as $table => $prefixed_table ) {
 923                  $this->$table = $prefixed_table;
 924              }
 925  
 926              if ( is_multisite() && empty( $this->blogid ) ) {
 927                  return $old_prefix;
 928              }
 929  
 930              $this->prefix = $this->get_blog_prefix();
 931  
 932              foreach ( $this->tables( 'blog' ) as $table => $prefixed_table ) {
 933                  $this->$table = $prefixed_table;
 934              }
 935  
 936              foreach ( $this->tables( 'old' ) as $table => $prefixed_table ) {
 937                  $this->$table = $prefixed_table;
 938              }
 939          }
 940          return $old_prefix;
 941      }
 942  
 943      /**
 944       * Sets blog id.
 945       *
 946       * @since 3.0.0
 947       *
 948       * @param int $blog_id
 949       * @param int $network_id Optional.
 950       * @return int previous blog id
 951       */
 952  	public function set_blog_id( $blog_id, $network_id = 0 ) {
 953          if ( ! empty( $network_id ) ) {
 954              $this->siteid = $network_id;
 955          }
 956  
 957          $old_blog_id  = $this->blogid;
 958          $this->blogid = $blog_id;
 959  
 960          $this->prefix = $this->get_blog_prefix();
 961  
 962          foreach ( $this->tables( 'blog' ) as $table => $prefixed_table ) {
 963              $this->$table = $prefixed_table;
 964          }
 965  
 966          foreach ( $this->tables( 'old' ) as $table => $prefixed_table ) {
 967              $this->$table = $prefixed_table;
 968          }
 969  
 970          return $old_blog_id;
 971      }
 972  
 973      /**
 974       * Gets blog prefix.
 975       *
 976       * @since 3.0.0
 977       * @param int $blog_id Optional.
 978       * @return string Blog prefix.
 979       */
 980  	public function get_blog_prefix( $blog_id = null ) {
 981          if ( is_multisite() ) {
 982              if ( null === $blog_id ) {
 983                  $blog_id = $this->blogid;
 984              }
 985              $blog_id = (int) $blog_id;
 986              if ( defined( 'MULTISITE' ) && ( 0 == $blog_id || 1 == $blog_id ) ) {
 987                  return $this->base_prefix;
 988              } else {
 989                  return $this->base_prefix . $blog_id . '_';
 990              }
 991          } else {
 992              return $this->base_prefix;
 993          }
 994      }
 995  
 996      /**
 997       * Returns an array of WordPress tables.
 998       *
 999       * Also allows for the CUSTOM_USER_TABLE and CUSTOM_USER_META_TABLE to
1000       * override the WordPress users and usermeta tables that would otherwise
1001       * be determined by the prefix.
1002       *
1003       * The scope argument can take one of the following:
1004       *
1005       * 'all' - returns 'all' and 'global' tables. No old tables are returned.
1006       * 'blog' - returns the blog-level tables for the queried blog.
1007       * 'global' - returns the global tables for the installation, returning multisite tables only if running multisite.
1008       * 'ms_global' - returns the multisite global tables, regardless if current installation is multisite.
1009       * 'old' - returns tables which are deprecated.
1010       *
1011       * @since 3.0.0
1012       * @uses wpdb::$tables
1013       * @uses wpdb::$old_tables
1014       * @uses wpdb::$global_tables
1015       * @uses wpdb::$ms_global_tables
1016       *
1017       * @param string $scope   Optional. Can be all, global, ms_global, blog, or old tables. Defaults to all.
1018       * @param bool   $prefix  Optional. Whether to include table prefixes. Default true. If blog
1019       *                        prefix is requested, then the custom users and usermeta tables will be mapped.
1020       * @param int    $blog_id Optional. The blog_id to prefix. Defaults to wpdb::$blogid. Used only when prefix is requested.
1021       * @return array Table names. When a prefix is requested, the key is the unprefixed table name.
1022       */
1023  	public function tables( $scope = 'all', $prefix = true, $blog_id = 0 ) {
1024          switch ( $scope ) {
1025              case 'all':
1026                  $tables = array_merge( $this->global_tables, $this->tables );
1027                  if ( is_multisite() ) {
1028                      $tables = array_merge( $tables, $this->ms_global_tables );
1029                  }
1030                  break;
1031              case 'blog':
1032                  $tables = $this->tables;
1033                  break;
1034              case 'global':
1035                  $tables = $this->global_tables;
1036                  if ( is_multisite() ) {
1037                      $tables = array_merge( $tables, $this->ms_global_tables );
1038                  }
1039                  break;
1040              case 'ms_global':
1041                  $tables = $this->ms_global_tables;
1042                  break;
1043              case 'old':
1044                  $tables = $this->old_tables;
1045                  break;
1046              default:
1047                  return array();
1048          }
1049  
1050          if ( $prefix ) {
1051              if ( ! $blog_id ) {
1052                  $blog_id = $this->blogid;
1053              }
1054              $blog_prefix   = $this->get_blog_prefix( $blog_id );
1055              $base_prefix   = $this->base_prefix;
1056              $global_tables = array_merge( $this->global_tables, $this->ms_global_tables );
1057              foreach ( $tables as $k => $table ) {
1058                  if ( in_array( $table, $global_tables ) ) {
1059                      $tables[ $table ] = $base_prefix . $table;
1060                  } else {
1061                      $tables[ $table ] = $blog_prefix . $table;
1062                  }
1063                  unset( $tables[ $k ] );
1064              }
1065  
1066              if ( isset( $tables['users'] ) && defined( 'CUSTOM_USER_TABLE' ) ) {
1067                  $tables['users'] = CUSTOM_USER_TABLE;
1068              }
1069  
1070              if ( isset( $tables['usermeta'] ) && defined( 'CUSTOM_USER_META_TABLE' ) ) {
1071                  $tables['usermeta'] = CUSTOM_USER_META_TABLE;
1072              }
1073          }
1074  
1075          return $tables;
1076      }
1077  
1078      /**
1079       * Selects a database using the current database connection.
1080       *
1081       * The database name will be changed based on the current database
1082       * connection. On failure, the execution will bail and display an DB error.
1083       *
1084       * @since 0.71
1085       *
1086       * @param string        $db  MySQL database name
1087       * @param resource|null $dbh Optional link identifier.
1088       */
1089  	public function select( $db, $dbh = null ) {
1090          if ( is_null( $dbh ) ) {
1091              $dbh = $this->dbh;
1092          }
1093  
1094          if ( $this->use_mysqli ) {
1095              $success = mysqli_select_db( $dbh, $db );
1096          } else {
1097              $success = mysql_select_db( $db, $dbh );
1098          }
1099          if ( ! $success ) {
1100              $this->ready = false;
1101              if ( ! did_action( 'template_redirect' ) ) {
1102                  wp_load_translations_early();
1103  
1104                  $message = '<h1>' . __( 'Can&#8217;t select database' ) . "</h1>\n";
1105  
1106                  $message .= '<p>' . sprintf(
1107                      /* translators: %s: database name */
1108                      __( 'We were able to connect to the database server (which means your username and password is okay) but not able to select the %s database.' ),
1109                      '<code>' . htmlspecialchars( $db, ENT_QUOTES ) . '</code>'
1110                  ) . "</p>\n";
1111  
1112                  $message .= "<ul>\n";
1113                  $message .= '<li>' . __( 'Are you sure it exists?' ) . "</li>\n";
1114  
1115                  $message .= '<li>' . sprintf(
1116                      /* translators: 1: database user, 2: database name */
1117                      __( 'Does the user %1$s have permission to use the %2$s database?' ),
1118                      '<code>' . htmlspecialchars( $this->dbuser, ENT_QUOTES ) . '</code>',
1119                      '<code>' . htmlspecialchars( $db, ENT_QUOTES ) . '</code>'
1120                  ) . "</li>\n";
1121  
1122                  $message .= '<li>' . sprintf(
1123                      /* translators: %s: database name */
1124                      __( 'On some systems the name of your database is prefixed with your username, so it would be like <code>username_%1$s</code>. Could that be the problem?' ),
1125                      htmlspecialchars( $db, ENT_QUOTES )
1126                  ) . "</li>\n";
1127  
1128                  $message .= "</ul>\n";
1129  
1130                  $message .= '<p>' . sprintf(
1131                      /* translators: %s: support forums URL */
1132                      __( 'If you don&#8217;t know how to set up a database you should <strong>contact your host</strong>. If all else fails you may find help at the <a href="%s">WordPress Support Forums</a>.' ),
1133                      __( 'https://wordpress.org/support/forums/' )
1134                  ) . "</p>\n";
1135  
1136                  $this->bail( $message, 'db_select_fail' );
1137              }
1138          }
1139      }
1140  
1141      /**
1142       * Do not use, deprecated.
1143       *
1144       * Use esc_sql() or wpdb::prepare() instead.
1145       *
1146       * @since 2.8.0
1147       * @deprecated 3.6.0 Use wpdb::prepare()
1148       * @see wpdb::prepare
1149       * @see esc_sql()
1150       *
1151       * @param string $string
1152       * @return string
1153       */
1154  	function _weak_escape( $string ) {
1155          if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) ) {
1156              _deprecated_function( __METHOD__, '3.6.0', 'wpdb::prepare() or esc_sql()' );
1157          }
1158          return addslashes( $string );
1159      }
1160  
1161      /**
1162       * Real escape, using mysqli_real_escape_string() or mysql_real_escape_string()
1163       *
1164       * @see mysqli_real_escape_string()
1165       * @see mysql_real_escape_string()
1166       * @since 2.8.0
1167       *
1168       * @param  string $string to escape
1169       * @return string escaped
1170       */
1171  	function _real_escape( $string ) {
1172          if ( $this->dbh ) {
1173              if ( $this->use_mysqli ) {
1174                  $escaped = mysqli_real_escape_string( $this->dbh, $string );
1175              } else {
1176                  $escaped = mysql_real_escape_string( $string, $this->dbh );
1177              }
1178          } else {
1179              $class = get_class( $this );
1180              if ( function_exists( '__' ) ) {
1181                  /* translators: %s: database access abstraction class, usually wpdb or a class extending wpdb */
1182                  _doing_it_wrong( $class, sprintf( __( '%s must set a database connection for use with escaping.' ), $class ), '3.6.0' );
1183              } else {
1184                  _doing_it_wrong( $class, sprintf( '%s must set a database connection for use with escaping.', $class ), '3.6.0' );
1185              }
1186              $escaped = addslashes( $string );
1187          }
1188  
1189          return $this->add_placeholder_escape( $escaped );
1190      }
1191  
1192      /**
1193       * Escape data. Works on arrays.
1194       *
1195       * @uses wpdb::_real_escape()
1196       * @since  2.8.0
1197       *
1198       * @param  string|array $data
1199       * @return string|array escaped
1200       */
1201  	public function _escape( $data ) {
1202          if ( is_array( $data ) ) {
1203              foreach ( $data as $k => $v ) {
1204                  if ( is_array( $v ) ) {
1205                      $data[ $k ] = $this->_escape( $v );
1206                  } else {
1207                      $data[ $k ] = $this->_real_escape( $v );
1208                  }
1209              }
1210          } else {
1211              $data = $this->_real_escape( $data );
1212          }
1213  
1214          return $data;
1215      }
1216  
1217      /**
1218       * Do not use, deprecated.
1219       *
1220       * Use esc_sql() or wpdb::prepare() instead.
1221       *
1222       * @since 0.71
1223       * @deprecated 3.6.0 Use wpdb::prepare()
1224       * @see wpdb::prepare()
1225       * @see esc_sql()
1226       *
1227       * @param mixed $data
1228       * @return mixed
1229       */
1230  	public function escape( $data ) {
1231          if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) ) {
1232              _deprecated_function( __METHOD__, '3.6.0', 'wpdb::prepare() or esc_sql()' );
1233          }
1234          if ( is_array( $data ) ) {
1235              foreach ( $data as $k => $v ) {
1236                  if ( is_array( $v ) ) {
1237                      $data[ $k ] = $this->escape( $v, 'recursive' );
1238                  } else {
1239                      $data[ $k ] = $this->_weak_escape( $v, 'internal' );
1240                  }
1241              }
1242          } else {
1243              $data = $this->_weak_escape( $data, 'internal' );
1244          }
1245  
1246          return $data;
1247      }
1248  
1249      /**
1250       * Escapes content by reference for insertion into the database, for security
1251       *
1252       * @uses wpdb::_real_escape()
1253       *
1254       * @since 2.3.0
1255       *
1256       * @param string $string to escape
1257       */
1258  	public function escape_by_ref( &$string ) {
1259          if ( ! is_float( $string ) ) {
1260              $string = $this->_real_escape( $string );
1261          }
1262      }
1263  
1264      /**
1265       * Prepares a SQL query for safe execution. Uses sprintf()-like syntax.
1266       *
1267       * The following placeholders can be used in the query string:
1268       *   %d (integer)
1269       *   %f (float)
1270       *   %s (string)
1271       *
1272       * All placeholders MUST be left unquoted in the query string. A corresponding argument
1273       * MUST be passed for each placeholder.
1274       *
1275       * For compatibility with old behavior, numbered or formatted string placeholders (eg, %1$s, %5s)
1276       * will not have quotes added by this function, so should be passed with appropriate quotes around
1277       * them for your usage.
1278       *
1279       * Literal percentage signs (%) in the query string must be written as %%. Percentage wildcards (for example,
1280       * to use in LIKE syntax) must be passed via a substitution argument containing the complete LIKE string, these
1281       * cannot be inserted directly in the query string. Also see wpdb::esc_like().
1282       *
1283       * Arguments may be passed as individual arguments to the method, or as a single array containing
1284       * all arguments. A combination of the two is not supported.
1285       *
1286       * Examples:
1287       *     $wpdb->prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d OR `other_field` LIKE %s", array( 'foo', 1337, '%bar' ) );
1288       *     $wpdb->prepare( "SELECT DATE_FORMAT(`field`, '%%c') FROM `table` WHERE `column` = %s", 'foo' );
1289       *
1290       * @link https://secure.php.net/sprintf Description of syntax.
1291       * @since 2.3.0
1292       *
1293       * @param string      $query   Query statement with sprintf()-like placeholders
1294       * @param array|mixed $args    The array of variables to substitute into the query's placeholders
1295       *                             if being called with an array of arguments, or the first variable
1296       *                             to substitute into the query's placeholders if being called with
1297       *                             individual arguments.
1298       * @param mixed       ...$args Further variables to substitute into the query's placeholders
1299       *                             if being called with individual arguments.
1300       * @return string|void Sanitized query string, if there is a query to prepare.
1301       */
1302  	public function prepare( $query, ...$args ) {
1303          if ( is_null( $query ) ) {
1304              return;
1305          }
1306  
1307          // This is not meant to be foolproof -- but it will catch obviously incorrect usage.
1308          if ( strpos( $query, '%' ) === false ) {
1309              wp_load_translations_early();
1310              _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9.0' );
1311          }
1312  
1313          // If args were passed as an array (as in vsprintf), move them up.
1314          $passed_as_array = false;
1315          if ( is_array( $args[0] ) && count( $args ) == 1 ) {
1316              $passed_as_array = true;
1317              $args            = $args[0];
1318          }
1319  
1320          foreach ( $args as $arg ) {
1321              if ( ! is_scalar( $arg ) && ! is_null( $arg ) ) {
1322                  wp_load_translations_early();
1323                  _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'Unsupported value type (%s).' ), gettype( $arg ) ), '4.8.2' );
1324              }
1325          }
1326  
1327          /*
1328           * Specify the formatting allowed in a placeholder. The following are allowed:
1329           *
1330           * - Sign specifier. eg, $+d
1331           * - Numbered placeholders. eg, %1$s
1332           * - Padding specifier, including custom padding characters. eg, %05s, %'#5s
1333           * - Alignment specifier. eg, %05-s
1334           * - Precision specifier. eg, %.2f
1335           */
1336          $allowed_format = '(?:[1-9][0-9]*[$])?[-+0-9]*(?: |0|\'.)?[-+0-9]*(?:\.[0-9]+)?';
1337  
1338          /*
1339           * If a %s placeholder already has quotes around it, removing the existing quotes and re-inserting them
1340           * ensures the quotes are consistent.
1341           *
1342           * For backward compatibility, this is only applied to %s, and not to placeholders like %1$s, which are frequently
1343           * used in the middle of longer strings, or as table name placeholders.
1344           */
1345          $query = str_replace( "'%s'", '%s', $query ); // Strip any existing single quotes.
1346          $query = str_replace( '"%s"', '%s', $query ); // Strip any existing double quotes.
1347          $query = preg_replace( '/(?<!%)%s/', "'%s'", $query ); // Quote the strings, avoiding escaped strings like %%s.
1348  
1349          $query = preg_replace( "/(?<!%)(%($allowed_format)?f)/", '%\\2F', $query ); // Force floats to be locale unaware.
1350  
1351          $query = preg_replace( "/%(?:%|$|(?!($allowed_format)?[sdF]))/", '%%\\1', $query ); // Escape any unescaped percents.
1352  
1353          // Count the number of valid placeholders in the query.
1354          $placeholders = preg_match_all( "/(^|[^%]|(%%)+)%($allowed_format)?[sdF]/", $query, $matches );
1355  
1356          if ( count( $args ) !== $placeholders ) {
1357              if ( 1 === $placeholders && $passed_as_array ) {
1358                  // If the passed query only expected one argument, but the wrong number of arguments were sent as an array, bail.
1359                  wp_load_translations_early();
1360                  _doing_it_wrong( 'wpdb::prepare', __( 'The query only expected one placeholder, but an array of multiple placeholders was sent.' ), '4.9.0' );
1361  
1362                  return;
1363              } else {
1364                  /*
1365                   * If we don't have the right number of placeholders, but they were passed as individual arguments,
1366                   * or we were expecting multiple arguments in an array, throw a warning.
1367                   */
1368                  wp_load_translations_early();
1369                  _doing_it_wrong(
1370                      'wpdb::prepare',
1371                      /* translators: 1: number of placeholders, 2: number of arguments passed */
1372                      sprintf(
1373                          __( 'The query does not contain the correct number of placeholders (%1$d) for the number of arguments passed (%2$d).' ),
1374                          $placeholders,
1375                          count( $args )
1376                      ),
1377                      '4.8.3'
1378                  );
1379              }
1380          }
1381  
1382          array_walk( $args, array( $this, 'escape_by_ref' ) );
1383          $query = vsprintf( $query, $args );
1384  
1385          return $this->add_placeholder_escape( $query );
1386      }
1387  
1388      /**
1389       * First half of escaping for LIKE special characters % and _ before preparing for MySQL.
1390       *
1391       * Use this only before wpdb::prepare() or esc_sql().  Reversing the order is very bad for security.
1392       *
1393       * Example Prepared Statement:
1394       *
1395       *     $wild = '%';
1396       *     $find = 'only 43% of planets';
1397       *     $like = $wild . $wpdb->esc_like( $find ) . $wild;
1398       *     $sql  = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE %s", $like );
1399       *
1400       * Example Escape Chain:
1401       *
1402       *     $sql  = esc_sql( $wpdb->esc_like( $input ) );
1403       *
1404       * @since 4.0.0
1405       *
1406       * @param string $text The raw text to be escaped. The input typed by the user should have no
1407       *                     extra or deleted slashes.
1408       * @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare()
1409       *                or real_escape next.
1410       */
1411  	public function esc_like( $text ) {
1412          return addcslashes( $text, '_%\\' );
1413      }
1414  
1415      /**
1416       * Print SQL/DB error.
1417       *
1418       * @since 0.71
1419       * @global array $EZSQL_ERROR Stores error information of query and error string
1420       *
1421       * @param string $str The error to display
1422       * @return false|void False if the showing of errors is disabled.
1423       */
1424  	public function print_error( $str = '' ) {
1425          global $EZSQL_ERROR;
1426  
1427          if ( ! $str ) {
1428              if ( $this->use_mysqli ) {
1429                  $str = mysqli_error( $this->dbh );
1430              } else {
1431                  $str = mysql_error( $this->dbh );
1432              }
1433          }
1434          $EZSQL_ERROR[] = array(
1435              'query'     => $this->last_query,
1436              'error_str' => $str,
1437          );
1438  
1439          if ( $this->suppress_errors ) {
1440              return false;
1441          }
1442  
1443          wp_load_translations_early();
1444  
1445          $caller = $this->get_caller();
1446          if ( $caller ) {
1447              /* translators: 1: Database error message, 2: SQL query, 3: Name of the calling function */
1448              $error_str = sprintf( __( 'WordPress database error %1$s for query %2$s made by %3$s' ), $str, $this->last_query, $caller );
1449          } else {
1450              /* translators: 1: Database error message, 2: SQL query */
1451              $error_str = sprintf( __( 'WordPress database error %1$s for query %2$s' ), $str, $this->last_query );
1452          }
1453  
1454          error_log( $error_str );
1455  
1456          // Are we showing errors?
1457          if ( ! $this->show_errors ) {
1458              return false;
1459          }
1460  
1461          // If there is an error then take note of it
1462          if ( is_multisite() ) {
1463              $msg = sprintf(
1464                  "%s [%s]\n%s\n",
1465                  __( 'WordPress database error:' ),
1466                  $str,
1467                  $this->last_query
1468              );
1469  
1470              if ( defined( 'ERRORLOGFILE' ) ) {
1471                  error_log( $msg, 3, ERRORLOGFILE );
1472              }
1473              if ( defined( 'DIEONDBERROR' ) ) {
1474                  wp_die( $msg );
1475              }
1476          } else {
1477              $str   = htmlspecialchars( $str, ENT_QUOTES );
1478              $query = htmlspecialchars( $this->last_query, ENT_QUOTES );
1479  
1480              printf(
1481                  '<div id="error"><p class="wpdberror"><strong>%s</strong> [%s]<br /><code>%s</code></p></div>',
1482                  __( 'WordPress database error:' ),
1483                  $str,
1484                  $query
1485              );
1486          }
1487      }
1488  
1489      /**
1490       * Enables showing of database errors.
1491       *
1492       * This function should be used only to enable showing of errors.
1493       * wpdb::hide_errors() should be used instead for hiding of errors. However,
1494       * this function can be used to enable and disable showing of database
1495       * errors.
1496       *
1497       * @since 0.71
1498       * @see wpdb::hide_errors()
1499       *
1500       * @param bool $show Whether to show or hide errors
1501       * @return bool Old value for showing errors.
1502       */
1503  	public function show_errors( $show = true ) {
1504          $errors            = $this->show_errors;
1505          $this->show_errors = $show;
1506          return $errors;
1507      }
1508  
1509      /**
1510       * Disables showing of database errors.
1511       *
1512       * By default database errors are not shown.
1513       *
1514       * @since 0.71
1515       * @see wpdb::show_errors()
1516       *
1517       * @return bool Whether showing of errors was active
1518       */
1519  	public function hide_errors() {
1520          $show              = $this->show_errors;
1521          $this->show_errors = false;
1522          return $show;
1523      }
1524  
1525      /**
1526       * Whether to suppress database errors.
1527       *
1528       * By default database errors are suppressed, with a simple
1529       * call to this function they can be enabled.
1530       *
1531       * @since 2.5.0
1532       * @see wpdb::hide_errors()
1533       * @param bool $suppress Optional. New value. Defaults to true.
1534       * @return bool Old value
1535       */
1536  	public function suppress_errors( $suppress = true ) {
1537          $errors                = $this->suppress_errors;
1538          $this->suppress_errors = (bool) $suppress;
1539          return $errors;
1540      }
1541  
1542      /**
1543       * Kill cached query results.
1544       *
1545       * @since 0.71
1546       */
1547  	public function flush() {
1548          $this->last_result   = array();
1549          $this->col_info      = null;
1550          $this->last_query    = null;
1551          $this->rows_affected = 0;
1552          $this->num_rows      = 0;
1553          $this->last_error    = '';
1554  
1555          if ( $this->use_mysqli && $this->result instanceof mysqli_result ) {
1556              mysqli_free_result( $this->result );
1557              $this->result = null;
1558  
1559              // Sanity check before using the handle
1560              if ( empty( $this->dbh ) || ! ( $this->dbh instanceof mysqli ) ) {
1561                  return;
1562              }
1563  
1564              // Clear out any results from a multi-query
1565              while ( mysqli_more_results( $this->dbh ) ) {
1566                  mysqli_next_result( $this->dbh );
1567              }
1568          } elseif ( is_resource( $this->result ) ) {
1569              mysql_free_result( $this->result );
1570          }
1571      }
1572  
1573      /**
1574       * Connect to and select database.
1575       *
1576       * If $allow_bail is false, the lack of database connection will need
1577       * to be handled manually.
1578       *
1579       * @since 3.0.0
1580       * @since 3.9.0 $allow_bail parameter added.
1581       *
1582       * @param bool $allow_bail Optional. Allows the function to bail. Default true.
1583       * @return bool True with a successful connection, false on failure.
1584       */
1585  	public function db_connect( $allow_bail = true ) {
1586          $this->is_mysql = true;
1587  
1588          /*
1589           * Deprecated in 3.9+ when using MySQLi. No equivalent
1590           * $new_link parameter exists for mysqli_* functions.
1591           */
1592          $new_link     = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
1593          $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
1594  
1595          if ( $this->use_mysqli ) {
1596              $this->dbh = mysqli_init();
1597  
1598              $host    = $this->dbhost;
1599              $port    = null;
1600              $socket  = null;
1601              $is_ipv6 = false;
1602  
1603              $host_data = $this->parse_db_host( $this->dbhost );
1604              if ( $host_data ) {
1605                  list( $host, $port, $socket, $is_ipv6 ) = $host_data;
1606              }
1607  
1608              /*
1609               * If using the `mysqlnd` library, the IPv6 address needs to be
1610               * enclosed in square brackets, whereas it doesn't while using the
1611               * `libmysqlclient` library.
1612               * @see https://bugs.php.net/bug.php?id=67563
1613               */
1614              if ( $is_ipv6 && extension_loaded( 'mysqlnd' ) ) {
1615                  $host = "[$host]";
1616              }
1617  
1618              if ( WP_DEBUG ) {
1619                  mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
1620              } else {
1621                  // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1622                  @mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
1623              }
1624  
1625              if ( $this->dbh->connect_errno ) {
1626                  $this->dbh = null;
1627  
1628                  /*
1629                   * It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
1630                   *  - We haven't previously connected, and
1631                   *  - WP_USE_EXT_MYSQL isn't set to false, and
1632                   *  - ext/mysql is loaded.
1633                   */
1634                  $attempt_fallback = true;
1635  
1636                  if ( $this->has_connected ) {
1637                      $attempt_fallback = false;
1638                  } elseif ( defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL ) {
1639                      $attempt_fallback = false;
1640                  } elseif ( ! function_exists( 'mysql_connect' ) ) {
1641                      $attempt_fallback = false;
1642                  }
1643  
1644                  if ( $attempt_fallback ) {
1645                      $this->use_mysqli = false;
1646                      return $this->db_connect( $allow_bail );
1647                  }
1648              }
1649          } else {
1650              if ( WP_DEBUG ) {
1651                  $this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
1652              } else {
1653                  // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1654                  $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
1655              }
1656          }
1657  
1658          if ( ! $this->dbh && $allow_bail ) {
1659              wp_load_translations_early();
1660  
1661              // Load custom DB error template, if present.
1662              if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
1663                  require_once( WP_CONTENT_DIR . '/db-error.php' );
1664                  die();
1665              }
1666  
1667              $message = '<h1>' . __( 'Error establishing a database connection' ) . "</h1>\n";
1668  
1669              $message .= '<p>' . sprintf(
1670                  /* translators: 1: wp-config.php, 2: database host */
1671                  __( 'This either means that the username and password information in your %1$s file is incorrect or we can&#8217;t contact the database server at %2$s. This could mean your host&#8217;s database server is down.' ),
1672                  '<code>wp-config.php</code>',
1673                  '<code>' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '</code>'
1674              ) . "</p>\n";
1675  
1676              $message .= "<ul>\n";
1677              $message .= '<li>' . __( 'Are you sure you have the correct username and password?' ) . "</li>\n";
1678              $message .= '<li>' . __( 'Are you sure you have typed the correct hostname?' ) . "</li>\n";
1679              $message .= '<li>' . __( 'Are you sure the database server is running?' ) . "</li>\n";
1680              $message .= "</ul>\n";
1681  
1682              $message .= '<p>' . sprintf(
1683                  /* translators: %s: support forums URL */
1684                  __( 'If you&#8217;re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href="%s">WordPress Support Forums</a>.' ),
1685                  __( 'https://wordpress.org/support/forums/' )
1686              ) . "</p>\n";
1687  
1688              $this->bail( $message, 'db_connect_fail' );
1689  
1690              return false;
1691          } elseif ( $this->dbh ) {
1692              if ( ! $this->has_connected ) {
1693                  $this->init_charset();
1694              }
1695  
1696              $this->has_connected = true;
1697  
1698              $this->set_charset( $this->dbh );
1699  
1700              $this->ready = true;
1701              $this->set_sql_mode();
1702              $this->select( $this->dbname, $this->dbh );
1703  
1704              return true;
1705          }
1706  
1707          return false;
1708      }
1709  
1710      /**
1711       * Parse the DB_HOST setting to interpret it for mysqli_real_connect.
1712       *
1713       * mysqli_real_connect doesn't support the host param including a port or
1714       * socket like mysql_connect does. This duplicates how mysql_connect detects
1715       * a port and/or socket file.
1716       *
1717       * @since 4.9.0
1718       *
1719       * @param string $host The DB_HOST setting to parse.
1720       * @return array|bool Array containing the host, the port, the socket and whether
1721       *                    it is an IPv6 address, in that order. If $host couldn't be parsed,
1722       *                    returns false.
1723       */
1724  	public function parse_db_host( $host ) {
1725          $port    = null;
1726          $socket  = null;
1727          $is_ipv6 = false;
1728  
1729          // First peel off the socket parameter from the right, if it exists.
1730          $socket_pos = strpos( $host, ':/' );
1731          if ( $socket_pos !== false ) {
1732              $socket = substr( $host, $socket_pos + 1 );
1733              $host   = substr( $host, 0, $socket_pos );
1734          }
1735  
1736          // We need to check for an IPv6 address first.
1737          // An IPv6 address will always contain at least two colons.
1738          if ( substr_count( $host, ':' ) > 1 ) {
1739              $pattern = '#^(?:\[)?(?P<host>[0-9a-fA-F:]+)(?:\]:(?P<port>[\d]+))?#';
1740              $is_ipv6 = true;
1741          } else {
1742              // We seem to be dealing with an IPv4 address.
1743              $pattern = '#^(?P<host>[^:/]*)(?::(?P<port>[\d]+))?#';
1744          }
1745  
1746          $matches = array();
1747          $result  = preg_match( $pattern, $host, $matches );
1748  
1749          if ( 1 !== $result ) {
1750              // Couldn't parse the address, bail.
1751              return false;
1752          }
1753  
1754          $host = '';
1755          foreach ( array( 'host', 'port' ) as $component ) {
1756              if ( ! empty( $matches[ $component ] ) ) {
1757                  $$component = $matches[ $component ];
1758              }
1759          }
1760  
1761          return array( $host, $port, $socket, $is_ipv6 );
1762      }
1763  
1764      /**
1765       * Checks that the connection to the database is still up. If not, try to reconnect.
1766       *
1767       * If this function is unable to reconnect, it will forcibly die, or if after the
1768       * the {@see 'template_redirect'} hook has been fired, return false instead.
1769       *
1770       * If $allow_bail is false, the lack of database connection will need
1771       * to be handled manually.
1772       *
1773       * @since 3.9.0
1774       *
1775       * @param bool $allow_bail Optional. Allows the function to bail. Default true.
1776       * @return bool|void True if the connection is up.
1777       */
1778  	public function check_connection( $allow_bail = true ) {
1779          if ( $this->use_mysqli ) {
1780              if ( ! empty( $this->dbh ) && mysqli_ping( $this->dbh ) ) {
1781                  return true;
1782              }
1783          } else {
1784              if ( ! empty( $this->dbh ) && mysql_ping( $this->dbh ) ) {
1785                  return true;
1786              }
1787          }
1788  
1789          $error_reporting = false;
1790  
1791          // Disable warnings, as we don't want to see a multitude of "unable to connect" messages
1792          if ( WP_DEBUG ) {
1793              $error_reporting = error_reporting();
1794              error_reporting( $error_reporting & ~E_WARNING );
1795          }
1796  
1797          for ( $tries = 1; $tries <= $this->reconnect_retries; $tries++ ) {
1798              // On the last try, re-enable warnings. We want to see a single instance of the
1799              // "unable to connect" message on the bail() screen, if it appears.
1800              if ( $this->reconnect_retries === $tries && WP_DEBUG ) {
1801                  error_reporting( $error_reporting );
1802              }
1803  
1804              if ( $this->db_connect( false ) ) {
1805                  if ( $error_reporting ) {
1806                      error_reporting( $error_reporting );
1807                  }
1808  
1809                  return true;
1810              }
1811  
1812              sleep( 1 );
1813          }
1814  
1815          // If template_redirect has already happened, it's too late for wp_die()/dead_db().
1816          // Let's just return and hope for the best.
1817          if ( did_action( 'template_redirect' ) ) {
1818              return false;
1819          }
1820  
1821          if ( ! $allow_bail ) {
1822              return false;
1823          }
1824  
1825          wp_load_translations_early();
1826  
1827          $message = '<h1>' . __( 'Error reconnecting to the database' ) . "</h1>\n";
1828  
1829          $message .= '<p>' . sprintf(
1830              /* translators: %s: database host */
1831              __( 'This means that we lost contact with the database server at %s. This could mean your host&#8217;s database server is down.' ),
1832              '<code>' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '</code>'
1833          ) . "</p>\n";
1834  
1835          $message .= "<ul>\n";
1836          $message .= '<li>' . __( 'Are you sure the database server is running?' ) . "</li>\n";
1837          $message .= '<li>' . __( 'Are you sure the database server is not under particularly heavy load?' ) . "</li>\n";
1838          $message .= "</ul>\n";
1839  
1840          $message .= '<p>' . sprintf(
1841              /* translators: %s: support forums URL */
1842              __( 'If you&#8217;re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the <a href="%s">WordPress Support Forums</a>.' ),
1843              __( 'https://wordpress.org/support/forums/' )
1844          ) . "</p>\n";
1845  
1846          // We weren't able to reconnect, so we better bail.
1847          $this->bail( $message, 'db_connect_fail' );
1848  
1849          // Call dead_db() if bail didn't die, because this database is no more. It has ceased to be (at least temporarily).
1850          dead_db();
1851      }
1852  
1853      /**
1854       * Perform a MySQL database query, using current database connection.
1855       *
1856       * More information can be found on the codex page.
1857       *
1858       * @since 0.71
1859       *
1860       * @param string $query Database query
1861       * @return int|bool Boolean true for CREATE, ALTER, TRUNCATE and DROP queries. Number of rows
1862       *                  affected/selected for all other queries. Boolean false on error.
1863       */
1864  	public function query( $query ) {
1865          if ( ! $this->ready ) {
1866              $this->check_current_query = true;
1867              return false;
1868          }
1869  
1870          /**
1871           * Filters the database query.
1872           *
1873           * Some queries are made before the plugins have been loaded,
1874           * and thus cannot be filtered with this method.
1875           *
1876           * @since 2.1.0
1877           *
1878           * @param string $query Database query.
1879           */
1880          $query = apply_filters( 'query', $query );
1881  
1882          $this->flush();
1883  
1884          // Log how the function was called
1885          $this->func_call = "\$db->query(\"$query\")";
1886  
1887          // If we're writing to the database, make sure the query will write safely.
1888          if ( $this->check_current_query && ! $this->check_ascii( $query ) ) {
1889              $stripped_query = $this->strip_invalid_text_from_query( $query );
1890              // strip_invalid_text_from_query() can perform queries, so we need
1891              // to flush again, just to make sure everything is clear.
1892              $this->flush();
1893              if ( $stripped_query !== $query ) {
1894                  $this->insert_id = 0;
1895                  return false;
1896              }
1897          }
1898  
1899          $this->check_current_query = true;
1900  
1901          // Keep track of the last query for debug.
1902          $this->last_query = $query;
1903  
1904          $this->_do_query( $query );
1905  
1906          // MySQL server has gone away, try to reconnect.
1907          $mysql_errno = 0;
1908          if ( ! empty( $this->dbh ) ) {
1909              if ( $this->use_mysqli ) {
1910                  if ( $this->dbh instanceof mysqli ) {
1911                      $mysql_errno = mysqli_errno( $this->dbh );
1912                  } else {
1913                      // $dbh is defined, but isn't a real connection.
1914                      // Something has gone horribly wrong, let's try a reconnect.
1915                      $mysql_errno = 2006;
1916                  }
1917              } else {
1918                  if ( is_resource( $this->dbh ) ) {
1919                      $mysql_errno = mysql_errno( $this->dbh );
1920                  } else {
1921                      $mysql_errno = 2006;
1922                  }
1923              }
1924          }
1925  
1926          if ( empty( $this->dbh ) || 2006 == $mysql_errno ) {
1927              if ( $this->check_connection() ) {
1928                  $this->_do_query( $query );
1929              } else {
1930                  $this->insert_id = 0;
1931                  return false;
1932              }
1933          }
1934  
1935          // If there is an error then take note of it.
1936          if ( $this->use_mysqli ) {
1937              if ( $this->dbh instanceof mysqli ) {
1938                  $this->last_error = mysqli_error( $this->dbh );
1939              } else {
1940                  $this->last_error = __( 'Unable to retrieve the error message from MySQL' );
1941              }
1942          } else {
1943              if ( is_resource( $this->dbh ) ) {
1944                  $this->last_error = mysql_error( $this->dbh );
1945              } else {
1946                  $this->last_error = __( 'Unable to retrieve the error message from MySQL' );
1947              }
1948          }
1949  
1950          if ( $this->last_error ) {
1951              // Clear insert_id on a subsequent failed insert.
1952              if ( $this->insert_id && preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
1953                  $this->insert_id = 0;
1954              }
1955  
1956              $this->print_error();
1957              return false;
1958          }
1959  
1960          if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) {
1961              $return_val = $this->result;
1962          } elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) {
1963              if ( $this->use_mysqli ) {
1964                  $this->rows_affected = mysqli_affected_rows( $this->dbh );
1965              } else {
1966                  $this->rows_affected = mysql_affected_rows( $this->dbh );
1967              }
1968              // Take note of the insert_id
1969              if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
1970                  if ( $this->use_mysqli ) {
1971                      $this->insert_id = mysqli_insert_id( $this->dbh );
1972                  } else {
1973                      $this->insert_id = mysql_insert_id( $this->dbh );
1974                  }
1975              }
1976              // Return number of rows affected
1977              $return_val = $this->rows_affected;
1978          } else {
1979              $num_rows = 0;
1980              if ( $this->use_mysqli && $this->result instanceof mysqli_result ) {
1981                  while ( $row = mysqli_fetch_object( $this->result ) ) {
1982                      $this->last_result[ $num_rows ] = $row;
1983                      $num_rows++;
1984                  }
1985              } elseif ( is_resource( $this->result ) ) {
1986                  while ( $row = mysql_fetch_object( $this->result ) ) {
1987                      $this->last_result[ $num_rows ] = $row;
1988                      $num_rows++;
1989                  }
1990              }
1991  
1992              // Log number of rows the query returned
1993              // and return number of rows selected
1994              $this->num_rows = $num_rows;
1995              $return_val     = $num_rows;
1996          }
1997  
1998          return $return_val;
1999      }
2000  
2001      /**
2002       * Internal function to perform the mysql_query() call.
2003       *
2004       * @since 3.9.0
2005       *
2006       * @see wpdb::query()
2007       *
2008       * @param string $query The query to run.
2009       */
2010  	private function _do_query( $query ) {
2011          if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
2012              $this->timer_start();
2013          }
2014  
2015          if ( ! empty( $this->dbh ) && $this->use_mysqli ) {
2016              $this->result = mysqli_query( $this->dbh, $query );
2017          } elseif ( ! empty( $this->dbh ) ) {
2018              $this->result = mysql_query( $query, $this->dbh );
2019          }
2020          $this->num_queries++;
2021  
2022          if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
2023              $this->log_query(
2024                  $query,
2025                  $this->timer_stop(),
2026                  $this->get_caller(),
2027                  $this->time_start,
2028                  array()
2029              );
2030          }
2031      }
2032  
2033      /**
2034       * Logs query data.
2035       *
2036       * @since 5.3.0
2037       *
2038       * @param string $query           The query's SQL.
2039       * @param float  $query_time      Total time spent on the query, in seconds.
2040       * @param string $query_callstack Comma separated list of the calling functions.
2041       * @param float  $query_start     Unix timestamp of the time at the start of the query.
2042       * @param array  $query_data      Custom query data.
2043       * }
2044       */
2045  	public function log_query( $query, $query_time, $query_callstack, $query_start, $query_data ) {
2046          /**
2047           * Filters the custom query data being logged.
2048           *
2049           * Caution should be used when modifying any of this data, it is recommended that any additional
2050           * information you need to store about a query be added as a new associative entry to the fourth
2051           * element $query_data.
2052           *
2053           * @since 5.3.0
2054           *
2055           * @param array  $query_data      Custom query data.
2056           * @param string $query           The query's SQL.
2057           * @param float  $query_time      Total time spent on the query, in seconds.
2058           * @param string $query_callstack Comma separated list of the calling functions.
2059           * @param float  $query_start     Unix timestamp of the time at the start of the query.
2060           */
2061          $query_data = apply_filters( 'log_query_custom_data', $query_data, $query, $query_time, $query_callstack, $query_start );
2062  
2063          $this->queries[] = array(
2064              $query,
2065              $query_time,
2066              $query_callstack,
2067              $query_start,
2068              $query_data,
2069          );
2070      }
2071  
2072      /**
2073       * Generates and returns a placeholder escape string for use in queries returned by ::prepare().
2074       *
2075       * @since 4.8.3
2076       *
2077       * @return string String to escape placeholders.
2078       */
2079  	public function placeholder_escape() {
2080          static $placeholder;
2081  
2082          if ( ! $placeholder ) {
2083              // If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
2084              $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
2085              // Old WP installs may not have AUTH_SALT defined.
2086              $salt = defined( 'AUTH_SALT' ) && AUTH_SALT ? AUTH_SALT : (string) rand();
2087  
2088              $placeholder = '{' . hash_hmac( $algo, uniqid( $salt, true ), $salt ) . '}';
2089          }
2090  
2091          /*
2092           * Add the filter to remove the placeholder escaper. Uses priority 0, so that anything
2093           * else attached to this filter will receive the query with the placeholder string removed.
2094           */
2095          if ( ! has_filter( 'query', array( $this, 'remove_placeholder_escape' ) ) ) {
2096              add_filter( 'query', array( $this, 'remove_placeholder_escape' ), 0 );
2097          }
2098  
2099          return $placeholder;
2100      }
2101  
2102      /**
2103       * Adds a placeholder escape string, to escape anything that resembles a printf() placeholder.
2104       *
2105       * @since 4.8.3
2106       *
2107       * @param string $query The query to escape.
2108       * @return string The query with the placeholder escape string inserted where necessary.
2109       */
2110  	public function add_placeholder_escape( $query ) {
2111          /*
2112           * To prevent returning anything that even vaguely resembles a placeholder,
2113           * we clobber every % we can find.
2114           */
2115          return str_replace( '%', $this->placeholder_escape(), $query );
2116      }
2117  
2118      /**
2119       * Removes the placeholder escape strings from a query.
2120       *
2121       * @since 4.8.3
2122       *
2123       * @param string $query The query from which the placeholder will be removed.
2124       * @return string The query with the placeholder removed.
2125       */
2126  	public function remove_placeholder_escape( $query ) {
2127          return str_replace( $this->placeholder_escape(), '%', $query );
2128      }
2129  
2130      /**
2131       * Insert a row into a table.
2132       *
2133       *     wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 'bar' ) )
2134       *     wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) )
2135       *
2136       * @since 2.5.0
2137       * @see wpdb::prepare()
2138       * @see wpdb::$field_types
2139       * @see wp_set_wpdb_vars()
2140       *
2141       * @param string       $table  Table name
2142       * @param array        $data   Data to insert (in column => value pairs).
2143       *                             Both $data columns and $data values should be "raw" (neither should be SQL escaped).
2144       *                             Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
2145       * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
2146       *                             If string, that format will be used for all of the values in $data.
2147       *                             A format is one of '%d', '%f', '%s' (integer, float, string).
2148       *                             If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
2149       * @return int|false The number of rows inserted, or false on error.
2150       */
2151  	public function insert( $table, $data, $format = null ) {
2152          return $this->_insert_replace_helper( $table, $data, $format, 'INSERT' );
2153      }
2154  
2155      /**
2156       * Replace a row into a table.
2157       *
2158       *     wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 'bar' ) )
2159       *     wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) )
2160       *
2161       * @since 3.0.0
2162       * @see wpdb::prepare()
2163       * @see wpdb::$field_types
2164       * @see wp_set_wpdb_vars()
2165       *
2166       * @param string       $table  Table name
2167       * @param array        $data   Data to insert (in column => value pairs).
2168       *                             Both $data columns and $data values should be "raw" (neither should be SQL escaped).
2169       *                             Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
2170       * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
2171       *                             If string, that format will be used for all of the values in $data.
2172       *                             A format is one of '%d', '%f', '%s' (integer, float, string).
2173       *                             If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
2174       * @return int|false The number of rows affected, or false on error.
2175       */
2176  	public function replace( $table, $data, $format = null ) {
2177          return $this->_insert_replace_helper( $table, $data, $format, 'REPLACE' );
2178      }
2179  
2180      /**
2181       * Helper function for insert and replace.
2182       *
2183       * Runs an insert or replace query based on $type argument.
2184       *
2185       * @since 3.0.0
2186       * @see wpdb::prepare()
2187       * @see wpdb::$field_types
2188       * @see wp_set_wpdb_vars()
2189       *
2190       * @param string       $table  Table name
2191       * @param array        $data   Data to insert (in column => value pairs).
2192       *                             Both $data columns and $data values should be "raw" (neither should be SQL escaped).
2193       *                             Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
2194       * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
2195       *                             If string, that format will be used for all of the values in $data.
2196       *                             A format is one of '%d', '%f', '%s' (integer, float, string).
2197       *                             If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
2198       * @param string $type         Optional. What type of operation is this? INSERT or REPLACE. Defaults to INSERT.
2199       * @return int|false The number of rows affected, or false on error.
2200       */
2201  	function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) {
2202          $this->insert_id = 0;
2203  
2204          if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) {
2205              return false;
2206          }
2207  
2208          $data = $this->process_fields( $table, $data, $format );
2209          if ( false === $data ) {
2210              return false;
2211          }
2212  
2213          $formats = array();
2214          $values  = array();
2215          foreach ( $data as $value ) {
2216              if ( is_null( $value['value'] ) ) {
2217                  $formats[] = 'NULL';
2218                  continue;
2219              }
2220  
2221              $formats[] = $value['format'];
2222              $values[]  = $value['value'];
2223          }
2224  
2225          $fields  = '`' . implode( '`, `', array_keys( $data ) ) . '`';
2226          $formats = implode( ', ', $formats );
2227  
2228          $sql = "$type INTO `$table` ($fields) VALUES ($formats)";
2229  
2230          $this->check_current_query = false;
2231          return $this->query( $this->prepare( $sql, $values ) );
2232      }
2233  
2234      /**
2235       * Update a row in the table
2236       *
2237       *     wpdb::update( 'table', array( 'column' => 'foo', 'field' => 'bar' ), array( 'ID' => 1 ) )
2238       *     wpdb::update( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( 'ID' => 1 ), array( '%s', '%d' ), array( '%d' ) )
2239       *
2240       * @since 2.5.0
2241       * @see wpdb::prepare()
2242       * @see wpdb::$field_types
2243       * @see wp_set_wpdb_vars()
2244       *
2245       * @param string       $table        Table name
2246       * @param array        $data         Data to update (in column => value pairs).
2247       *                                   Both $data columns and $data values should be "raw" (neither should be SQL escaped).
2248       *                                   Sending a null value will cause the column to be set to NULL - the corresponding
2249       *                                   format is ignored in this case.
2250       * @param array        $where        A named array of WHERE clauses (in column => value pairs).
2251       *                                   Multiple clauses will be joined with ANDs.
2252       *                                   Both $where columns and $where values should be "raw".
2253       *                                   Sending a null value will create an IS NULL comparison - the corresponding format will be ignored in this case.
2254       * @param array|string $format       Optional. An array of formats to be mapped to each of the values in $data.
2255       *                                   If string, that format will be used for all of the values in $data.
2256       *                                   A format is one of '%d', '%f', '%s' (integer, float, string).
2257       *                                   If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
2258       * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where.
2259       *                                   If string, that format will be used for all of the items in $where.
2260       *                                   A format is one of '%d', '%f', '%s' (integer, float, string).
2261       *                                   If omitted, all values in $where will be treated as strings.
2262       * @return int|false The number of rows updated, or false on error.
2263       */
2264  	public function update( $table, $data, $where, $format = null, $where_format = null ) {
2265          if ( ! is_array( $data ) || ! is_array( $where ) ) {
2266              return false;
2267          }
2268  
2269          $data = $this->process_fields( $table, $data, $format );
2270          if ( false === $data ) {
2271              return false;
2272          }
2273          $where = $this->process_fields( $table, $where, $where_format );
2274          if ( false === $where ) {
2275              return false;
2276          }
2277  
2278          $fields     = array();
2279          $conditions = array();
2280          $values     = array();
2281          foreach ( $data as $field => $value ) {
2282              if ( is_null( $value['value'] ) ) {
2283                  $fields[] = "`$field` = NULL";
2284                  continue;
2285              }
2286  
2287              $fields[] = "`$field` = " . $value['format'];
2288              $values[] = $value['value'];
2289          }
2290          foreach ( $where as $field => $value ) {
2291              if ( is_null( $value['value'] ) ) {
2292                  $conditions[] = "`$field` IS NULL";
2293                  continue;
2294              }
2295  
2296              $conditions[] = "`$field` = " . $value['format'];
2297              $values[]     = $value['value'];
2298          }
2299  
2300          $fields     = implode( ', ', $fields );
2301          $conditions = implode( ' AND ', $conditions );
2302  
2303          $sql = "UPDATE `$table` SET $fields WHERE $conditions";
2304  
2305          $this->check_current_query = false;
2306          return $this->query( $this->prepare( $sql, $values ) );
2307      }
2308  
2309      /**
2310       * Delete a row in the table
2311       *
2312       *     wpdb::delete( 'table', array( 'ID' => 1 ) )
2313       *     wpdb::delete( 'table', array( 'ID' => 1 ), array( '%d' ) )
2314       *
2315       * @since 3.4.0
2316       * @see wpdb::prepare()
2317       * @see wpdb::$field_types
2318       * @see wp_set_wpdb_vars()
2319       *
2320       * @param string       $table        Table name
2321       * @param array        $where        A named array of WHERE clauses (in column => value pairs).
2322       *                                   Multiple clauses will be joined with ANDs.
2323       *                                   Both $where columns and $where values should be "raw".
2324       *                                   Sending a null value will create an IS NULL comparison - the corresponding format will be ignored in this case.
2325       * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where.
2326       *                                   If string, that format will be used for all of the items in $where.
2327       *                                   A format is one of '%d', '%f', '%s' (integer, float, string).
2328       *                                   If omitted, all values in $where will be treated as strings unless otherwise specified in wpdb::$field_types.
2329       * @return int|false The number of rows updated, or false on error.
2330       */
2331  	public function delete( $table, $where, $where_format = null ) {
2332          if ( ! is_array( $where ) ) {
2333              return false;
2334          }
2335  
2336          $where = $this->process_fields( $table, $where, $where_format );
2337          if ( false === $where ) {
2338              return false;
2339          }
2340  
2341          $conditions = array();
2342          $values     = array();
2343          foreach ( $where as $field => $value ) {
2344              if ( is_null( $value['value'] ) ) {
2345                  $conditions[] = "`$field` IS NULL";
2346                  continue;
2347              }
2348  
2349              $conditions[] = "`$field` = " . $value['format'];
2350              $values[]     = $value['value'];
2351          }
2352  
2353          $conditions = implode( ' AND ', $conditions );
2354  
2355          $sql = "DELETE FROM `$table` WHERE $conditions";
2356  
2357          $this->check_current_query = false;
2358          return $this->query( $this->prepare( $sql, $values ) );
2359      }
2360  
2361      /**
2362       * Processes arrays of field/value pairs and field formats.
2363       *
2364       * This is a helper method for wpdb's CRUD methods, which take field/value
2365       * pairs for inserts, updates, and where clauses. This method first pairs
2366       * each value with a format. Then it determines the charset of that field,
2367       * using that to determine if any invalid text would be stripped. If text is
2368       * stripped, then field processing is rejected and the query fails.
2369       *
2370       * @since 4.2.0
2371       *
2372       * @param string $table  Table name.
2373       * @param array  $data   Field/value pair.
2374       * @param mixed  $format Format for each field.
2375       * @return array|false Returns an array of fields that contain paired values
2376       *                    and formats. Returns false for invalid values.
2377       */
2378  	protected function process_fields( $table, $data, $format ) {
2379          $data = $this->process_field_formats( $data, $format );
2380          if ( false === $data ) {
2381              return false;
2382          }
2383  
2384          $data = $this->process_field_charsets( $data, $table );
2385          if ( false === $data ) {
2386              return false;
2387          }
2388  
2389          $data = $this->process_field_lengths( $data, $table );
2390          if ( false === $data ) {
2391              return false;
2392          }
2393  
2394          $converted_data = $this->strip_invalid_text( $data );
2395  
2396          if ( $data !== $converted_data ) {
2397              return false;
2398          }
2399  
2400          return $data;
2401      }
2402  
2403      /**
2404       * Prepares arrays of value/format pairs as passed to wpdb CRUD methods.
2405       *
2406       * @since 4.2.0
2407       *
2408       * @param array $data   Array of fields to values.
2409       * @param mixed $format Formats to be mapped to the values in $data.
2410       * @return array Array, keyed by field names with values being an array
2411       *               of 'value' and 'format' keys.
2412       */
2413  	protected function process_field_formats( $data, $format ) {
2414          $formats          = (array) $format;
2415          $original_formats = $formats;
2416  
2417          foreach ( $data as $field => $value ) {
2418              $value = array(
2419                  'value'  => $value,
2420                  'format' => '%s',
2421              );
2422  
2423              if ( ! empty( $format ) ) {
2424                  $value['format'] = array_shift( $formats );
2425                  if ( ! $value['format'] ) {
2426                      $value['format'] = reset( $original_formats );
2427                  }
2428              } elseif ( isset( $this->field_types[ $field ] ) ) {
2429                  $value['format'] = $this->field_types[ $field ];
2430              }
2431  
2432              $data[ $field ] = $value;
2433          }
2434  
2435          return $data;
2436      }
2437  
2438      /**
2439       * Adds field charsets to field/value/format arrays generated by
2440       * the wpdb::process_field_formats() method.
2441       *
2442       * @since 4.2.0
2443       *
2444       * @param array  $data  As it comes from the wpdb::process_field_formats() method.
2445       * @param string $table Table name.
2446       * @return array|false The same array as $data with additional 'charset' keys.
2447       */
2448  	protected function process_field_charsets( $data, $table ) {
2449          foreach ( $data as $field => $value ) {
2450              if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
2451                  /*
2452                   * We can skip this field if we know it isn't a string.
2453                   * This checks %d/%f versus ! %s because its sprintf() could take more.
2454                   */
2455                  $value['charset'] = false;
2456              } else {
2457                  $value['charset'] = $this->get_col_charset( $table, $field );
2458                  if ( is_wp_error( $value['charset'] ) ) {
2459                      return false;
2460                  }
2461              }
2462  
2463              $data[ $field ] = $value;
2464          }
2465  
2466          return $data;
2467      }
2468  
2469      /**
2470       * For string fields, record the maximum string length that field can safely save.
2471       *
2472       * @since 4.2.1
2473       *
2474       * @param array  $data  As it comes from the wpdb::process_field_charsets() method.
2475       * @param string $table Table name.
2476       * @return array|false The same array as $data with additional 'length' keys, or false if
2477       *                     any of the values were too long for their corresponding field.
2478       */
2479  	protected function process_field_lengths( $data, $table ) {
2480          foreach ( $data as $field => $value ) {
2481              if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
2482                  /*
2483                   * We can skip this field if we know it isn't a string.
2484                   * This checks %d/%f versus ! %s because its sprintf() could take more.
2485                   */
2486                  $value['length'] = false;
2487              } else {
2488                  $value['length'] = $this->get_col_length( $table, $field );
2489                  if ( is_wp_error( $value['length'] ) ) {
2490                      return false;
2491                  }
2492              }
2493  
2494              $data[ $field ] = $value;
2495          }
2496  
2497          return $data;
2498      }
2499  
2500      /**
2501       * Retrieve one variable from the database.
2502       *
2503       * Executes a SQL query and returns the value from the SQL result.
2504       * If the SQL result contains more than one column and/or more than one row, this function returns the value in the column and row specified.
2505       * If $query is null, this function returns the value in the specified column and row from the previous SQL result.
2506       *
2507       * @since 0.71
2508       *
2509       * @param string|null $query Optional. SQL query. Defaults to null, use the result from the previous query.
2510       * @param int         $x     Optional. Column of value to return. Indexed from 0.
2511       * @param int         $y     Optional. Row of value to return. Indexed from 0.
2512       * @return string|null Database query result (as string), or null on failure
2513       */
2514  	public function get_var( $query = null, $x = 0, $y = 0 ) {
2515          $this->func_call = "\$db->get_var(\"$query\", $x, $y)";
2516  
2517          if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2518              $this->check_current_query = false;
2519          }
2520  
2521          if ( $query ) {
2522              $this->query( $query );
2523          }
2524  
2525          // Extract var out of cached results based x,y vals
2526          if ( ! empty( $this->last_result[ $y ] ) ) {
2527              $values = array_values( get_object_vars( $this->last_result[ $y ] ) );
2528          }
2529  
2530          // If there is a value return it else return null
2531          return ( isset( $values[ $x ] ) && $values[ $x ] !== '' ) ? $values[ $x ] : null;
2532      }
2533  
2534      /**
2535       * Retrieve one row from the database.
2536       *
2537       * Executes a SQL query and returns the row from the SQL result.
2538       *
2539       * @since 0.71
2540       *
2541       * @param string|null $query  SQL query.
2542       * @param string      $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
2543       *                            an stdClass object, an associative array, or a numeric array, respectively. Default OBJECT.
2544       * @param int         $y      Optional. Row to return. Indexed from 0.
2545       * @return array|object|null|void Database query result in format specified by $output or null on failure
2546       */
2547  	public function get_row( $query = null, $output = OBJECT, $y = 0 ) {
2548          $this->func_call = "\$db->get_row(\"$query\",$output,$y)";
2549  
2550          if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2551              $this->check_current_query = false;
2552          }
2553  
2554          if ( $query ) {
2555              $this->query( $query );
2556          } else {
2557              return null;
2558          }
2559  
2560          if ( ! isset( $this->last_result[ $y ] ) ) {
2561              return null;
2562          }
2563  
2564          if ( $output == OBJECT ) {
2565              return $this->last_result[ $y ] ? $this->last_result[ $y ] : null;
2566          } elseif ( $output == ARRAY_A ) {
2567              return $this->last_result[ $y ] ? get_object_vars( $this->last_result[ $y ] ) : null;
2568          } elseif ( $output == ARRAY_N ) {
2569              return $this->last_result[ $y ] ? array_values( get_object_vars( $this->last_result[ $y ] ) ) : null;
2570          } elseif ( strtoupper( $output ) === OBJECT ) {
2571              // Back compat for OBJECT being previously case insensitive.
2572              return $this->last_result[ $y ] ? $this->last_result[ $y ] : null;
2573          } else {
2574              $this->print_error( ' $db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N' );
2575          }
2576      }
2577  
2578      /**
2579       * Retrieve one column from the database.
2580       *
2581       * Executes a SQL query and returns the column from the SQL result.
2582       * If the SQL result contains more than one column, this function returns the column specified.
2583       * If $query is null, this function returns the specified column from the previous SQL result.
2584       *
2585       * @since 0.71
2586       *
2587       * @param string|null $query Optional. SQL query. Defaults to previous query.
2588       * @param int         $x     Optional. Column to return. Indexed from 0.
2589       * @return array Database query result. Array indexed from 0 by SQL result row number.
2590       */
2591  	public function get_col( $query = null, $x = 0 ) {
2592          if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2593              $this->check_current_query = false;
2594          }
2595  
2596          if ( $query ) {
2597              $this->query( $query );
2598          }
2599  
2600          $new_array = array();
2601          // Extract the column values
2602          if ( $this->last_result ) {
2603              for ( $i = 0, $j = count( $this->last_result ); $i < $j; $i++ ) {
2604                  $new_array[ $i ] = $this->get_var( null, $x, $i );
2605              }
2606          }
2607          return $new_array;
2608      }
2609  
2610      /**
2611       * Retrieve an entire SQL result set from the database (i.e., many rows)
2612       *
2613       * Executes a SQL query and returns the entire SQL result.
2614       *
2615       * @since 0.71
2616       *
2617       * @param string $query  SQL query.
2618       * @param string $output Optional. Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants.
2619       *                       With one of the first three, return an array of rows indexed from 0 by SQL result row number.
2620       *                       Each row is an associative array (column => value, ...), a numerically indexed array (0 => value, ...), or an object. ( ->column = value ), respectively.
2621       *                       With OBJECT_K, return an associative array of row objects keyed by the value of each row's first column's value.
2622       *                       Duplicate keys are discarded.
2623       * @return array|object|null Database query results
2624       */
2625  	public function get_results( $query = null, $output = OBJECT ) {
2626          $this->func_call = "\$db->get_results(\"$query\", $output)";
2627  
2628          if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2629              $this->check_current_query = false;
2630          }
2631  
2632          if ( $query ) {
2633              $this->query( $query );
2634          } else {
2635              return null;
2636          }
2637  
2638          $new_array = array();
2639          if ( $output == OBJECT ) {
2640              // Return an integer-keyed array of row objects
2641              return $this->last_result;
2642          } elseif ( $output == OBJECT_K ) {
2643              // Return an array of row objects with keys from column 1
2644              // (Duplicates are discarded)
2645              if ( $this->last_result ) {
2646                  foreach ( $this->last_result as $row ) {
2647                      $var_by_ref = get_object_vars( $row );
2648                      $key        = array_shift( $var_by_ref );
2649                      if ( ! isset( $new_array[ $key ] ) ) {
2650                          $new_array[ $key ] = $row;
2651                      }
2652                  }
2653              }
2654              return $new_array;
2655          } elseif ( $output == ARRAY_A || $output == ARRAY_N ) {
2656              // Return an integer-keyed array of...
2657              if ( $this->last_result ) {
2658                  foreach ( (array) $this->last_result as $row ) {
2659                      if ( $output == ARRAY_N ) {
2660                          // ...integer-keyed row arrays
2661                          $new_array[] = array_values( get_object_vars( $row ) );
2662                      } else {
2663                          // ...column name-keyed row arrays
2664                          $new_array[] = get_object_vars( $row );
2665                      }
2666                  }
2667              }
2668              return $new_array;
2669          } elseif ( strtoupper( $output ) === OBJECT ) {
2670              // Back compat for OBJECT being previously case insensitive.
2671              return $this->last_result;
2672          }
2673          return null;
2674      }
2675  
2676      /**
2677       * Retrieves the character set for the given table.
2678       *
2679       * @since 4.2.0
2680       *
2681       * @param string $table Table name.
2682       * @return string|WP_Error Table character set, WP_Error object if it couldn't be found.
2683       */
2684  	protected function get_table_charset( $table ) {
2685          $tablekey = strtolower( $table );
2686  
2687          /**
2688           * Filters the table charset value before the DB is checked.
2689           *
2690           * Passing a non-null value to the filter will effectively short-circuit
2691           * checking the DB for the charset, returning that value instead.
2692           *
2693           * @since 4.2.0
2694           *
2695           * @param string $charset The character set to use. Default null.
2696           * @param string $table   The name of the table being checked.
2697           */
2698          $charset = apply_filters( 'pre_get_table_charset', null, $table );
2699          if ( null !== $charset ) {
2700              return $charset;
2701          }
2702  
2703          if ( isset( $this->table_charset[ $tablekey ] ) ) {
2704              return $this->table_charset[ $tablekey ];
2705          }
2706  
2707          $charsets = array();
2708          $columns  = array();
2709  
2710          $table_parts = explode( '.', $table );
2711          $table       = '`' . implode( '`.`', $table_parts ) . '`';
2712          $results     = $this->get_results( "SHOW FULL COLUMNS FROM $table" );
2713          if ( ! $results ) {
2714              return new WP_Error( 'wpdb_get_table_charset_failure' );
2715          }
2716  
2717          foreach ( $results as $column ) {
2718              $columns[ strtolower( $column->Field ) ] = $column;
2719          }
2720  
2721          $this->col_meta[ $tablekey ] = $columns;
2722  
2723          foreach ( $columns as $column ) {
2724              if ( ! empty( $column->Collation ) ) {
2725                  list( $charset ) = explode( '_', $column->Collation );
2726  
2727                  // If the current connection can't support utf8mb4 characters, let's only send 3-byte utf8 characters.
2728                  if ( 'utf8mb4' === $charset && ! $this->has_cap( 'utf8mb4' ) ) {
2729                      $charset = 'utf8';
2730                  }
2731  
2732                  $charsets[ strtolower( $charset ) ] = true;
2733              }
2734  
2735              list( $type ) = explode( '(', $column->Type );
2736  
2737              // A binary/blob means the whole query gets treated like this.
2738              if ( in_array( strtoupper( $type ), array( 'BINARY', 'VARBINARY', 'TINYBLOB', 'MEDIUMBLOB', 'BLOB', 'LONGBLOB' ) ) ) {
2739                  $this->table_charset[ $tablekey ] = 'binary';
2740                  return 'binary';
2741              }
2742          }
2743  
2744          // utf8mb3 is an alias for utf8.
2745          if ( isset( $charsets['utf8mb3'] ) ) {
2746              $charsets['utf8'] = true;
2747              unset( $charsets['utf8mb3'] );
2748          }
2749  
2750          // Check if we have more than one charset in play.
2751          $count = count( $charsets );
2752          if ( 1 === $count ) {
2753              $charset = key( $charsets );
2754          } elseif ( 0 === $count ) {
2755              // No charsets, assume this table can store whatever.
2756              $charset = false;
2757          } else {
2758              // More than one charset. Remove latin1 if present and recalculate.
2759              unset( $charsets['latin1'] );
2760              $count = count( $charsets );
2761              if ( 1 === $count ) {
2762                  // Only one charset (besides latin1).
2763                  $charset = key( $charsets );
2764              } elseif ( 2 === $count && isset( $charsets['utf8'], $charsets['utf8mb4'] ) ) {
2765                  // Two charsets, but they're utf8 and utf8mb4, use utf8.
2766                  $charset = 'utf8';
2767              } else {
2768                  // Two mixed character sets. ascii.
2769                  $charset = 'ascii';
2770              }
2771          }
2772  
2773          $this->table_charset[ $tablekey ] = $charset;
2774          return $charset;
2775      }
2776  
2777      /**
2778       * Retrieves the character set for the given column.
2779       *
2780       * @since 4.2.0
2781       *
2782       * @param string $table  Table name.
2783       * @param string $column Column name.
2784       * @return string|false|WP_Error Column character set as a string. False if the column has no
2785       *                               character set. WP_Error object if there was an error.
2786       */
2787  	public function get_col_charset( $table, $column ) {
2788          $tablekey  = strtolower( $table );
2789          $columnkey = strtolower( $column );
2790  
2791          /**
2792           * Filters the column charset value before the DB is checked.
2793           *
2794           * Passing a non-null value to the filter will short-circuit
2795           * checking the DB for the charset, returning that value instead.
2796           *
2797           * @since 4.2.0
2798           *
2799           * @param string $charset The character set to use. Default null.
2800           * @param string $table   The name of the table being checked.
2801           * @param string $column  The name of the column being checked.
2802           */
2803          $charset = apply_filters( 'pre_get_col_charset', null, $table, $column );
2804          if ( null !== $charset ) {
2805              return $charset;
2806          }
2807  
2808          // Skip this entirely if this isn't a MySQL database.
2809          if ( empty( $this->is_mysql ) ) {
2810              return false;
2811          }
2812  
2813          if ( empty( $this->table_charset[ $tablekey ] ) ) {
2814              // This primes column information for us.
2815              $table_charset = $this->get_table_charset( $table );
2816              if ( is_wp_error( $table_charset ) ) {
2817                  return $table_charset;
2818              }
2819          }
2820  
2821          // If still no column information, return the table charset.
2822          if ( empty( $this->col_meta[ $tablekey ] ) ) {
2823              return $this->table_charset[ $tablekey ];
2824          }
2825  
2826          // If this column doesn't exist, return the table charset.
2827          if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {
2828              return $this->table_charset[ $tablekey ];
2829          }
2830  
2831          // Return false when it's not a string column.
2832          if ( empty( $this->col_meta[ $tablekey ][ $columnkey ]->Collation ) ) {
2833              return false;
2834          }
2835  
2836          list( $charset ) = explode( '_', $this->col_meta[ $tablekey ][ $columnkey ]->Collation );
2837          return $charset;
2838      }
2839  
2840      /**
2841       * Retrieve the maximum string length allowed in a given column.
2842       * The length may either be specified as a byte length or a character length.
2843       *
2844       * @since 4.2.1
2845       *
2846       * @param string $table  Table name.
2847       * @param string $column Column name.
2848       * @return array|false|WP_Error array( 'length' => (int), 'type' => 'byte' | 'char' )
2849       *                              false if the column has no length (for example, numeric column)
2850       *                              WP_Error object if there was an error.
2851       */
2852  	public function get_col_length( $table, $column ) {
2853          $tablekey  = strtolower( $table );
2854          $columnkey = strtolower( $column );
2855  
2856          // Skip this entirely if this isn't a MySQL database.
2857          if ( empty( $this->is_mysql ) ) {
2858              return false;
2859          }
2860  
2861          if ( empty( $this->col_meta[ $tablekey ] ) ) {
2862              // This primes column information for us.
2863              $table_charset = $this->get_table_charset( $table );
2864              if ( is_wp_error( $table_charset ) ) {
2865                  return $table_charset;
2866              }
2867          }
2868  
2869          if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {
2870              return false;
2871          }
2872  
2873          $typeinfo = explode( '(', $this->col_meta[ $tablekey ][ $columnkey ]->Type );
2874  
2875          $type = strtolower( $typeinfo[0] );
2876          if ( ! empty( $typeinfo[1] ) ) {
2877              $length = trim( $typeinfo[1], ')' );
2878          } else {
2879              $length = false;
2880          }
2881  
2882          switch ( $type ) {
2883              case 'char':
2884              case 'varchar':
2885                  return array(
2886                      'type'   => 'char',
2887                      'length' => (int) $length,
2888                  );
2889  
2890              case 'binary':
2891              case 'varbinary':
2892                  return array(
2893                      'type'   => 'byte',
2894                      'length' => (int) $length,
2895                  );
2896  
2897              case 'tinyblob':
2898              case 'tinytext':
2899                  return array(
2900                      'type'   => 'byte',
2901                      'length' => 255,        // 2^8 - 1
2902                  );
2903  
2904              case 'blob':
2905              case 'text':
2906                  return array(
2907                      'type'   => 'byte',
2908                      'length' => 65535,      // 2^16 - 1
2909                  );
2910  
2911              case 'mediumblob':
2912              case 'mediumtext':
2913                  return array(
2914                      'type'   => 'byte',
2915                      'length' => 16777215,   // 2^24 - 1
2916                  );
2917  
2918              case 'longblob':
2919              case 'longtext':
2920                  return array(
2921                      'type'   => 'byte',
2922                      'length' => 4294967295, // 2^32 - 1
2923                  );
2924  
2925              default:
2926                  return false;
2927          }
2928      }
2929  
2930      /**
2931       * Check if a string is ASCII.
2932       *
2933       * The negative regex is faster for non-ASCII strings, as it allows
2934       * the search to finish as soon as it encounters a non-ASCII character.
2935       *
2936       * @since 4.2.0
2937       *
2938       * @param string $string String to check.
2939       * @return bool True if ASCII, false if not.
2940       */
2941  	protected function check_ascii( $string ) {
2942          if ( function_exists( 'mb_check_encoding' ) ) {
2943              if ( mb_check_encoding( $string, 'ASCII' ) ) {
2944                  return true;
2945              }
2946          } elseif ( ! preg_match( '/[^\x00-\x7F]/', $string ) ) {
2947              return true;
2948          }
2949  
2950          return false;
2951      }
2952  
2953      /**
2954       * Check if the query is accessing a collation considered safe on the current version of MySQL.
2955       *
2956       * @since 4.2.0
2957       *
2958       * @param string $query The query to check.
2959       * @return bool True if the collation is safe, false if it isn't.
2960       */
2961  	protected function check_safe_collation( $query ) {
2962          if ( $this->checking_collation ) {
2963              return true;
2964          }
2965  
2966          // We don't need to check the collation for queries that don't read data.
2967          $query = ltrim( $query, "\r\n\t (" );
2968          if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $query ) ) {
2969              return true;
2970          }
2971  
2972          // All-ASCII queries don't need extra checking.
2973          if ( $this->check_ascii( $query ) ) {
2974              return true;
2975          }
2976  
2977          $table = $this->get_table_from_query( $query );
2978          if ( ! $table ) {
2979              return false;
2980          }
2981  
2982          $this->checking_collation = true;
2983          $collation                = $this->get_table_charset( $table );
2984          $this->checking_collation = false;
2985  
2986          // Tables with no collation, or latin1 only, don't need extra checking.
2987          if ( false === $collation || 'latin1' === $collation ) {
2988              return true;
2989          }
2990  
2991          $table = strtolower( $table );
2992          if ( empty( $this->col_meta[ $table ] ) ) {
2993              return false;
2994          }
2995  
2996          // If any of the columns don't have one of these collations, it needs more sanity checking.
2997          foreach ( $this->col_meta[ $table ] as $col ) {
2998              if ( empty( $col->Collation ) ) {
2999                  continue;
3000              }
3001  
3002              if ( ! in_array( $col->Collation, array( 'utf8_general_ci', 'utf8_bin', 'utf8mb4_general_ci', 'utf8mb4_bin' ), true ) ) {
3003                  return false;
3004              }
3005          }
3006  
3007          return true;
3008      }
3009  
3010      /**
3011       * Strips any invalid characters based on value/charset pairs.
3012       *
3013       * @since 4.2.0
3014       *
3015       * @param array $data Array of value arrays. Each value array has the keys
3016       *                    'value' and 'charset'. An optional 'ascii' key can be
3017       *                    set to false to avoid redundant ASCII checks.
3018       * @return array|WP_Error The $data parameter, with invalid characters removed from
3019       *                        each value. This works as a passthrough: any additional keys
3020       *                        such as 'field' are retained in each value array. If we cannot
3021       *                        remove invalid characters, a WP_Error object is returned.
3022       */
3023  	protected function strip_invalid_text( $data ) {
3024          $db_check_string = false;
3025  
3026          foreach ( $data as &$value ) {
3027              $charset = $value['charset'];
3028  
3029              if ( is_array( $value['length'] ) ) {
3030                  $length                  = $value['length']['length'];
3031                  $truncate_by_byte_length = 'byte' === $value['length']['type'];
3032              } else {
3033                  $length = false;
3034                  // Since we have no length, we'll never truncate.
3035                  // Initialize the variable to false. true would take us
3036                  // through an unnecessary (for this case) codepath below.
3037                  $truncate_by_byte_length = false;
3038              }
3039  
3040              // There's no charset to work with.
3041              if ( false === $charset ) {
3042                  continue;
3043              }
3044  
3045              // Column isn't a string.
3046              if ( ! is_string( $value['value'] ) ) {
3047                  continue;
3048              }
3049  
3050              $needs_validation = true;
3051              if (
3052                  // latin1 can store any byte sequence
3053                  'latin1' === $charset
3054              ||
3055                  // ASCII is always OK.
3056                  ( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) )
3057              ) {
3058                  $truncate_by_byte_length = true;
3059                  $needs_validation        = false;
3060              }
3061  
3062              if ( $truncate_by_byte_length ) {
3063                  mbstring_binary_safe_encoding();
3064                  if ( false !== $length && strlen( $value['value'] ) > $length ) {
3065                      $value['value'] = substr( $value['value'], 0, $length );
3066                  }
3067                  reset_mbstring_encoding();
3068  
3069                  if ( ! $needs_validation ) {
3070                      continue;
3071                  }
3072              }
3073  
3074              // utf8 can be handled by regex, which is a bunch faster than a DB lookup.
3075              if ( ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) && function_exists( 'mb_strlen' ) ) {
3076                  $regex = '/
3077                      (
3078                          (?: [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
3079                          |   [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
3080                          |   \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
3081                          |   [\xE1-\xEC][\x80-\xBF]{2}
3082                          |   \xED[\x80-\x9F][\x80-\xBF]
3083                          |   [\xEE-\xEF][\x80-\xBF]{2}';
3084  
3085                  if ( 'utf8mb4' === $charset ) {
3086                      $regex .= '
3087                          |    \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
3088                          |    [\xF1-\xF3][\x80-\xBF]{3}
3089                          |    \xF4[\x80-\x8F][\x80-\xBF]{2}
3090                      ';
3091                  }
3092  
3093                  $regex         .= '){1,40}                          # ...one or more times
3094                      )
3095                      | .                                  # anything else
3096                      /x';
3097                  $value['value'] = preg_replace( $regex, '$1', $value['value'] );
3098  
3099                  if ( false !== $length && mb_strlen( $value['value'], 'UTF-8' ) > $length ) {
3100                      $value['value'] = mb_substr( $value['value'], 0, $length, 'UTF-8' );
3101                  }
3102                  continue;
3103              }
3104  
3105              // We couldn't use any local conversions, send it to the DB.
3106              $value['db']     = true;
3107              $db_check_string = true;
3108          }
3109          unset( $value ); // Remove by reference.
3110  
3111          if ( $db_check_string ) {
3112              $queries = array();
3113              foreach ( $data as $col => $value ) {
3114                  if ( ! empty( $value['db'] ) ) {
3115                      // We're going to need to truncate by characters or bytes, depending on the length value we have.
3116                      if ( isset( $value['length']['type'] ) && 'byte' === $value['length']['type'] ) {
3117                          // Using binary causes LEFT() to truncate by bytes.
3118                          $charset = 'binary';
3119                      } else {
3120                          $charset = $value['charset'];
3121                      }
3122  
3123                      if ( $this->charset ) {
3124                          $connection_charset = $this->charset;
3125                      } else {
3126                          if ( $this->use_mysqli ) {
3127                              $connection_charset = mysqli_character_set_name( $this->dbh );
3128                          } else {
3129                              $connection_charset = mysql_client_encoding();
3130                          }
3131                      }
3132  
3133                      if ( is_array( $value['length'] ) ) {
3134                          $length          = sprintf( '%.0f', $value['length']['length'] );
3135                          $queries[ $col ] = $this->prepare( "CONVERT( LEFT( CONVERT( %s USING $charset ), $length ) USING $connection_charset )", $value['value'] );
3136                      } elseif ( 'binary' !== $charset ) {
3137                          // If we don't have a length, there's no need to convert binary - it will always return the same result.
3138                          $queries[ $col ] = $this->prepare( "CONVERT( CONVERT( %s USING $charset ) USING $connection_charset )", $value['value'] );
3139                      }
3140  
3141                      unset( $data[ $col ]['db'] );
3142                  }
3143              }
3144  
3145              $sql = array();
3146              foreach ( $queries as $column => $query ) {
3147                  if ( ! $query ) {
3148                      continue;
3149                  }
3150  
3151                  $sql[] = $query . " AS x_$column";
3152              }
3153  
3154              $this->check_current_query = false;
3155              $row                       = $this->get_row( 'SELECT ' . implode( ', ', $sql ), ARRAY_A );
3156              if ( ! $row ) {
3157                  return new WP_Error( 'wpdb_strip_invalid_text_failure' );
3158              }
3159  
3160              foreach ( array_keys( $data ) as $column ) {
3161                  if ( isset( $row[ "x_$column" ] ) ) {
3162                      $data[ $column ]['value'] = $row[ "x_$column" ];
3163                  }
3164              }
3165          }
3166  
3167          return $data;
3168      }
3169  
3170      /**
3171       * Strips any invalid characters from the query.
3172       *
3173       * @since 4.2.0
3174       *
3175       * @param string $query Query to convert.
3176       * @return string|WP_Error The converted query, or a WP_Error object if the conversion fails.
3177       */
3178  	protected function strip_invalid_text_from_query( $query ) {
3179          // We don't need to check the collation for queries that don't read data.
3180          $trimmed_query = ltrim( $query, "\r\n\t (" );
3181          if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $trimmed_query ) ) {
3182              return $query;
3183          }
3184  
3185          $table = $this->get_table_from_query( $query );
3186          if ( $table ) {
3187              $charset = $this->get_table_charset( $table );
3188              if ( is_wp_error( $charset ) ) {
3189                  return $charset;
3190              }
3191  
3192              // We can't reliably strip text from tables containing binary/blob columns
3193              if ( 'binary' === $charset ) {
3194                  return $query;
3195              }
3196          } else {
3197              $charset = $this->charset;
3198          }
3199  
3200          $data = array(
3201              'value'   => $query,
3202              'charset' => $charset,
3203              'ascii'   => false,
3204              'length'  => false,
3205          );
3206  
3207          $data = $this->strip_invalid_text( array( $data ) );
3208          if ( is_wp_error( $data ) ) {
3209              return $data;
3210          }
3211  
3212          return $data[0]['value'];
3213      }
3214  
3215      /**
3216       * Strips any invalid characters from the string for a given table and column.
3217       *
3218       * @since 4.2.0
3219       *
3220       * @param string $table  Table name.
3221       * @param string $column Column name.
3222       * @param string $value  The text to check.
3223       * @return string|WP_Error The converted string, or a WP_Error object if the conversion fails.
3224       */
3225  	public function strip_invalid_text_for_column( $table, $column, $value ) {
3226          if ( ! is_string( $value ) ) {
3227              return $value;
3228          }
3229  
3230          $charset = $this->get_col_charset( $table, $column );
3231          if ( ! $charset ) {
3232              // Not a string column.
3233              return $value;
3234          } elseif ( is_wp_error( $charset ) ) {
3235              // Bail on real errors.
3236              return $charset;
3237          }
3238  
3239          $data = array(
3240              $column => array(
3241                  'value'   => $value,
3242                  'charset' => $charset,
3243                  'length'  => $this->get_col_length( $table, $column ),
3244              ),
3245          );
3246  
3247          $data = $this->strip_invalid_text( $data );
3248          if ( is_wp_error( $data ) ) {
3249              return $data;
3250          }
3251  
3252          return $data[ $column ]['value'];
3253      }
3254  
3255      /**
3256       * Find the first table name referenced in a query.
3257       *
3258       * @since 4.2.0
3259       *
3260       * @param string $query The query to search.
3261       * @return string|false $table The table name found, or false if a table couldn't be found.
3262       */
3263  	protected function get_table_from_query( $query ) {
3264          // Remove characters that can legally trail the table name.
3265          $query = rtrim( $query, ';/-#' );
3266  
3267          // Allow (select...) union [...] style queries. Use the first query's table name.
3268          $query = ltrim( $query, "\r\n\t (" );
3269  
3270          // Strip everything between parentheses except nested selects.
3271          $query = preg_replace( '/\((?!\s*select)[^(]*?\)/is', '()', $query );
3272  
3273          // Quickly match most common queries.
3274          if ( preg_match(
3275              '/^\s*(?:'
3276                  . 'SELECT.*?\s+FROM'
3277                  . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'
3278                  . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
3279                  . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
3280                  . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:.+?FROM)?'
3281              . ')\s+((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)/is',
3282              $query,
3283              $maybe
3284          ) ) {
3285              return str_replace( '`', '', $maybe[1] );
3286          }
3287  
3288          // SHOW TABLE STATUS and SHOW TABLES WHERE Name = 'wp_posts'
3289          if ( preg_match( '/^\s*SHOW\s+(?:TABLE\s+STATUS|(?:FULL\s+)?TABLES).+WHERE\s+Name\s*=\s*("|\')((?:[0-9a-zA-Z$_.-]|[\xC2-\xDF][\x80-\xBF])+)\\1/is', $query, $maybe ) ) {
3290              return $maybe[2];
3291          }
3292  
3293          // SHOW TABLE STATUS LIKE and SHOW TABLES LIKE 'wp\_123\_%'
3294          // This quoted LIKE operand seldom holds a full table name.
3295          // It is usually a pattern for matching a prefix so we just
3296          // strip the trailing % and unescape the _ to get 'wp_123_'
3297          // which drop-ins can use for routing these SQL statements.
3298          if ( preg_match( '/^\s*SHOW\s+(?:TABLE\s+STATUS|(?:FULL\s+)?TABLES)\s+(?:WHERE\s+Name\s+)?LIKE\s*("|\')((?:[\\\\0-9a-zA-Z$_.-]|[\xC2-\xDF][\x80-\xBF])+)%?\\1/is', $query, $maybe ) ) {
3299              return str_replace( '\\_', '_', $maybe[2] );
3300          }
3301  
3302          // Big pattern for the rest of the table-related queries.
3303          if ( preg_match(
3304              '/^\s*(?:'
3305                  . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'
3306                  . '|DESCRIBE|DESC|EXPLAIN|HANDLER'
3307                  . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'
3308                  . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|REPAIR).*\s+TABLE'
3309                  . '|TRUNCATE(?:\s+TABLE)?'
3310                  . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'
3311                  . '|ALTER(?:\s+IGNORE)?\s+TABLE'
3312                  . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'
3313                  . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'
3314                  . '|DROP\s+INDEX.*\s+ON'
3315                  . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
3316                  . '|(?:GRANT|REVOKE).*ON\s+TABLE'
3317                  . '|SHOW\s+(?:.*FROM|.*TABLE)'
3318              . ')\s+\(*\s*((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)\s*\)*/is',
3319              $query,
3320              $maybe
3321          ) ) {
3322              return str_replace( '`', '', $maybe[1] );
3323          }
3324  
3325          return false;
3326      }
3327  
3328      /**
3329       * Load the column metadata from the last query.
3330       *
3331       * @since 3.5.0
3332       */
3333  	protected function load_col_info() {
3334          if ( $this->col_info ) {
3335              return;
3336          }
3337  
3338          if ( $this->use_mysqli ) {
3339              $num_fields = mysqli_num_fields( $this->result );
3340              for ( $i = 0; $i < $num_fields; $i++ ) {
3341                  $this->col_info[ $i ] = mysqli_fetch_field( $this->result );
3342              }
3343          } else {
3344              $num_fields = mysql_num_fields( $this->result );
3345              for ( $i = 0; $i < $num_fields; $i++ ) {
3346                  $this->col_info[ $i ] = mysql_fetch_field( $this->result, $i );
3347              }
3348          }
3349      }
3350  
3351      /**
3352       * Retrieve column metadata from the last query.
3353       *
3354       * @since 0.71
3355       *
3356       * @param string $info_type  Optional. Type one of name, table, def, max_length, not_null, primary_key, multiple_key, unique_key, numeric, blob, type, unsigned, zerofill
3357       * @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
3358       * @return mixed Column Results
3359       */
3360  	public function get_col_info( $info_type = 'name', $col_offset = -1 ) {
3361          $this->load_col_info();
3362  
3363          if ( $this->col_info ) {
3364              if ( $col_offset == -1 ) {
3365                  $i         = 0;
3366                  $new_array = array();
3367                  foreach ( (array) $this->col_info as $col ) {
3368                      $new_array[ $i ] = $col->{$info_type};
3369                      $i++;
3370                  }
3371                  return $new_array;
3372              } else {
3373                  return $this->col_info[ $col_offset ]->{$info_type};
3374              }
3375          }
3376      }
3377  
3378      /**
3379       * Starts the timer, for debugging purposes.
3380       *
3381       * @since 1.5.0
3382       *
3383       * @return true
3384       */
3385  	public function timer_start() {
3386          $this->time_start = microtime( true );
3387          return true;
3388      }
3389  
3390      /**
3391       * Stops the debugging timer.
3392       *
3393       * @since 1.5.0
3394       *
3395       * @return float Total time spent on the query, in seconds
3396       */
3397  	public function timer_stop() {
3398          return ( microtime( true ) - $this->time_start );
3399      }
3400  
3401      /**
3402       * Wraps errors in a nice header and footer and dies.
3403       *
3404       * Will not die if wpdb::$show_errors is false.
3405       *
3406       * @since 1.5.0
3407       *
3408       * @param string $message    The Error message
3409       * @param string $error_code Optional. A Computer readable string to identify the error.
3410       * @return false|void
3411       */
3412  	public function bail( $message, $error_code = '500' ) {
3413          if ( $this->show_errors ) {
3414              $error = '';
3415  
3416              if ( $this->use_mysqli ) {
3417                  if ( $this->dbh instanceof mysqli ) {
3418                      $error = mysqli_error( $this->dbh );
3419                  } elseif ( mysqli_connect_errno() ) {
3420                      $error = mysqli_connect_error();
3421                  }
3422              } else {
3423                  if ( is_resource( $this->dbh ) ) {
3424                      $error = mysql_error( $this->dbh );
3425                  } else {
3426                      $error = mysql_error();
3427                  }
3428              }
3429  
3430              if ( $error ) {
3431                  $message = '<p><code>' . $error . "</code></p>\n" . $message;
3432              }
3433  
3434              wp_die( $message );
3435          } else {
3436              if ( class_exists( 'WP_Error', false ) ) {
3437                  $this->error = new WP_Error( $error_code, $message );
3438              } else {
3439                  $this->error = $message;
3440              }
3441  
3442              return false;
3443          }
3444      }
3445  
3446  
3447      /**
3448       * Closes the current database connection.
3449       *
3450       * @since 4.5.0
3451       *
3452       * @return bool True if the connection was successfully closed, false if it wasn't,
3453       *              or the connection doesn't exist.
3454       */
3455  	public function close() {
3456          if ( ! $this->dbh ) {
3457              return false;
3458          }
3459  
3460          if ( $this->use_mysqli ) {
3461              $closed = mysqli_close( $this->dbh );
3462          } else {
3463              $closed = mysql_close( $this->dbh );
3464          }
3465  
3466          if ( $closed ) {
3467              $this->dbh           = null;
3468              $this->ready         = false;
3469              $this->has_connected = false;
3470          }
3471  
3472          return $closed;
3473      }
3474  
3475      /**
3476       * Whether MySQL database is at least the required minimum version.
3477       *
3478       * @since 2.5.0
3479       *
3480       * @global string $wp_version
3481       * @global string $required_mysql_version
3482       *
3483       * @return WP_Error|void
3484       */
3485  	public function check_database_version() {
3486          global $wp_version, $required_mysql_version;
3487          // Make sure the server has the required MySQL version
3488          if ( version_compare( $this->db_version(), $required_mysql_version, '<' ) ) {
3489              /* translators: 1: WordPress version number, 2: Minimum required MySQL version number */
3490              return new WP_Error( 'database_version', sprintf( __( '<strong>ERROR</strong>: WordPress %1$s requires MySQL %2$s or higher' ), $wp_version, $required_mysql_version ) );
3491          }
3492      }
3493  
3494      /**
3495       * Whether the database supports collation.
3496       *
3497       * Called when WordPress is generating the table scheme.
3498       *
3499       * Use `wpdb::has_cap( 'collation' )`.
3500       *
3501       * @since 2.5.0
3502       * @deprecated 3.5.0 Use wpdb::has_cap()
3503       *
3504       * @return bool True if collation is supported, false if version does not
3505       */
3506  	public function supports_collation() {
3507          _deprecated_function( __FUNCTION__, '3.5.0', 'wpdb::has_cap( \'collation\' )' );
3508          return $this->has_cap( 'collation' );
3509      }
3510  
3511      /**
3512       * The database character collate.
3513       *
3514       * @since 3.5.0
3515       *
3516       * @return string The database character collate.
3517       */
3518  	public function get_charset_collate() {
3519          $charset_collate = '';
3520  
3521          if ( ! empty( $this->charset ) ) {
3522              $charset_collate = "DEFAULT CHARACTER SET $this->charset";
3523          }
3524          if ( ! empty( $this->collate ) ) {
3525              $charset_collate .= " COLLATE $this->collate";
3526          }
3527  
3528          return $charset_collate;
3529      }
3530  
3531      /**
3532       * Determine if a database supports a particular feature.
3533       *
3534       * @since 2.7.0
3535       * @since 4.1.0 Added support for the 'utf8mb4' feature.
3536       * @since 4.6.0 Added support for the 'utf8mb4_520' feature.
3537       *
3538       * @see wpdb::db_version()
3539       *
3540       * @param string $db_cap The feature to check for. Accepts 'collation',
3541       *                       'group_concat', 'subqueries', 'set_charset',
3542       *                       'utf8mb4', or 'utf8mb4_520'.
3543       * @return int|false Whether the database feature is supported, false otherwise.
3544       */
3545  	public function has_cap( $db_cap ) {
3546          $version = $this->db_version();
3547  
3548          switch ( strtolower( $db_cap ) ) {
3549              case 'collation':    // @since 2.5.0
3550              case 'group_concat': // @since 2.7.0
3551              case 'subqueries':   // @since 2.7.0
3552                  return version_compare( $version, '4.1', '>=' );
3553              case 'set_charset':
3554                  return version_compare( $version, '5.0.7', '>=' );
3555              case 'utf8mb4':      // @since 4.1.0
3556                  if ( version_compare( $version, '5.5.3', '<' ) ) {
3557                      return false;
3558                  }
3559                  if ( $this->use_mysqli ) {
3560                      $client_version = mysqli_get_client_info();
3561                  } else {
3562                      $client_version = mysql_get_client_info();
3563                  }
3564  
3565                  /*
3566                   * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server.
3567                   * mysqlnd has supported utf8mb4 since 5.0.9.
3568                   */
3569                  if ( false !== strpos( $client_version, 'mysqlnd' ) ) {
3570                      $client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $client_version );
3571                      return version_compare( $client_version, '5.0.9', '>=' );
3572                  } else {
3573                      return version_compare( $client_version, '5.5.3', '>=' );
3574                  }
3575              case 'utf8mb4_520': // @since 4.6.0
3576                  return version_compare( $version, '5.6', '>=' );
3577          }
3578  
3579          return false;
3580      }
3581  
3582      /**
3583       * Retrieve the name of the function that called wpdb.
3584       *
3585       * Searches up the list of functions until it reaches
3586       * the one that would most logically had called this method.
3587       *
3588       * @since 2.5.0
3589       *
3590       * @return string Comma separated list of the calling functions.
3591       */
3592  	public function get_caller() {
3593          return wp_debug_backtrace_summary( __CLASS__ );
3594      }
3595  
3596      /**
3597       * Retrieves the MySQL server version.
3598       *
3599       * @since 2.7.0
3600       *
3601       * @return null|string Null on failure, version number on success.
3602       */
3603  	public function db_version() {
3604          if ( $this->use_mysqli ) {
3605              $server_info = mysqli_get_server_info( $this->dbh );
3606          } else {
3607              $server_info = mysql_get_server_info( $this->dbh );
3608          }
3609          return preg_replace( '/[^0-9.].*/', '', $server_info );
3610      }
3611  }


Generated: Tue Jul 16 01:00:03 2019 Cross-referenced by PHPXref 0.7.1