Make WordPress Core

Ticket #41956: wp-db.php

File wp-db.php, 93.7 KB (added by LeonN1960, 7 years ago)

my db file with last_errno

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