[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Mon Jul 26 01:00:04 2021 Cross-referenced by PHPXref 0.7.1