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