Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/wp-db.php (27 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * WordPress DB Class
4
 *
5
 * Original code from {@link http://php.justinvincent.com Justin Vincent ([email protected])}
6
 *
7
 * @package WordPress
8
 * @subpackage Database
9
 * @since 0.71
10
 */
11
12
/**
13
 * @since 0.71
14
 */
15
define( 'EZSQL_VERSION', 'WP1.25' );
16
17
/**
18
 * @since 0.71
19
 */
20
define( 'OBJECT', 'OBJECT' );
21
define( 'object', 'OBJECT' ); // Back compat.
22
23
/**
24
 * @since 2.5.0
25
 */
26
define( 'OBJECT_K', 'OBJECT_K' );
27
28
/**
29
 * @since 0.71
30
 */
31
define( 'ARRAY_A', 'ARRAY_A' );
32
33
/**
34
 * @since 0.71
35
 */
36
define( '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
 */
52
class 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
83
	/**
84
	 * Amount of queries made
85
	 *
86
	 * @since 1.2.0
87
	 * @access public
88
	 * @var int
89
	 */
90
	public $num_queries = 0;
91
92
	/**
93
	 * Count of rows returned by previous query
94
	 *
95
	 * @since 0.71
96
	 * @access public
97
	 * @var int
98
	 */
99
	public $num_rows = 0;
100
101
	/**
102
	 * Count of affected rows by previous query
103
	 *
104
	 * @since 0.71
105
	 * @access private
106
	 * @var int
107
	 */
108
	var $rows_affected = 0;
109
110
	/**
111
	 * The ID generated for an AUTO_INCREMENT column by the previous query (usually INSERT).
112
	 *
113
	 * @since 0.71
114
	 * @access public
115
	 * @var int
116
	 */
117
	public $insert_id = 0;
118
119
	/**
120
	 * Last query made
121
	 *
122
	 * @since 0.71
123
	 * @access private
124
	 * @var array
125
	 */
126
	var $last_query;
127
128
	/**
129
	 * Results of the last query made
130
	 *
131
	 * @since 0.71
132
	 * @access private
133
	 * @var array|null
134
	 */
135
	var $last_result;
136
137
	/**
138
	 * MySQL result, which is either a resource or boolean.
139
	 *
140
	 * @since 0.71
141
	 * @access protected
142
	 * @var mixed
143
	 */
144
	protected $result;
145
146
	/**
147
	 * Cached column info, for sanity checking data before inserting
148
	 *
149
	 * @since 4.2.0
150
	 * @access protected
151
	 * @var array
152
	 */
153
	protected $col_meta = array();
154
155
	/**
156
	 * Calculated character sets on tables
157
	 *
158
	 * @since 4.2.0
159
	 * @access protected
160
	 * @var array
161
	 */
162
	protected $table_charset = array();
163
164
	/**
165
	 * Whether text fields in the current query need to be sanity checked.
166
	 *
167
	 * @since 4.2.0
168
	 * @access protected
169
	 * @var bool
170
	 */
171
	protected $check_current_query = true;
172
173
	/**
174
	 * Flag to ensure we don't run into recursion problems when checking the collation.
175
	 *
176
	 * @since 4.2.0
177
	 * @access private
178
	 * @see wpdb::check_safe_collation()
179
	 * @var bool
180
	 */
181
	private $checking_collation = false;
182
183
	/**
184
	 * Saved info on the table column
185
	 *
186
	 * @since 0.71
187
	 * @access protected
188
	 * @var array
189
	 */
190
	protected $col_info;
191
192
	/**
193
	 * Saved queries that were executed
194
	 *
195
	 * @since 1.5.0
196
	 * @access private
197
	 * @var array
198
	 */
199
	var $queries;
200
201
	/**
202
	 * The number of times to retry reconnecting before dying.
203
	 *
204
	 * @since 3.9.0
205
	 * @access protected
206
	 * @see wpdb::check_connection()
207
	 * @var int
208
	 */
209
	protected $reconnect_retries = 5;
210
211
	/**
212
	 * WordPress table prefix
213
	 *
214
	 * You can set this to have multiple WordPress installations
215
	 * in a single database. The second reason is for possible
216
	 * security precautions.
217
	 *
218
	 * @since 2.5.0
219
	 * @access public
220
	 * @var string
221
	 */
222
	public $prefix = '';
223
224
	/**
225
	 * WordPress base table prefix.
226
	 *
227
	 * @since 3.0.0
228
	 * @access public
229
	 * @var string
230
	 */
231
	 public $base_prefix;
232
233
	/**
234
	 * Whether the database queries are ready to start executing.
235
	 *
236
	 * @since 2.3.2
237
	 * @access private
238
	 * @var bool
239
	 */
240
	var $ready = false;
241
242
	/**
243
	 * Blog ID.
244
	 *
245
	 * @since 3.0.0
246
	 * @access public
247
	 * @var int
248
	 */
249
	public $blogid = 0;
250
251
	/**
252
	 * Site ID.
253
	 *
254
	 * @since 3.0.0
255
	 * @access public
256
	 * @var int
257
	 */
258
	public $siteid = 0;
259
260
	/**
261
	 * List of WordPress per-blog tables
262
	 *
263
	 * @since 2.5.0
264
	 * @access private
265
	 * @see wpdb::tables()
266
	 * @var array
267
	 */
268
	var $tables = array( 'posts', 'comments', 'links', 'options', 'postmeta',
269
		'terms', 'term_taxonomy', 'term_relationships', 'termmeta', 'commentmeta' );
270
271
	/**
272
	 * List of deprecated WordPress tables
273
	 *
274
	 * categories, post2cat, and link2cat were deprecated in 2.3.0, db version 5539
275
	 *
276
	 * @since 2.9.0
277
	 * @access private
278
	 * @see wpdb::tables()
279
	 * @var array
280
	 */
281
	var $old_tables = array( 'categories', 'post2cat', 'link2cat' );
282
283
	/**
284
	 * List of WordPress global tables
285
	 *
286
	 * @since 3.0.0
287
	 * @access private
288
	 * @see wpdb::tables()
289
	 * @var array
290
	 */
291
	var $global_tables = array( 'users', 'usermeta' );
292
293
	/**
294
	 * List of Multisite global tables
295
	 *
296
	 * @since 3.0.0
297
	 * @access private
298
	 * @see wpdb::tables()
299
	 * @var array
300
	 */
301
	var $ms_global_tables = array( 'blogs', 'signups', 'site', 'sitemeta',
302
		'sitecategories', 'registration_log', 'blog_versions' );
303
304
	/**
305
	 * WordPress Comments table
306
	 *
307
	 * @since 1.5.0
308
	 * @access public
309
	 * @var string
310
	 */
311
	public $comments;
312
313
	/**
314
	 * WordPress Comment Metadata table
315
	 *
316
	 * @since 2.9.0
317
	 * @access public
318
	 * @var string
319
	 */
320
	public $commentmeta;
321
322
	/**
323
	 * WordPress Links table
324
	 *
325
	 * @since 1.5.0
326
	 * @access public
327
	 * @var string
328
	 */
329
	public $links;
330
331
	/**
332
	 * WordPress Options table
333
	 *
334
	 * @since 1.5.0
335
	 * @access public
336
	 * @var string
337
	 */
338
	public $options;
339
340
	/**
341
	 * WordPress Post Metadata table
342
	 *
343
	 * @since 1.5.0
344
	 * @access public
345
	 * @var string
346
	 */
347
	public $postmeta;
348
349
	/**
350
	 * WordPress Posts table
351
	 *
352
	 * @since 1.5.0
353
	 * @access public
354
	 * @var string
355
	 */
356
	public $posts;
357
358
	/**
359
	 * WordPress Terms table
360
	 *
361
	 * @since 2.3.0
362
	 * @access public
363
	 * @var string
364
	 */
365
	public $terms;
366
367
	/**
368
	 * WordPress Term Relationships table
369
	 *
370
	 * @since 2.3.0
371
	 * @access public
372
	 * @var string
373
	 */
374
	public $term_relationships;
375
376
	/**
377
	 * WordPress Term Taxonomy table
378
	 *
379
	 * @since 2.3.0
380
	 * @access public
381
	 * @var string
382
	 */
383
	public $term_taxonomy;
384
385
	/**
386
	 * WordPress Term Meta table.
387
	 *
388
	 * @since 4.4.0
389
	 * @access public
390
	 * @var string
391
	 */
392
	public $termmeta;
393
394
	//
395
	// Global and Multisite tables
396
	//
397
398
	/**
399
	 * WordPress User Metadata table
400
	 *
401
	 * @since 2.3.0
402
	 * @access public
403
	 * @var string
404
	 */
405
	public $usermeta;
406
407
	/**
408
	 * WordPress Users table
409
	 *
410
	 * @since 1.5.0
411
	 * @access public
412
	 * @var string
413
	 */
414
	public $users;
415
416
	/**
417
	 * Multisite Blogs table
418
	 *
419
	 * @since 3.0.0
420
	 * @access public
421
	 * @var string
422
	 */
423
	public $blogs;
424
425
	/**
426
	 * Multisite Blog Versions table
427
	 *
428
	 * @since 3.0.0
429
	 * @access public
430
	 * @var string
431
	 */
432
	public $blog_versions;
433
434
	/**
435
	 * Multisite Registration Log table
436
	 *
437
	 * @since 3.0.0
438
	 * @access public
439
	 * @var string
440
	 */
441
	public $registration_log;
442
443
	/**
444
	 * Multisite Signups table
445
	 *
446
	 * @since 3.0.0
447
	 * @access public
448
	 * @var string
449
	 */
450
	public $signups;
451
452
	/**
453
	 * Multisite Sites table
454
	 *
455
	 * @since 3.0.0
456
	 * @access public
457
	 * @var string
458
	 */
459
	public $site;
460
461
	/**
462
	 * Multisite Sitewide Terms table
463
	 *
464
	 * @since 3.0.0
465
	 * @access public
466
	 * @var string
467
	 */
468
	public $sitecategories;
469
470
	/**
471
	 * Multisite Site Metadata table
472
	 *
473
	 * @since 3.0.0
474
	 * @access public
475
	 * @var string
476
	 */
477
	public $sitemeta;
478
479
	/**
480
	 * Format specifiers for DB columns. Columns not listed here default to %s. Initialized during WP load.
481
	 *
482
	 * Keys are column names, values are format types: 'ID' => '%d'
483
	 *
484
	 * @since 2.8.0
485
	 * @see wpdb::prepare()
486
	 * @see wpdb::insert()
487
	 * @see wpdb::update()
488
	 * @see wpdb::delete()
489
	 * @see wp_set_wpdb_vars()
490
	 * @access public
491
	 * @var array
492
	 */
493
	public $field_types = array();
494
495
	/**
496
	 * Database table columns charset
497
	 *
498
	 * @since 2.2.0
499
	 * @access public
500
	 * @var string
501
	 */
502
	public $charset;
503
504
	/**
505
	 * Database table columns collate
506
	 *
507
	 * @since 2.2.0
508
	 * @access public
509
	 * @var string
510
	 */
511
	public $collate;
512
513
	/**
514
	 * Database Username
515
	 *
516
	 * @since 2.9.0
517
	 * @access protected
518
	 * @var string
519
	 */
520
	protected $dbuser;
521
522
	/**
523
	 * Database Password
524
	 *
525
	 * @since 3.1.0
526
	 * @access protected
527
	 * @var string
528
	 */
529
	protected $dbpassword;
530
531
	/**
532
	 * Database Name
533
	 *
534
	 * @since 3.1.0
535
	 * @access protected
536
	 * @var string
537
	 */
538
	protected $dbname;
539
540
	/**
541
	 * Database Host
542
	 *
543
	 * @since 3.1.0
544
	 * @access protected
545
	 * @var string
546
	 */
547
	protected $dbhost;
548
549
	/**
550
	 * Database Handle
551
	 *
552
	 * @since 0.71
553
	 * @access protected
554
	 * @var string
555
	 */
556
	protected $dbh;
557
558
	/**
559
	 * A textual description of the last query/get_row/get_var call
560
	 *
561
	 * @since 3.0.0
562
	 * @access public
563
	 * @var string
564
	 */
565
	public $func_call;
566
567
	/**
568
	 * Whether MySQL is used as the database engine.
569
	 *
570
	 * Set in WPDB::db_connect() to true, by default. This is used when checking
571
	 * against the required MySQL version for WordPress. Normally, a replacement
572
	 * database drop-in (db.php) will skip these checks, but setting this to true
573
	 * will force the checks to occur.
574
	 *
575
	 * @since 3.3.0
576
	 * @access public
577
	 * @var bool
578
	 */
579
	public $is_mysql = null;
580
581
	/**
582
	 * A list of incompatible SQL modes.
583
	 *
584
	 * @since 3.9.0
585
	 * @access protected
586
	 * @var array
587
	 */
588
	protected $incompatible_modes = array( 'NO_ZERO_DATE', 'ONLY_FULL_GROUP_BY',
589
		'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'TRADITIONAL' );
590
591
	/**
592
	 * Whether to use mysqli over mysql.
593
	 *
594
	 * @since 3.9.0
595
	 * @access private
596
	 * @var bool
597
	 */
598
	private $use_mysqli = false;
599
600
	/**
601
	 * Whether we've managed to successfully connect at some point
602
	 *
603
	 * @since 3.9.0
604
	 * @access private
605
	 * @var bool
606
	 */
607
	private $has_connected = false;
608
609
	/**
610
	 * Connects to the database server and selects a database
611
	 *
612
	 * PHP5 style constructor for compatibility with PHP5. Does
613
	 * the actual setting up of the class properties and connection
614
	 * to the database.
615
	 *
616
	 * @link https://core.trac.wordpress.org/ticket/3354
617
	 * @since 2.0.8
618
	 *
619
	 * @global string $wp_version
620
	 *
621
	 * @param string $dbuser     MySQL database user
622
	 * @param string $dbpassword MySQL database password
623
	 * @param string $dbname     MySQL database name
624
	 * @param string $dbhost     MySQL database host
625
	 */
626
	public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
627
		register_shutdown_function( array( $this, '__destruct' ) );
628
629
		if ( WP_DEBUG && WP_DEBUG_DISPLAY )
630
			$this->show_errors();
631
632
		/* Use ext/mysqli if it exists and:
633
		 *  - WP_USE_EXT_MYSQL is defined as false, or
634
		 *  - We are a development version of WordPress, or
635
		 *  - We are running PHP 5.5 or greater, or
636
		 *  - ext/mysql is not loaded.
637
		 */
638
		if ( function_exists( 'mysqli_connect' ) ) {
639
			if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
640
				$this->use_mysqli = ! WP_USE_EXT_MYSQL;
641
			} elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) {
642
				$this->use_mysqli = true;
643
			} elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
644
				$this->use_mysqli = true;
645
			}
646
		}
647
648
		$this->dbuser = $dbuser;
649
		$this->dbpassword = $dbpassword;
650
		$this->dbname = $dbname;
651
		$this->dbhost = $dbhost;
652
653
		// wp-config.php creation will manually connect when ready.
654
		if ( defined( 'WP_SETUP_CONFIG' ) ) {
655
			return;
656
		}
657
658
		$this->db_connect();
659
	}
660
661
	/**
662
	 * PHP5 style destructor and will run when database object is destroyed.
663
	 *
664
	 * @see wpdb::__construct()
665
	 * @since 2.0.8
666
	 * @return true
667
	 */
668
	public function __destruct() {
669
		return true;
670
	}
671
672
	/**
673
	 * Makes private properties readable for backward compatibility.
674
	 *
675
	 * @since 3.5.0
676
	 *
677
	 * @param string $name The private member to get, and optionally process
678
	 * @return mixed The private member
679
	 */
680
	public function __get( $name ) {
681
		if ( 'col_info' === $name )
682
			$this->load_col_info();
683
684
		return $this->$name;
685
	}
686
687
	/**
688
	 * Makes private properties settable for backward compatibility.
689
	 *
690
	 * @since 3.5.0
691
	 *
692
	 * @param string $name  The private member to set
693
	 * @param mixed  $value The value to set
694
	 */
695
	public function __set( $name, $value ) {
696
		$protected_members = array(
697
			'col_meta',
698
			'table_charset',
699
			'check_current_query',
700
		);
701
		if (  in_array( $name, $protected_members, true ) ) {
702
			return;
703
		}
704
		$this->$name = $value;
705
	}
706
707
	/**
708
	 * Makes private properties check-able for backward compatibility.
709
	 *
710
	 * @since 3.5.0
711
	 *
712
	 * @param string $name  The private member to check
713
	 *
714
	 * @return bool If the member is set or not
715
	 */
716
	public function __isset( $name ) {
717
		return isset( $this->$name );
718
	}
719
720
	/**
721
	 * Makes private properties un-settable for backward compatibility.
722
	 *
723
	 * @since 3.5.0
724
	 *
725
	 * @param string $name  The private member to unset
726
	 */
727
	public function __unset( $name ) {
728
		unset( $this->$name );
729
	}
730
731
	/**
732
	 * Set $this->charset and $this->collate
733
	 *
734
	 * @since 3.1.0
735
	 */
736
	public function init_charset() {
737
		if ( function_exists('is_multisite') && is_multisite() ) {
738
			$charset = 'utf8';
739
			if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) {
740
				$collate = DB_COLLATE;
741
			} else {
742
				$collate = 'utf8_general_ci';
743
			}
744
		} elseif ( defined( 'DB_COLLATE' ) ) {
745
			$collate = DB_COLLATE;
746
		}
747
748
		if ( defined( 'DB_CHARSET' ) ) {
749
			$charset = DB_CHARSET;
750
		}
751
752
		$charset_collate = $this->determine_charset( $charset, $collate );
0 ignored issues
show
The variable $charset does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
The variable $collate does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
753
754
		$this->charset = $charset_collate['charset'];
755
		$this->collate = $charset_collate['collate'];
756
	}
757
758
	/**
759
	 * Determines the best charset and collation to use given a charset and collation.
760
	 *
761
	 * For example, when able, utf8mb4 should be used instead of utf8.
762
	 *
763
	 * @since 4.6.0
764
	 * @access public
765
	 *
766
	 * @param string $charset The character set to check.
767
	 * @param string $collate The collation to check.
768
	 * @return array The most appropriate character set and collation to use.
769
	 */
770
	public function determine_charset( $charset, $collate ) {
771
		if ( ( $this->use_mysqli && ! ( $this->dbh instanceof mysqli ) ) || empty( $this->dbh ) ) {
772
			return compact( 'charset', 'collate' );
773
		}
774
775
		if ( 'utf8' === $charset && $this->has_cap( 'utf8mb4' ) ) {
776
			$charset = 'utf8mb4';
777
		}
778
779
		if ( 'utf8mb4' === $charset ) {
780
			// _general_ is outdated, so we can upgrade it to _unicode_, instead.
781
			if ( ! $collate || 'utf8_general_ci' === $collate ) {
782
				$collate = 'utf8mb4_unicode_ci';
783
			} else {
784
				$collate = str_replace( 'utf8_', 'utf8mb4_', $collate );
785
			}
786
		}
787
788
		// _unicode_520_ is a better collation, we should use that when it's available.
789
		if ( $this->has_cap( 'utf8mb4_520' ) && 'utf8mb4_unicode_ci' === $collate ) {
790
			$collate = 'utf8mb4_unicode_520_ci';
791
		}
792
793
		return compact( 'charset', 'collate' );
794
	}
795
796
	/**
797
	 * Sets the connection's character set.
798
	 *
799
	 * @since 3.1.0
800
	 *
801
	 * @param resource $dbh     The resource given by mysql_connect
802
	 * @param string   $charset Optional. The character set. Default null.
803
	 * @param string   $collate Optional. The collation. Default null.
804
	 */
805
	public function set_charset( $dbh, $charset = null, $collate = null ) {
806
		if ( ! isset( $charset ) )
807
			$charset = $this->charset;
808
		if ( ! isset( $collate ) )
809
			$collate = $this->collate;
810
		if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) {
811
			if ( $this->use_mysqli ) {
812
				if ( function_exists( 'mysqli_set_charset' ) && $this->has_cap( 'set_charset' ) ) {
813
					mysqli_set_charset( $dbh, $charset );
814
				}
815
				$query = $this->prepare( 'SET NAMES %s', $charset );
816
				if ( ! empty( $collate ) )
817
					$query .= $this->prepare( ' COLLATE %s', $collate );
818
				mysqli_query( $dbh, $query );
819
			} else {
820
				if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset' ) ) {
821
					mysql_set_charset( $charset, $dbh );
822
				}
823
				$query = $this->prepare( 'SET NAMES %s', $charset );
824
				if ( ! empty( $collate ) )
825
					$query .= $this->prepare( ' COLLATE %s', $collate );
826
				mysql_query( $query, $dbh );
827
			}
828
		}
829
	}
830
831
	/**
832
	 * Change the current SQL mode, and ensure its WordPress compatibility.
833
	 *
834
	 * If no modes are passed, it will ensure the current MySQL server
835
	 * modes are compatible.
836
	 *
837
	 * @since 3.9.0
838
	 *
839
	 * @param array $modes Optional. A list of SQL modes to set.
840
	 */
841
	public function set_sql_mode( $modes = array() ) {
842
		if ( empty( $modes ) ) {
843 View Code Duplication
			if ( $this->use_mysqli ) {
844
				$res = mysqli_query( $this->dbh, 'SELECT @@SESSION.sql_mode' );
845
			} else {
846
				$res = mysql_query( 'SELECT @@SESSION.sql_mode', $this->dbh );
847
			}
848
849
			if ( empty( $res ) ) {
850
				return;
851
			}
852
853
			if ( $this->use_mysqli ) {
854
				$modes_array = mysqli_fetch_array( $res );
855
				if ( empty( $modes_array[0] ) ) {
856
					return;
857
				}
858
				$modes_str = $modes_array[0];
859
			} else {
860
				$modes_str = mysql_result( $res, 0 );
861
			}
862
863
			if ( empty( $modes_str ) ) {
864
				return;
865
			}
866
867
			$modes = explode( ',', $modes_str );
868
		}
869
870
		$modes = array_change_key_case( $modes, CASE_UPPER );
871
872
		/**
873
		 * Filters the list of incompatible SQL modes to exclude.
874
		 *
875
		 * @since 3.9.0
876
		 *
877
		 * @param array $incompatible_modes An array of incompatible modes.
878
		 */
879
		$incompatible_modes = (array) apply_filters( 'incompatible_sql_modes', $this->incompatible_modes );
880
881
		foreach ( $modes as $i => $mode ) {
882
			if ( in_array( $mode, $incompatible_modes ) ) {
883
				unset( $modes[ $i ] );
884
			}
885
		}
886
887
		$modes_str = implode( ',', $modes );
888
889 View Code Duplication
		if ( $this->use_mysqli ) {
890
			mysqli_query( $this->dbh, "SET SESSION sql_mode='$modes_str'" );
891
		} else {
892
			mysql_query( "SET SESSION sql_mode='$modes_str'", $this->dbh );
893
		}
894
	}
895
896
	/**
897
	 * Sets the table prefix for the WordPress tables.
898
	 *
899
	 * @since 2.5.0
900
	 *
901
	 * @param string $prefix          Alphanumeric name for the new prefix.
902
	 * @param bool   $set_table_names Optional. Whether the table names, e.g. wpdb::$posts, should be updated or not.
903
	 * @return string|WP_Error Old prefix or WP_Error on error
904
	 */
905
	public function set_prefix( $prefix, $set_table_names = true ) {
906
907
		if ( preg_match( '|[^a-z0-9_]|i', $prefix ) )
908
			return new WP_Error('invalid_db_prefix', 'Invalid database prefix' );
909
910
		$old_prefix = is_multisite() ? '' : $prefix;
911
912
		if ( isset( $this->base_prefix ) )
913
			$old_prefix = $this->base_prefix;
914
915
		$this->base_prefix = $prefix;
916
917
		if ( $set_table_names ) {
918
			foreach ( $this->tables( 'global' ) as $table => $prefixed_table )
919
				$this->$table = $prefixed_table;
920
921
			if ( is_multisite() && empty( $this->blogid ) )
922
				return $old_prefix;
923
924
			$this->prefix = $this->get_blog_prefix();
925
926
			foreach ( $this->tables( 'blog' ) as $table => $prefixed_table )
927
				$this->$table = $prefixed_table;
928
929
			foreach ( $this->tables( 'old' ) as $table => $prefixed_table )
930
				$this->$table = $prefixed_table;
931
		}
932
		return $old_prefix;
933
	}
934
935
	/**
936
	 * Sets blog id.
937
	 *
938
	 * @since 3.0.0
939
	 * @access public
940
	 *
941
	 * @param int $blog_id
942
	 * @param int $site_id Optional.
943
	 * @return int previous blog id
944
	 */
945
	public function set_blog_id( $blog_id, $site_id = 0 ) {
946
		if ( ! empty( $site_id ) )
947
			$this->siteid = $site_id;
948
949
		$old_blog_id  = $this->blogid;
950
		$this->blogid = $blog_id;
951
952
		$this->prefix = $this->get_blog_prefix();
953
954
		foreach ( $this->tables( 'blog' ) as $table => $prefixed_table )
955
			$this->$table = $prefixed_table;
956
957
		foreach ( $this->tables( 'old' ) as $table => $prefixed_table )
958
			$this->$table = $prefixed_table;
959
960
		return $old_blog_id;
961
	}
962
963
	/**
964
	 * Gets blog prefix.
965
	 *
966
	 * @since 3.0.0
967
	 * @param int $blog_id Optional.
968
	 * @return string Blog prefix.
969
	 */
970
	public function get_blog_prefix( $blog_id = null ) {
971
		if ( is_multisite() ) {
972
			if ( null === $blog_id )
973
				$blog_id = $this->blogid;
974
			$blog_id = (int) $blog_id;
975
			if ( defined( 'MULTISITE' ) && ( 0 == $blog_id || 1 == $blog_id ) )
976
				return $this->base_prefix;
977
			else
978
				return $this->base_prefix . $blog_id . '_';
979
		} else {
980
			return $this->base_prefix;
981
		}
982
	}
983
984
	/**
985
	 * Returns an array of WordPress tables.
986
	 *
987
	 * Also allows for the CUSTOM_USER_TABLE and CUSTOM_USER_META_TABLE to
988
	 * override the WordPress users and usermeta tables that would otherwise
989
	 * be determined by the prefix.
990
	 *
991
	 * The scope argument can take one of the following:
992
	 *
993
	 * 'all' - returns 'all' and 'global' tables. No old tables are returned.
994
	 * 'blog' - returns the blog-level tables for the queried blog.
995
	 * 'global' - returns the global tables for the installation, returning multisite tables only if running multisite.
996
	 * 'ms_global' - returns the multisite global tables, regardless if current installation is multisite.
997
	 * 'old' - returns tables which are deprecated.
998
	 *
999
	 * @since 3.0.0
1000
	 * @uses wpdb::$tables
1001
	 * @uses wpdb::$old_tables
1002
	 * @uses wpdb::$global_tables
1003
	 * @uses wpdb::$ms_global_tables
1004
	 *
1005
	 * @param string $scope   Optional. Can be all, global, ms_global, blog, or old tables. Defaults to all.
1006
	 * @param bool   $prefix  Optional. Whether to include table prefixes. Default true. If blog
1007
	 *                        prefix is requested, then the custom users and usermeta tables will be mapped.
1008
	 * @param int    $blog_id Optional. The blog_id to prefix. Defaults to wpdb::$blogid. Used only when prefix is requested.
1009
	 * @return array Table names. When a prefix is requested, the key is the unprefixed table name.
1010
	 */
1011
	public function tables( $scope = 'all', $prefix = true, $blog_id = 0 ) {
1012
		switch ( $scope ) {
1013 View Code Duplication
			case 'all' :
1014
				$tables = array_merge( $this->global_tables, $this->tables );
1015
				if ( is_multisite() )
1016
					$tables = array_merge( $tables, $this->ms_global_tables );
1017
				break;
1018
			case 'blog' :
1019
				$tables = $this->tables;
1020
				break;
1021 View Code Duplication
			case 'global' :
1022
				$tables = $this->global_tables;
1023
				if ( is_multisite() )
1024
					$tables = array_merge( $tables, $this->ms_global_tables );
1025
				break;
1026
			case 'ms_global' :
1027
				$tables = $this->ms_global_tables;
1028
				break;
1029
			case 'old' :
1030
				$tables = $this->old_tables;
1031
				break;
1032
			default :
1033
				return array();
1034
		}
1035
1036
		if ( $prefix ) {
1037
			if ( ! $blog_id )
1038
				$blog_id = $this->blogid;
1039
			$blog_prefix = $this->get_blog_prefix( $blog_id );
1040
			$base_prefix = $this->base_prefix;
1041
			$global_tables = array_merge( $this->global_tables, $this->ms_global_tables );
1042
			foreach ( $tables as $k => $table ) {
1043
				if ( in_array( $table, $global_tables ) )
1044
					$tables[ $table ] = $base_prefix . $table;
1045
				else
1046
					$tables[ $table ] = $blog_prefix . $table;
1047
				unset( $tables[ $k ] );
1048
			}
1049
1050
			if ( isset( $tables['users'] ) && defined( 'CUSTOM_USER_TABLE' ) )
1051
				$tables['users'] = CUSTOM_USER_TABLE;
1052
1053
			if ( isset( $tables['usermeta'] ) && defined( 'CUSTOM_USER_META_TABLE' ) )
1054
				$tables['usermeta'] = CUSTOM_USER_META_TABLE;
1055
		}
1056
1057
		return $tables;
1058
	}
1059
1060
	/**
1061
	 * Selects a database using the current database connection.
1062
	 *
1063
	 * The database name will be changed based on the current database
1064
	 * connection. On failure, the execution will bail and display an DB error.
1065
	 *
1066
	 * @since 0.71
1067
	 *
1068
	 * @param string        $db  MySQL database name
1069
	 * @param resource|null $dbh Optional link identifier.
1070
	 */
1071
	public function select( $db, $dbh = null ) {
1072
		if ( is_null($dbh) )
1073
			$dbh = $this->dbh;
1074
1075
		if ( $this->use_mysqli ) {
1076
			$success = mysqli_select_db( $dbh, $db );
1077
		} else {
1078
			$success = mysql_select_db( $db, $dbh );
1079
		}
1080
		if ( ! $success ) {
1081
			$this->ready = false;
1082
			if ( ! did_action( 'template_redirect' ) ) {
1083
				wp_load_translations_early();
1084
1085
				$message = '<h1>' . __( 'Can&#8217;t select database' ) . "</h1>\n";
1086
1087
				$message .= '<p>' . sprintf(
1088
					/* translators: %s: database name */
1089
					__( '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.' ),
1090
					'<code>' . htmlspecialchars( $db, ENT_QUOTES ) . '</code>'
1091
				) . "</p>\n";
1092
1093
				$message .= "<ul>\n";
1094
				$message .= '<li>' . __( 'Are you sure it exists?' ) . "</li>\n";
1095
1096
				$message .= '<li>' . sprintf(
1097
					/* translators: 1: database user, 2: database name */
1098
					__( 'Does the user %1$s have permission to use the %2$s database?' ),
1099
					'<code>' . htmlspecialchars( $this->dbuser, ENT_QUOTES )  . '</code>',
1100
					'<code>' . htmlspecialchars( $db, ENT_QUOTES ) . '</code>'
1101
				) . "</li>\n";
1102
1103
				$message .= '<li>' . sprintf(
1104
					/* translators: %s: database name */
1105
					__( '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?' ),
1106
					htmlspecialchars( $db, ENT_QUOTES )
1107
				). "</li>\n";
1108
1109
				$message .= "</ul>\n";
1110
1111
				$message .= '<p>' . sprintf(
1112
					/* translators: %s: support forums URL */
1113
					__( '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>.' ),
1114
					__( 'https://wordpress.org/support/' )
1115
				) . "</p>\n";
1116
1117
				$this->bail( $message, 'db_select_fail' );
1118
			}
1119
		}
1120
	}
1121
1122
	/**
1123
	 * Do not use, deprecated.
1124
	 *
1125
	 * Use esc_sql() or wpdb::prepare() instead.
1126
	 *
1127
	 * @since 2.8.0
1128
	 * @deprecated 3.6.0 Use wpdb::prepare()
1129
	 * @see wpdb::prepare
1130
	 * @see esc_sql()
1131
	 * @access private
1132
	 *
1133
	 * @param string $string
1134
	 * @return string
1135
	 */
1136
	function _weak_escape( $string ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for _weak_escape.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
1137
		if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) )
1138
			_deprecated_function( __METHOD__, '3.6.0', 'wpdb::prepare() or esc_sql()' );
1139
		return addslashes( $string );
1140
	}
1141
1142
	/**
1143
	 * Real escape, using mysqli_real_escape_string() or mysql_real_escape_string()
1144
	 *
1145
	 * @see mysqli_real_escape_string()
1146
	 * @see mysql_real_escape_string()
1147
	 * @since 2.8.0
1148
	 * @access private
1149
	 *
1150
	 * @param  string $string to escape
1151
	 * @return string escaped
1152
	 */
1153
	function _real_escape( $string ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for _real_escape.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
1154
		if ( $this->dbh ) {
1155
			if ( $this->use_mysqli ) {
1156
				return mysqli_real_escape_string( $this->dbh, $string );
1157
			} else {
1158
				return mysql_real_escape_string( $string, $this->dbh );
1159
			}
1160
		}
1161
1162
		$class = get_class( $this );
1163
		if ( function_exists( '__' ) ) {
1164
			/* translators: %s: database access abstraction class, usually wpdb or a class extending wpdb */
1165
			_doing_it_wrong( $class, sprintf( __( '%s must set a database connection for use with escaping.' ), $class ), '3.6.0' );
1166
		} else {
1167
			_doing_it_wrong( $class, sprintf( '%s must set a database connection for use with escaping.', $class ), '3.6.0' );
1168
		}
1169
		return addslashes( $string );
1170
	}
1171
1172
	/**
1173
	 * Escape data. Works on arrays.
1174
	 *
1175
	 * @uses wpdb::_real_escape()
1176
	 * @since  2.8.0
1177
	 * @access public
1178
	 *
1179
	 * @param  string|array $data
1180
	 * @return string|array escaped
1181
	 */
1182
	public function _escape( $data ) {
1183
		if ( is_array( $data ) ) {
1184
			foreach ( $data as $k => $v ) {
1185
				if ( is_array( $v ) ) {
1186
					$data[$k] = $this->_escape( $v );
1187
				} else {
1188
					$data[$k] = $this->_real_escape( $v );
1189
				}
1190
			}
1191
		} else {
1192
			$data = $this->_real_escape( $data );
1193
		}
1194
1195
		return $data;
1196
	}
1197
1198
	/**
1199
	 * Do not use, deprecated.
1200
	 *
1201
	 * Use esc_sql() or wpdb::prepare() instead.
1202
	 *
1203
	 * @since 0.71
1204
	 * @deprecated 3.6.0 Use wpdb::prepare()
1205
	 * @see wpdb::prepare()
1206
	 * @see esc_sql()
1207
	 *
1208
	 * @param mixed $data
1209
	 * @return mixed
1210
	 */
1211
	public function escape( $data ) {
1212
		if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) )
1213
			_deprecated_function( __METHOD__, '3.6.0', 'wpdb::prepare() or esc_sql()' );
1214
		if ( is_array( $data ) ) {
1215
			foreach ( $data as $k => $v ) {
1216
				if ( is_array( $v ) )
1217
					$data[$k] = $this->escape( $v, 'recursive' );
1218
				else
1219
					$data[$k] = $this->_weak_escape( $v, 'internal' );
1220
			}
1221
		} else {
1222
			$data = $this->_weak_escape( $data, 'internal' );
1223
		}
1224
1225
		return $data;
1226
	}
1227
1228
	/**
1229
	 * Escapes content by reference for insertion into the database, for security
1230
	 *
1231
	 * @uses wpdb::_real_escape()
1232
	 *
1233
	 * @since 2.3.0
1234
	 *
1235
	 * @param string $string to escape
1236
	 */
1237
	public function escape_by_ref( &$string ) {
1238
		if ( ! is_float( $string ) )
1239
			$string = $this->_real_escape( $string );
1240
	}
1241
1242
	/**
1243
	 * Prepares a SQL query for safe execution. Uses sprintf()-like syntax.
1244
	 *
1245
	 * The following directives can be used in the query format string:
1246
	 *   %d (integer)
1247
	 *   %f (float)
1248
	 *   %s (string)
1249
	 *   %% (literal percentage sign - no argument needed)
1250
	 *
1251
	 * All of %d, %f, and %s are to be left unquoted in the query string and they need an argument passed for them.
1252
	 * Literals (%) as parts of the query must be properly written as %%.
1253
	 *
1254
	 * This function only supports a small subset of the sprintf syntax; it only supports %d (integer), %f (float), and %s (string).
1255
	 * Does not support sign, padding, alignment, width or precision specifiers.
1256
	 * Does not support argument numbering/swapping.
1257
	 *
1258
	 * May be called like {@link https://secure.php.net/sprintf sprintf()} or like {@link https://secure.php.net/vsprintf vsprintf()}.
1259
	 *
1260
	 * Both %d and %s should be left unquoted in the query string.
1261
	 *
1262
	 *     $wpdb->prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d", 'foo', 1337 );
1263
	 *     $wpdb->prepare( "SELECT DATE_FORMAT(`field`, '%%c') FROM `table` WHERE `column` = %s", 'foo' );
1264
	 *
1265
	 * @link https://secure.php.net/sprintf Description of syntax.
1266
	 * @since 2.3.0
1267
	 *
1268
	 * @param string      $query    Query statement with sprintf()-like placeholders
1269
	 * @param array|mixed $args     The array of variables to substitute into the query's placeholders if being called like
1270
	 *                              {@link https://secure.php.net/vsprintf vsprintf()}, or the first variable to substitute into the query's placeholders if
1271
	 *                              being called like {@link https://secure.php.net/sprintf sprintf()}.
1272
	 * @param mixed       $args,... further variables to substitute into the query's placeholders if being called like
0 ignored issues
show
There is no parameter named $args,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1273
	 *                              {@link https://secure.php.net/sprintf sprintf()}.
1274
	 * @return string|void Sanitized query string, if there is a query to prepare.
1275
	 */
1276
	public function prepare( $query, $args ) {
1277
		if ( is_null( $query ) )
1278
			return;
1279
1280
		// This is not meant to be foolproof -- but it will catch obviously incorrect usage.
1281
		if ( strpos( $query, '%' ) === false ) {
1282
			_doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9.0' );
1283
		}
1284
1285
		$args = func_get_args();
1286
		array_shift( $args );
1287
		// If args were passed as an array (as in vsprintf), move them up
1288 View Code Duplication
		if ( isset( $args[0] ) && is_array($args[0]) )
1289
			$args = $args[0];
1290
		$query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it
1291
		$query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting
1292
		$query = preg_replace( '|(?<!%)%f|' , '%F', $query ); // Force floats to be locale unaware
1293
		$query = preg_replace( '|(?<!%)%s|', "'%s'", $query ); // quote the strings, avoiding escaped strings like %%s
1294
		array_walk( $args, array( $this, 'escape_by_ref' ) );
1295
		return @vsprintf( $query, $args );
1296
	}
1297
1298
	/**
1299
	 * First half of escaping for LIKE special characters % and _ before preparing for MySQL.
1300
	 *
1301
	 * Use this only before wpdb::prepare() or esc_sql().  Reversing the order is very bad for security.
1302
	 *
1303
	 * Example Prepared Statement:
1304
	 *
1305
	 *     $wild = '%';
1306
	 *     $find = 'only 43% of planets';
1307
	 *     $like = $wild . $wpdb->esc_like( $find ) . $wild;
1308
	 *     $sql  = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE '%s'", $like );
1309
	 *
1310
	 * Example Escape Chain:
1311
	 *
1312
	 *     $sql  = esc_sql( $wpdb->esc_like( $input ) );
1313
	 *
1314
	 * @since 4.0.0
1315
	 * @access public
1316
	 *
1317
	 * @param string $text The raw text to be escaped. The input typed by the user should have no
1318
	 *                     extra or deleted slashes.
1319
	 * @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare()
1320
	 *                or real_escape next.
1321
	 */
1322
	public function esc_like( $text ) {
1323
		return addcslashes( $text, '_%\\' );
1324
	}
1325
1326
	/**
1327
	 * Print SQL/DB error.
1328
	 *
1329
	 * @since 0.71
1330
	 * @global array $EZSQL_ERROR Stores error information of query and error string
1331
	 *
1332
	 * @param string $str The error to display
1333
	 * @return false|void False if the showing of errors is disabled.
1334
	 */
1335
	public function print_error( $str = '' ) {
1336
		global $EZSQL_ERROR;
1337
1338
		if ( !$str ) {
1339
			if ( $this->use_mysqli ) {
1340
				$str = mysqli_error( $this->dbh );
1341
			} else {
1342
				$str = mysql_error( $this->dbh );
1343
			}
1344
		}
1345
		$EZSQL_ERROR[] = array( 'query' => $this->last_query, 'error_str' => $str );
1346
1347
		if ( $this->suppress_errors )
1348
			return false;
1349
1350
		wp_load_translations_early();
1351
1352
		if ( $caller = $this->get_caller() )
1353
			$error_str = sprintf( __( 'WordPress database error %1$s for query %2$s made by %3$s' ), $str, $this->last_query, $caller );
1354
		else
1355
			$error_str = sprintf( __( 'WordPress database error %1$s for query %2$s' ), $str, $this->last_query );
1356
1357
		error_log( $error_str );
1358
1359
		// Are we showing errors?
1360
		if ( ! $this->show_errors )
1361
			return false;
1362
1363
		// If there is an error then take note of it
1364
		if ( is_multisite() ) {
1365
			$msg = sprintf(
1366
				"%s [%s]\n%s\n",
1367
				__( 'WordPress database error:' ),
1368
				$str,
1369
				$this->last_query
1370
			);
1371
1372
			if ( defined( 'ERRORLOGFILE' ) ) {
1373
				error_log( $msg, 3, ERRORLOGFILE );
1374
			}
1375
			if ( defined( 'DIEONDBERROR' ) ) {
1376
				wp_die( $msg );
1377
			}
1378
		} else {
1379
			$str   = htmlspecialchars( $str, ENT_QUOTES );
1380
			$query = htmlspecialchars( $this->last_query, ENT_QUOTES );
1381
1382
			printf(
1383
				'<div id="error"><p class="wpdberror"><strong>%s</strong> [%s]<br /><code>%s</code></p></div>',
1384
				__( 'WordPress database error:' ),
1385
				$str,
1386
				$query
1387
			);
1388
		}
1389
	}
1390
1391
	/**
1392
	 * Enables showing of database errors.
1393
	 *
1394
	 * This function should be used only to enable showing of errors.
1395
	 * wpdb::hide_errors() should be used instead for hiding of errors. However,
1396
	 * this function can be used to enable and disable showing of database
1397
	 * errors.
1398
	 *
1399
	 * @since 0.71
1400
	 * @see wpdb::hide_errors()
1401
	 *
1402
	 * @param bool $show Whether to show or hide errors
1403
	 * @return bool Old value for showing errors.
1404
	 */
1405
	public function show_errors( $show = true ) {
1406
		$errors = $this->show_errors;
1407
		$this->show_errors = $show;
1408
		return $errors;
1409
	}
1410
1411
	/**
1412
	 * Disables showing of database errors.
1413
	 *
1414
	 * By default database errors are not shown.
1415
	 *
1416
	 * @since 0.71
1417
	 * @see wpdb::show_errors()
1418
	 *
1419
	 * @return bool Whether showing of errors was active
1420
	 */
1421
	public function hide_errors() {
1422
		$show = $this->show_errors;
1423
		$this->show_errors = false;
1424
		return $show;
1425
	}
1426
1427
	/**
1428
	 * Whether to suppress database errors.
1429
	 *
1430
	 * By default database errors are suppressed, with a simple
1431
	 * call to this function they can be enabled.
1432
	 *
1433
	 * @since 2.5.0
1434
	 * @see wpdb::hide_errors()
1435
	 * @param bool $suppress Optional. New value. Defaults to true.
1436
	 * @return bool Old value
1437
	 */
1438
	public function suppress_errors( $suppress = true ) {
1439
		$errors = $this->suppress_errors;
1440
		$this->suppress_errors = (bool) $suppress;
1441
		return $errors;
1442
	}
1443
1444
	/**
1445
	 * Kill cached query results.
1446
	 *
1447
	 * @since 0.71
1448
	 */
1449
	public function flush() {
1450
		$this->last_result = array();
1451
		$this->col_info    = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $col_info.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1452
		$this->last_query  = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $last_query.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1453
		$this->rows_affected = $this->num_rows = 0;
1454
		$this->last_error  = '';
1455
1456
		if ( $this->use_mysqli && $this->result instanceof mysqli_result ) {
1457
			mysqli_free_result( $this->result );
1458
			$this->result = null;
1459
1460
			// Sanity check before using the handle
1461
			if ( empty( $this->dbh ) || !( $this->dbh instanceof mysqli ) ) {
1462
				return;
1463
			}
1464
1465
			// Clear out any results from a multi-query
1466
			while ( mysqli_more_results( $this->dbh ) ) {
1467
				mysqli_next_result( $this->dbh );
1468
			}
1469
		} elseif ( is_resource( $this->result ) ) {
1470
			mysql_free_result( $this->result );
1471
		}
1472
	}
1473
1474
	/**
1475
	 * Connect to and select database.
1476
	 *
1477
	 * If $allow_bail is false, the lack of database connection will need
1478
	 * to be handled manually.
1479
	 *
1480
	 * @since 3.0.0
1481
	 * @since 3.9.0 $allow_bail parameter added.
1482
	 *
1483
	 * @param bool $allow_bail Optional. Allows the function to bail. Default true.
1484
	 * @return bool True with a successful connection, false on failure.
1485
	 */
1486
	public function db_connect( $allow_bail = true ) {
1487
		$this->is_mysql = true;
1488
1489
		/*
1490
		 * Deprecated in 3.9+ when using MySQLi. No equivalent
1491
		 * $new_link parameter exists for mysqli_* functions.
1492
		 */
1493
		$new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
1494
		$client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
1495
1496
		if ( $this->use_mysqli ) {
1497
			$this->dbh = mysqli_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like mysqli_init() of type object<mysql> is incompatible with the declared type string of property $dbh.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1498
1499
			// mysqli_real_connect doesn't support the host param including a port or socket
1500
			// like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file.
1501
			$port = null;
1502
			$socket = null;
1503
			$host = $this->dbhost;
1504
			$port_or_socket = strstr( $host, ':' );
1505
			if ( ! empty( $port_or_socket ) ) {
1506
				$host = substr( $host, 0, strpos( $host, ':' ) );
1507
				$port_or_socket = substr( $port_or_socket, 1 );
1508
				if ( 0 !== strpos( $port_or_socket, '/' ) ) {
1509
					$port = intval( $port_or_socket );
1510
					$maybe_socket = strstr( $port_or_socket, ':' );
1511
					if ( ! empty( $maybe_socket ) ) {
1512
						$socket = substr( $maybe_socket, 1 );
1513
					}
1514
				} else {
1515
					$socket = $port_or_socket;
1516
				}
1517
			}
1518
1519
			if ( WP_DEBUG ) {
1520
				mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
1521
			} else {
1522
				@mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags );
1523
			}
1524
1525
			if ( $this->dbh->connect_errno ) {
1526
				$this->dbh = null;
1527
1528
				/* It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
1529
		 		 *  - We haven't previously connected, and
1530
		 		 *  - WP_USE_EXT_MYSQL isn't set to false, and
1531
		 		 *  - ext/mysql is loaded.
1532
		 		 */
1533
				$attempt_fallback = true;
1534
1535
				if ( $this->has_connected ) {
1536
					$attempt_fallback = false;
1537
				} elseif ( defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL ) {
1538
					$attempt_fallback = false;
1539
				} elseif ( ! function_exists( 'mysql_connect' ) ) {
1540
					$attempt_fallback = false;
1541
				}
1542
1543
				if ( $attempt_fallback ) {
1544
					$this->use_mysqli = false;
1545
					return $this->db_connect( $allow_bail );
1546
				}
1547
			}
1548
		} else {
1549
			if ( WP_DEBUG ) {
1550
				$this->dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
0 ignored issues
show
Documentation Bug introduced by
It seems like mysql_connect($this->dbh...ew_link, $client_flags) of type resource is incompatible with the declared type string of property $dbh.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1551
			} else {
1552
				$this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags );
1553
			}
1554
		}
1555
1556
		if ( ! $this->dbh && $allow_bail ) {
1557
			wp_load_translations_early();
1558
1559
			// Load custom DB error template, if present.
1560
			if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) {
1561
				require_once( WP_CONTENT_DIR . '/db-error.php' );
1562
				die();
1563
			}
1564
1565
			$message = '<h1>' . __( 'Error establishing a database connection' ) . "</h1>\n";
1566
1567
			$message .= '<p>' . sprintf(
1568
				/* translators: 1: wp-config.php. 2: database host */
1569
				__( '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.' ),
1570
				'<code>wp-config.php</code>',
1571
				'<code>' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '</code>'
1572
			) . "</p>\n";
1573
1574
			$message .= "<ul>\n";
1575
			$message .= '<li>' . __( 'Are you sure you have the correct username and password?' ) . "</li>\n";
1576
			$message .= '<li>' . __( 'Are you sure that you have typed the correct hostname?' ) . "</li>\n";
1577
			$message .= '<li>' . __( 'Are you sure that the database server is running?' ) . "</li>\n";
1578
			$message .= "</ul>\n";
1579
1580
			$message .= '<p>' . sprintf(
1581
				/* translators: %s: support forums URL */
1582
				__( '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>.' ),
1583
				__( 'https://wordpress.org/support/' )
1584
			) . "</p>\n";
1585
1586
			$this->bail( $message, 'db_connect_fail' );
1587
1588
			return false;
1589
		} elseif ( $this->dbh ) {
1590
			if ( ! $this->has_connected ) {
1591
				$this->init_charset();
1592
			}
1593
1594
			$this->has_connected = true;
1595
1596
			$this->set_charset( $this->dbh );
1597
1598
			$this->ready = true;
1599
			$this->set_sql_mode();
1600
			$this->select( $this->dbname, $this->dbh );
1601
1602
			return true;
1603
		}
1604
1605
		return false;
1606
	}
1607
1608
	/**
1609
	 * Checks that the connection to the database is still up. If not, try to reconnect.
1610
	 *
1611
	 * If this function is unable to reconnect, it will forcibly die, or if after the
1612
	 * the {@see 'template_redirect'} hook has been fired, return false instead.
1613
	 *
1614
	 * If $allow_bail is false, the lack of database connection will need
1615
	 * to be handled manually.
1616
	 *
1617
	 * @since 3.9.0
1618
	 *
1619
	 * @param bool $allow_bail Optional. Allows the function to bail. Default true.
1620
	 * @return bool|void True if the connection is up.
1621
	 */
1622
	public function check_connection( $allow_bail = true ) {
1623
		if ( $this->use_mysqli ) {
1624
			if ( ! empty( $this->dbh ) && mysqli_ping( $this->dbh ) ) {
1625
				return true;
1626
			}
1627
		} else {
1628
			if ( ! empty( $this->dbh ) && mysql_ping( $this->dbh ) ) {
1629
				return true;
1630
			}
1631
		}
1632
1633
		$error_reporting = false;
1634
1635
		// Disable warnings, as we don't want to see a multitude of "unable to connect" messages
1636
		if ( WP_DEBUG ) {
1637
			$error_reporting = error_reporting();
1638
			error_reporting( $error_reporting & ~E_WARNING );
1639
		}
1640
1641
		for ( $tries = 1; $tries <= $this->reconnect_retries; $tries++ ) {
1642
			// On the last try, re-enable warnings. We want to see a single instance of the
1643
			// "unable to connect" message on the bail() screen, if it appears.
1644
			if ( $this->reconnect_retries === $tries && WP_DEBUG ) {
1645
				error_reporting( $error_reporting );
1646
			}
1647
1648
			if ( $this->db_connect( false ) ) {
1649
				if ( $error_reporting ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error_reporting of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1650
					error_reporting( $error_reporting );
1651
				}
1652
1653
				return true;
1654
			}
1655
1656
			sleep( 1 );
1657
		}
1658
1659
		// If template_redirect has already happened, it's too late for wp_die()/dead_db().
1660
		// Let's just return and hope for the best.
1661
		if ( did_action( 'template_redirect' ) ) {
1662
			return false;
1663
		}
1664
1665
		if ( ! $allow_bail ) {
1666
			return false;
1667
		}
1668
1669
		wp_load_translations_early();
1670
1671
		$message = '<h1>' . __( 'Error reconnecting to the database' ) . "</h1>\n";
1672
1673
		$message .= '<p>' . sprintf(
1674
			/* translators: %s: database host */
1675
			__( 'This means that we lost contact with the database server at %s. This could mean your host&#8217;s database server is down.' ),
1676
			'<code>' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '</code>'
1677
		) . "</p>\n";
1678
1679
		$message .= "<ul>\n";
1680
		$message .= '<li>' . __( 'Are you sure that the database server is running?' ) . "</li>\n";
1681
		$message .= '<li>' . __( 'Are you sure that the database server is not under particularly heavy load?' ) . "</li>\n";
1682
		$message .= "</ul>\n";
1683
1684
		$message .= '<p>' . sprintf(
1685
			/* translators: %s: support forums URL */
1686
			__( '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>.' ),
1687
			__( 'https://wordpress.org/support/' )
1688
		) . "</p>\n";
1689
1690
		// We weren't able to reconnect, so we better bail.
1691
		$this->bail( $message, 'db_connect_fail' );
1692
1693
		// Call dead_db() if bail didn't die, because this database is no more. It has ceased to be (at least temporarily).
1694
		dead_db();
1695
	}
1696
1697
	/**
1698
	 * Perform a MySQL database query, using current database connection.
1699
	 *
1700
	 * More information can be found on the codex page.
1701
	 *
1702
	 * @since 0.71
1703
	 *
1704
	 * @param string $query Database query
1705
	 * @return int|false Number of rows affected/selected or false on error
1706
	 */
1707
	public function query( $query ) {
1708
		if ( ! $this->ready ) {
1709
			$this->check_current_query = true;
1710
			return false;
1711
		}
1712
1713
		/**
1714
		 * Filters the database query.
1715
		 *
1716
		 * Some queries are made before the plugins have been loaded,
1717
		 * and thus cannot be filtered with this method.
1718
		 *
1719
		 * @since 2.1.0
1720
		 *
1721
		 * @param string $query Database query.
1722
		 */
1723
		$query = apply_filters( 'query', $query );
1724
1725
		$this->flush();
1726
1727
		// Log how the function was called
1728
		$this->func_call = "\$db->query(\"$query\")";
1729
1730
		// If we're writing to the database, make sure the query will write safely.
1731
		if ( $this->check_current_query && ! $this->check_ascii( $query ) ) {
1732
			$stripped_query = $this->strip_invalid_text_from_query( $query );
1733
			// strip_invalid_text_from_query() can perform queries, so we need
1734
			// to flush again, just to make sure everything is clear.
1735
			$this->flush();
1736
			if ( $stripped_query !== $query ) {
1737
				$this->insert_id = 0;
1738
				return false;
1739
			}
1740
		}
1741
1742
		$this->check_current_query = true;
1743
1744
		// Keep track of the last query for debug.
1745
		$this->last_query = $query;
0 ignored issues
show
Documentation Bug introduced by
It seems like $query of type * is incompatible with the declared type array of property $last_query.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1746
1747
		$this->_do_query( $query );
1748
1749
		// MySQL server has gone away, try to reconnect.
1750
		$mysql_errno = 0;
1751
		if ( ! empty( $this->dbh ) ) {
1752
			if ( $this->use_mysqli ) {
1753
				if ( $this->dbh instanceof mysqli ) {
1754
					$mysql_errno = mysqli_errno( $this->dbh );
1755
				} else {
1756
					// $dbh is defined, but isn't a real connection.
1757
					// Something has gone horribly wrong, let's try a reconnect.
1758
					$mysql_errno = 2006;
1759
				}
1760
			} else {
1761
				if ( is_resource( $this->dbh ) ) {
1762
					$mysql_errno = mysql_errno( $this->dbh );
1763
				} else {
1764
					$mysql_errno = 2006;
1765
				}
1766
			}
1767
		}
1768
1769
		if ( empty( $this->dbh ) || 2006 == $mysql_errno ) {
1770
			if ( $this->check_connection() ) {
1771
				$this->_do_query( $query );
1772
			} else {
1773
				$this->insert_id = 0;
1774
				return false;
1775
			}
1776
		}
1777
1778
		// If there is an error then take note of it.
1779
		if ( $this->use_mysqli ) {
1780
			if ( $this->dbh instanceof mysqli ) {
1781
				$this->last_error = mysqli_error( $this->dbh );
1782
			} else {
1783
				$this->last_error = __( 'Unable to retrieve the error message from MySQL' );
1784
			}
1785
		} else {
1786
			if ( is_resource( $this->dbh ) ) {
1787
				$this->last_error = mysql_error( $this->dbh );
1788
			} else {
1789
				$this->last_error = __( 'Unable to retrieve the error message from MySQL' );
1790
			}
1791
		}
1792
1793
		if ( $this->last_error ) {
1794
			// Clear insert_id on a subsequent failed insert.
1795
			if ( $this->insert_id && preg_match( '/^\s*(insert|replace)\s/i', $query ) )
1796
				$this->insert_id = 0;
1797
1798
			$this->print_error();
1799
			return false;
1800
		}
1801
1802
		if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) {
1803
			$return_val = $this->result;
1804
		} elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) {
1805
			if ( $this->use_mysqli ) {
1806
				$this->rows_affected = mysqli_affected_rows( $this->dbh );
1807
			} else {
1808
				$this->rows_affected = mysql_affected_rows( $this->dbh );
1809
			}
1810
			// Take note of the insert_id
1811
			if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
1812
				if ( $this->use_mysqli ) {
1813
					$this->insert_id = mysqli_insert_id( $this->dbh );
1814
				} else {
1815
					$this->insert_id = mysql_insert_id( $this->dbh );
1816
				}
1817
			}
1818
			// Return number of rows affected
1819
			$return_val = $this->rows_affected;
1820
		} else {
1821
			$num_rows = 0;
1822
			if ( $this->use_mysqli && $this->result instanceof mysqli_result ) {
1823
				while ( $row = mysqli_fetch_object( $this->result ) ) {
1824
					$this->last_result[$num_rows] = $row;
1825
					$num_rows++;
1826
				}
1827
			} elseif ( is_resource( $this->result ) ) {
1828
				while ( $row = mysql_fetch_object( $this->result ) ) {
1829
					$this->last_result[$num_rows] = $row;
1830
					$num_rows++;
1831
				}
1832
			}
1833
1834
			// Log number of rows the query returned
1835
			// and return number of rows selected
1836
			$this->num_rows = $num_rows;
1837
			$return_val     = $num_rows;
1838
		}
1839
1840
		return $return_val;
1841
	}
1842
1843
	/**
1844
	 * Internal function to perform the mysql_query() call.
1845
	 *
1846
	 * @since 3.9.0
1847
	 *
1848
	 * @access private
1849
	 * @see wpdb::query()
1850
	 *
1851
	 * @param string $query The query to run.
1852
	 */
1853
	private function _do_query( $query ) {
1854
		if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
1855
			$this->timer_start();
1856
		}
1857
1858
		if ( ! empty( $this->dbh ) && $this->use_mysqli ) {
1859
			$this->result = mysqli_query( $this->dbh, $query );
1860
		} elseif ( ! empty( $this->dbh ) ) {
1861
			$this->result = mysql_query( $query, $this->dbh );
1862
		}
1863
		$this->num_queries++;
1864
1865
		if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
1866
			$this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() );
1867
		}
1868
	}
1869
1870
	/**
1871
	 * Insert a row into a table.
1872
	 *
1873
	 *     wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 'bar' ) )
1874
	 *     wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) )
1875
	 *
1876
	 * @since 2.5.0
1877
	 * @see wpdb::prepare()
1878
	 * @see wpdb::$field_types
1879
	 * @see wp_set_wpdb_vars()
1880
	 *
1881
	 * @param string       $table  Table name
1882
	 * @param array        $data   Data to insert (in column => value pairs).
1883
	 *                             Both $data columns and $data values should be "raw" (neither should be SQL escaped).
1884
	 *                             Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
1885
	 * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
1886
	 *                             If string, that format will be used for all of the values in $data.
1887
	 *                             A format is one of '%d', '%f', '%s' (integer, float, string).
1888
	 *                             If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
1889
	 * @return int|false The number of rows inserted, or false on error.
1890
	 */
1891
	public function insert( $table, $data, $format = null ) {
1892
		return $this->_insert_replace_helper( $table, $data, $format, 'INSERT' );
1893
	}
1894
1895
	/**
1896
	 * Replace a row into a table.
1897
	 *
1898
	 *     wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 'bar' ) )
1899
	 *     wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) )
1900
	 *
1901
	 * @since 3.0.0
1902
	 * @see wpdb::prepare()
1903
	 * @see wpdb::$field_types
1904
	 * @see wp_set_wpdb_vars()
1905
	 *
1906
	 * @param string       $table  Table name
1907
	 * @param array        $data   Data to insert (in column => value pairs).
1908
	 *                             Both $data columns and $data values should be "raw" (neither should be SQL escaped).
1909
	 *                             Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
1910
	 * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
1911
	 *                             If string, that format will be used for all of the values in $data.
1912
	 *                             A format is one of '%d', '%f', '%s' (integer, float, string).
1913
	 *                             If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
1914
	 * @return int|false The number of rows affected, or false on error.
1915
	 */
1916
	public function replace( $table, $data, $format = null ) {
1917
		return $this->_insert_replace_helper( $table, $data, $format, 'REPLACE' );
1918
	}
1919
1920
	/**
1921
	 * Helper function for insert and replace.
1922
	 *
1923
	 * Runs an insert or replace query based on $type argument.
1924
	 *
1925
	 * @access private
1926
	 * @since 3.0.0
1927
	 * @see wpdb::prepare()
1928
	 * @see wpdb::$field_types
1929
	 * @see wp_set_wpdb_vars()
1930
	 *
1931
	 * @param string       $table  Table name
1932
	 * @param array        $data   Data to insert (in column => value pairs).
1933
	 *                             Both $data columns and $data values should be "raw" (neither should be SQL escaped).
1934
	 *                             Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case.
1935
	 * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data.
1936
	 *                             If string, that format will be used for all of the values in $data.
1937
	 *                             A format is one of '%d', '%f', '%s' (integer, float, string).
1938
	 *                             If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
1939
	 * @param string $type         Optional. What type of operation is this? INSERT or REPLACE. Defaults to INSERT.
1940
	 * @return int|false The number of rows affected, or false on error.
1941
	 */
1942
	function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for _insert_replace_helper.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
1943
		$this->insert_id = 0;
1944
1945
		if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) {
1946
			return false;
1947
		}
1948
1949
		$data = $this->process_fields( $table, $data, $format );
1950
		if ( false === $data ) {
1951
			return false;
1952
		}
1953
1954
		$formats = $values = array();
1955
		foreach ( $data as $value ) {
1956
			if ( is_null( $value['value'] ) ) {
1957
				$formats[] = 'NULL';
1958
				continue;
1959
			}
1960
1961
			$formats[] = $value['format'];
1962
			$values[]  = $value['value'];
1963
		}
1964
1965
		$fields  = '`' . implode( '`, `', array_keys( $data ) ) . '`';
1966
		$formats = implode( ', ', $formats );
1967
1968
		$sql = "$type INTO `$table` ($fields) VALUES ($formats)";
1969
1970
		$this->check_current_query = false;
1971
		return $this->query( $this->prepare( $sql, $values ) );
1972
	}
1973
1974
	/**
1975
	 * Update a row in the table
1976
	 *
1977
	 *     wpdb::update( 'table', array( 'column' => 'foo', 'field' => 'bar' ), array( 'ID' => 1 ) )
1978
	 *     wpdb::update( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( 'ID' => 1 ), array( '%s', '%d' ), array( '%d' ) )
1979
	 *
1980
	 * @since 2.5.0
1981
	 * @see wpdb::prepare()
1982
	 * @see wpdb::$field_types
1983
	 * @see wp_set_wpdb_vars()
1984
	 *
1985
	 * @param string       $table        Table name
1986
	 * @param array        $data         Data to update (in column => value pairs).
1987
	 *                                   Both $data columns and $data values should be "raw" (neither should be SQL escaped).
1988
	 *                                   Sending a null value will cause the column to be set to NULL - the corresponding
1989
	 *                                   format is ignored in this case.
1990
	 * @param array        $where        A named array of WHERE clauses (in column => value pairs).
1991
	 *                                   Multiple clauses will be joined with ANDs.
1992
	 *                                   Both $where columns and $where values should be "raw".
1993
	 *                                   Sending a null value will create an IS NULL comparison - the corresponding format will be ignored in this case.
1994
	 * @param array|string $format       Optional. An array of formats to be mapped to each of the values in $data.
1995
	 *                                   If string, that format will be used for all of the values in $data.
1996
	 *                                   A format is one of '%d', '%f', '%s' (integer, float, string).
1997
	 *                                   If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types.
1998
	 * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where.
1999
	 *                                   If string, that format will be used for all of the items in $where.
2000
	 *                                   A format is one of '%d', '%f', '%s' (integer, float, string).
2001
	 *                                   If omitted, all values in $where will be treated as strings.
2002
	 * @return int|false The number of rows updated, or false on error.
2003
	 */
2004
	public function update( $table, $data, $where, $format = null, $where_format = null ) {
2005
		if ( ! is_array( $data ) || ! is_array( $where ) ) {
2006
			return false;
2007
		}
2008
2009
		$data = $this->process_fields( $table, $data, $format );
2010
		if ( false === $data ) {
2011
			return false;
2012
		}
2013
		$where = $this->process_fields( $table, $where, $where_format );
2014
		if ( false === $where ) {
2015
			return false;
2016
		}
2017
2018
		$fields = $conditions = $values = array();
2019 View Code Duplication
		foreach ( $data as $field => $value ) {
2020
			if ( is_null( $value['value'] ) ) {
2021
				$fields[] = "`$field` = NULL";
2022
				continue;
2023
			}
2024
2025
			$fields[] = "`$field` = " . $value['format'];
2026
			$values[] = $value['value'];
2027
		}
2028 View Code Duplication
		foreach ( $where as $field => $value ) {
2029
			if ( is_null( $value['value'] ) ) {
2030
				$conditions[] = "`$field` IS NULL";
2031
				continue;
2032
			}
2033
2034
			$conditions[] = "`$field` = " . $value['format'];
2035
			$values[] = $value['value'];
2036
		}
2037
2038
		$fields = implode( ', ', $fields );
2039
		$conditions = implode( ' AND ', $conditions );
2040
2041
		$sql = "UPDATE `$table` SET $fields WHERE $conditions";
2042
2043
		$this->check_current_query = false;
2044
		return $this->query( $this->prepare( $sql, $values ) );
2045
	}
2046
2047
	/**
2048
	 * Delete a row in the table
2049
	 *
2050
	 *     wpdb::delete( 'table', array( 'ID' => 1 ) )
2051
	 *     wpdb::delete( 'table', array( 'ID' => 1 ), array( '%d' ) )
2052
	 *
2053
	 * @since 3.4.0
2054
	 * @see wpdb::prepare()
2055
	 * @see wpdb::$field_types
2056
	 * @see wp_set_wpdb_vars()
2057
	 *
2058
	 * @param string       $table        Table name
2059
	 * @param array        $where        A named array of WHERE clauses (in column => value pairs).
2060
	 *                                   Multiple clauses will be joined with ANDs.
2061
	 *                                   Both $where columns and $where values should be "raw".
2062
	 *                                   Sending a null value will create an IS NULL comparison - the corresponding format will be ignored in this case.
2063
	 * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where.
2064
	 *                                   If string, that format will be used for all of the items in $where.
2065
	 *                                   A format is one of '%d', '%f', '%s' (integer, float, string).
2066
	 *                                   If omitted, all values in $where will be treated as strings unless otherwise specified in wpdb::$field_types.
2067
	 * @return int|false The number of rows updated, or false on error.
2068
	 */
2069
	public function delete( $table, $where, $where_format = null ) {
2070
		if ( ! is_array( $where ) ) {
2071
			return false;
2072
		}
2073
2074
		$where = $this->process_fields( $table, $where, $where_format );
2075
		if ( false === $where ) {
2076
			return false;
2077
		}
2078
2079
		$conditions = $values = array();
2080 View Code Duplication
		foreach ( $where as $field => $value ) {
2081
			if ( is_null( $value['value'] ) ) {
2082
				$conditions[] = "`$field` IS NULL";
2083
				continue;
2084
			}
2085
2086
			$conditions[] = "`$field` = " . $value['format'];
2087
			$values[] = $value['value'];
2088
		}
2089
2090
		$conditions = implode( ' AND ', $conditions );
2091
2092
		$sql = "DELETE FROM `$table` WHERE $conditions";
2093
2094
		$this->check_current_query = false;
2095
		return $this->query( $this->prepare( $sql, $values ) );
2096
	}
2097
2098
	/**
2099
	 * Processes arrays of field/value pairs and field formats.
2100
	 *
2101
	 * This is a helper method for wpdb's CRUD methods, which take field/value
2102
	 * pairs for inserts, updates, and where clauses. This method first pairs
2103
	 * each value with a format. Then it determines the charset of that field,
2104
	 * using that to determine if any invalid text would be stripped. If text is
2105
	 * stripped, then field processing is rejected and the query fails.
2106
	 *
2107
	 * @since 4.2.0
2108
	 * @access protected
2109
	 *
2110
	 * @param string $table  Table name.
2111
	 * @param array  $data   Field/value pair.
2112
	 * @param mixed  $format Format for each field.
2113
	 * @return array|false Returns an array of fields that contain paired values
2114
	 *                    and formats. Returns false for invalid values.
2115
	 */
2116
	protected function process_fields( $table, $data, $format ) {
2117
		$data = $this->process_field_formats( $data, $format );
2118
		if ( false === $data ) {
2119
			return false;
2120
		}
2121
2122
		$data = $this->process_field_charsets( $data, $table );
2123
		if ( false === $data ) {
2124
			return false;
2125
		}
2126
2127
		$data = $this->process_field_lengths( $data, $table );
2128
		if ( false === $data ) {
2129
			return false;
2130
		}
2131
2132
		$converted_data = $this->strip_invalid_text( $data );
2133
2134
		if ( $data !== $converted_data ) {
2135
			return false;
2136
		}
2137
2138
		return $data;
2139
	}
2140
2141
	/**
2142
	 * Prepares arrays of value/format pairs as passed to wpdb CRUD methods.
2143
	 *
2144
	 * @since 4.2.0
2145
	 * @access protected
2146
	 *
2147
	 * @param array $data   Array of fields to values.
2148
	 * @param mixed $format Formats to be mapped to the values in $data.
2149
	 * @return array Array, keyed by field names with values being an array
2150
	 *               of 'value' and 'format' keys.
2151
	 */
2152
	protected function process_field_formats( $data, $format ) {
2153
		$formats = $original_formats = (array) $format;
2154
2155
		foreach ( $data as $field => $value ) {
2156
			$value = array(
2157
				'value'  => $value,
2158
				'format' => '%s',
2159
			);
2160
2161
			if ( ! empty( $format ) ) {
2162
				$value['format'] = array_shift( $formats );
2163
				if ( ! $value['format'] ) {
2164
					$value['format'] = reset( $original_formats );
2165
				}
2166
			} elseif ( isset( $this->field_types[ $field ] ) ) {
2167
				$value['format'] = $this->field_types[ $field ];
2168
			}
2169
2170
			$data[ $field ] = $value;
2171
		}
2172
2173
		return $data;
2174
	}
2175
2176
	/**
2177
	 * Adds field charsets to field/value/format arrays generated by
2178
	 * the wpdb::process_field_formats() method.
2179
	 *
2180
	 * @since 4.2.0
2181
	 * @access protected
2182
	 *
2183
	 * @param array  $data  As it comes from the wpdb::process_field_formats() method.
2184
	 * @param string $table Table name.
2185
	 * @return array|false The same array as $data with additional 'charset' keys.
2186
	 */
2187 View Code Duplication
	protected function process_field_charsets( $data, $table ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2188
		foreach ( $data as $field => $value ) {
2189
			if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
2190
				/*
2191
				 * We can skip this field if we know it isn't a string.
2192
				 * This checks %d/%f versus ! %s because its sprintf() could take more.
2193
				 */
2194
				$value['charset'] = false;
2195
			} else {
2196
				$value['charset'] = $this->get_col_charset( $table, $field );
2197
				if ( is_wp_error( $value['charset'] ) ) {
2198
					return false;
2199
				}
2200
			}
2201
2202
			$data[ $field ] = $value;
2203
		}
2204
2205
		return $data;
2206
	}
2207
2208
	/**
2209
	 * For string fields, record the maximum string length that field can safely save.
2210
	 *
2211
	 * @since 4.2.1
2212
	 * @access protected
2213
	 *
2214
	 * @param array  $data  As it comes from the wpdb::process_field_charsets() method.
2215
	 * @param string $table Table name.
2216
	 * @return array|false The same array as $data with additional 'length' keys, or false if
2217
	 *                     any of the values were too long for their corresponding field.
2218
	 */
2219 View Code Duplication
	protected function process_field_lengths( $data, $table ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2220
		foreach ( $data as $field => $value ) {
2221
			if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
2222
				/*
2223
				 * We can skip this field if we know it isn't a string.
2224
				 * This checks %d/%f versus ! %s because its sprintf() could take more.
2225
				 */
2226
				$value['length'] = false;
2227
			} else {
2228
				$value['length'] = $this->get_col_length( $table, $field );
2229
				if ( is_wp_error( $value['length'] ) ) {
2230
					return false;
2231
				}
2232
			}
2233
2234
			$data[ $field ] = $value;
2235
		}
2236
2237
		return $data;
2238
	}
2239
2240
	/**
2241
	 * Retrieve one variable from the database.
2242
	 *
2243
	 * Executes a SQL query and returns the value from the SQL result.
2244
	 * 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.
2245
	 * If $query is null, this function returns the value in the specified column and row from the previous SQL result.
2246
	 *
2247
	 * @since 0.71
2248
	 *
2249
	 * @param string|null $query Optional. SQL query. Defaults to null, use the result from the previous query.
2250
	 * @param int         $x     Optional. Column of value to return. Indexed from 0.
2251
	 * @param int         $y     Optional. Row of value to return. Indexed from 0.
2252
	 * @return string|null Database query result (as string), or null on failure
2253
	 */
2254
	public function get_var( $query = null, $x = 0, $y = 0 ) {
2255
		$this->func_call = "\$db->get_var(\"$query\", $x, $y)";
2256
2257
		if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2258
			$this->check_current_query = false;
2259
		}
2260
2261
		if ( $query ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $query of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2262
			$this->query( $query );
2263
		}
2264
2265
		// Extract var out of cached results based x,y vals
2266
		if ( !empty( $this->last_result[$y] ) ) {
2267
			$values = array_values( get_object_vars( $this->last_result[$y] ) );
2268
		}
2269
2270
		// If there is a value return it else return null
2271
		return ( isset( $values[$x] ) && $values[$x] !== '' ) ? $values[$x] : null;
2272
	}
2273
2274
	/**
2275
	 * Retrieve one row from the database.
2276
	 *
2277
	 * Executes a SQL query and returns the row from the SQL result.
2278
	 *
2279
	 * @since 0.71
2280
	 *
2281
	 * @param string|null $query  SQL query.
2282
	 * @param string      $output Optional. one of ARRAY_A | ARRAY_N | OBJECT constants.
2283
	 *                            Return an associative array (column => value, ...),
2284
	 *                            a numerically indexed array (0 => value, ...) or
2285
	 *                            an object ( ->column = value ), respectively.
2286
	 * @param int         $y      Optional. Row to return. Indexed from 0.
2287
	 * @return array|object|null|void Database query result in format specified by $output or null on failure
2288
	 */
2289
	public function get_row( $query = null, $output = OBJECT, $y = 0 ) {
2290
		$this->func_call = "\$db->get_row(\"$query\",$output,$y)";
2291
2292
		if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2293
			$this->check_current_query = false;
2294
		}
2295
2296
		if ( $query ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $query of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2297
			$this->query( $query );
2298
		} else {
2299
			return null;
2300
		}
2301
2302
		if ( !isset( $this->last_result[$y] ) )
2303
			return null;
2304
2305
		if ( $output == OBJECT ) {
2306
			return $this->last_result[$y] ? $this->last_result[$y] : null;
2307
		} elseif ( $output == ARRAY_A ) {
2308
			return $this->last_result[$y] ? get_object_vars( $this->last_result[$y] ) : null;
2309
		} elseif ( $output == ARRAY_N ) {
2310
			return $this->last_result[$y] ? array_values( get_object_vars( $this->last_result[$y] ) ) : null;
2311
		} elseif ( strtoupper( $output ) === OBJECT ) {
2312
			// Back compat for OBJECT being previously case insensitive.
2313
			return $this->last_result[$y] ? $this->last_result[$y] : null;
2314
		} else {
2315
			$this->print_error( " \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N" );
2316
		}
2317
	}
2318
2319
	/**
2320
	 * Retrieve one column from the database.
2321
	 *
2322
	 * Executes a SQL query and returns the column from the SQL result.
2323
	 * If the SQL result contains more than one column, this function returns the column specified.
2324
	 * If $query is null, this function returns the specified column from the previous SQL result.
2325
	 *
2326
	 * @since 0.71
2327
	 *
2328
	 * @param string|null $query Optional. SQL query. Defaults to previous query.
2329
	 * @param int         $x     Optional. Column to return. Indexed from 0.
2330
	 * @return array Database query result. Array indexed from 0 by SQL result row number.
2331
	 */
2332
	public function get_col( $query = null , $x = 0 ) {
2333
		if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2334
			$this->check_current_query = false;
2335
		}
2336
2337
		if ( $query ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $query of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2338
			$this->query( $query );
2339
		}
2340
2341
		$new_array = array();
2342
		// Extract the column values
2343
		for ( $i = 0, $j = count( $this->last_result ); $i < $j; $i++ ) {
2344
			$new_array[$i] = $this->get_var( null, $x, $i );
2345
		}
2346
		return $new_array;
2347
	}
2348
2349
	/**
2350
	 * Retrieve an entire SQL result set from the database (i.e., many rows)
2351
	 *
2352
	 * Executes a SQL query and returns the entire SQL result.
2353
	 *
2354
	 * @since 0.71
2355
	 *
2356
	 * @param string $query  SQL query.
2357
	 * @param string $output Optional. Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants.
2358
	 *                       With one of the first three, return an array of rows indexed from 0 by SQL result row number.
2359
	 *                       Each row is an associative array (column => value, ...), a numerically indexed array (0 => value, ...), or an object. ( ->column = value ), respectively.
2360
	 *                       With OBJECT_K, return an associative array of row objects keyed by the value of each row's first column's value.
2361
	 *                       Duplicate keys are discarded.
2362
	 * @return array|object|null Database query results
2363
	 */
2364
	public function get_results( $query = null, $output = OBJECT ) {
2365
		$this->func_call = "\$db->get_results(\"$query\", $output)";
2366
2367
		if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
2368
			$this->check_current_query = false;
2369
		}
2370
2371
		if ( $query ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $query of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2372
			$this->query( $query );
2373
		} else {
2374
			return null;
2375
		}
2376
2377
		$new_array = array();
2378
		if ( $output == OBJECT ) {
2379
			// Return an integer-keyed array of row objects
2380
			return $this->last_result;
2381
		} elseif ( $output == OBJECT_K ) {
2382
			// Return an array of row objects with keys from column 1
2383
			// (Duplicates are discarded)
2384
			foreach ( $this->last_result as $row ) {
0 ignored issues
show
The expression $this->last_result of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2385
				$var_by_ref = get_object_vars( $row );
2386
				$key = array_shift( $var_by_ref );
2387
				if ( ! isset( $new_array[ $key ] ) )
2388
					$new_array[ $key ] = $row;
2389
			}
2390
			return $new_array;
2391
		} elseif ( $output == ARRAY_A || $output == ARRAY_N ) {
2392
			// Return an integer-keyed array of...
2393
			if ( $this->last_result ) {
2394
				foreach ( (array) $this->last_result as $row ) {
2395
					if ( $output == ARRAY_N ) {
2396
						// ...integer-keyed row arrays
2397
						$new_array[] = array_values( get_object_vars( $row ) );
2398
					} else {
2399
						// ...column name-keyed row arrays
2400
						$new_array[] = get_object_vars( $row );
2401
					}
2402
				}
2403
			}
2404
			return $new_array;
2405
		} elseif ( strtoupper( $output ) === OBJECT ) {
2406
			// Back compat for OBJECT being previously case insensitive.
2407
			return $this->last_result;
2408
		}
2409
		return null;
2410
	}
2411
2412
	/**
2413
	 * Retrieves the character set for the given table.
2414
	 *
2415
	 * @since 4.2.0
2416
	 * @access protected
2417
	 *
2418
	 * @param string $table Table name.
2419
	 * @return string|WP_Error Table character set, WP_Error object if it couldn't be found.
2420
	 */
2421
	protected function get_table_charset( $table ) {
2422
		$tablekey = strtolower( $table );
2423
2424
		/**
2425
		 * Filters the table charset value before the DB is checked.
2426
		 *
2427
		 * Passing a non-null value to the filter will effectively short-circuit
2428
		 * checking the DB for the charset, returning that value instead.
2429
		 *
2430
		 * @since 4.2.0
2431
		 *
2432
		 * @param string $charset The character set to use. Default null.
2433
		 * @param string $table   The name of the table being checked.
2434
		 */
2435
		$charset = apply_filters( 'pre_get_table_charset', null, $table );
2436
		if ( null !== $charset ) {
2437
			return $charset;
2438
		}
2439
2440
		if ( isset( $this->table_charset[ $tablekey ] ) ) {
2441
			return $this->table_charset[ $tablekey ];
2442
		}
2443
2444
		$charsets = $columns = array();
2445
2446
		$table_parts = explode( '.', $table );
2447
		$table = '`' . implode( '`.`', $table_parts ) . '`';
2448
		$results = $this->get_results( "SHOW FULL COLUMNS FROM $table" );
2449
		if ( ! $results ) {
2450
			return new WP_Error( 'wpdb_get_table_charset_failure' );
2451
		}
2452
2453
		foreach ( $results as $column ) {
2454
			$columns[ strtolower( $column->Field ) ] = $column;
2455
		}
2456
2457
		$this->col_meta[ $tablekey ] = $columns;
2458
2459
		foreach ( $columns as $column ) {
2460
			if ( ! empty( $column->Collation ) ) {
2461
				list( $charset ) = explode( '_', $column->Collation );
2462
2463
				// If the current connection can't support utf8mb4 characters, let's only send 3-byte utf8 characters.
2464
				if ( 'utf8mb4' === $charset && ! $this->has_cap( 'utf8mb4' ) ) {
2465
					$charset = 'utf8';
2466
				}
2467
2468
				$charsets[ strtolower( $charset ) ] = true;
2469
			}
2470
2471
			list( $type ) = explode( '(', $column->Type );
2472
2473
			// A binary/blob means the whole query gets treated like this.
2474
			if ( in_array( strtoupper( $type ), array( 'BINARY', 'VARBINARY', 'TINYBLOB', 'MEDIUMBLOB', 'BLOB', 'LONGBLOB' ) ) ) {
2475
				$this->table_charset[ $tablekey ] = 'binary';
2476
				return 'binary';
2477
			}
2478
		}
2479
2480
		// utf8mb3 is an alias for utf8.
2481
		if ( isset( $charsets['utf8mb3'] ) ) {
2482
			$charsets['utf8'] = true;
2483
			unset( $charsets['utf8mb3'] );
2484
		}
2485
2486
		// Check if we have more than one charset in play.
2487
		$count = count( $charsets );
2488
		if ( 1 === $count ) {
2489
			$charset = key( $charsets );
2490
		} elseif ( 0 === $count ) {
2491
			// No charsets, assume this table can store whatever.
2492
			$charset = false;
2493
		} else {
2494
			// More than one charset. Remove latin1 if present and recalculate.
2495
			unset( $charsets['latin1'] );
2496
			$count = count( $charsets );
2497
			if ( 1 === $count ) {
2498
				// Only one charset (besides latin1).
2499
				$charset = key( $charsets );
2500
			} elseif ( 2 === $count && isset( $charsets['utf8'], $charsets['utf8mb4'] ) ) {
2501
				// Two charsets, but they're utf8 and utf8mb4, use utf8.
2502
				$charset = 'utf8';
2503
			} else {
2504
				// Two mixed character sets. ascii.
2505
				$charset = 'ascii';
2506
			}
2507
		}
2508
2509
		$this->table_charset[ $tablekey ] = $charset;
2510
		return $charset;
2511
	}
2512
2513
	/**
2514
	 * Retrieves the character set for the given column.
2515
	 *
2516
	 * @since 4.2.0
2517
	 * @access public
2518
	 *
2519
	 * @param string $table  Table name.
2520
	 * @param string $column Column name.
2521
	 * @return string|false|WP_Error Column character set as a string. False if the column has no
2522
	 *                               character set. WP_Error object if there was an error.
2523
	 */
2524
	public function get_col_charset( $table, $column ) {
2525
		$tablekey = strtolower( $table );
2526
		$columnkey = strtolower( $column );
2527
2528
		/**
2529
		 * Filters the column charset value before the DB is checked.
2530
		 *
2531
		 * Passing a non-null value to the filter will short-circuit
2532
		 * checking the DB for the charset, returning that value instead.
2533
		 *
2534
		 * @since 4.2.0
2535
		 *
2536
		 * @param string $charset The character set to use. Default null.
2537
		 * @param string $table   The name of the table being checked.
2538
		 * @param string $column  The name of the column being checked.
2539
		 */
2540
		$charset = apply_filters( 'pre_get_col_charset', null, $table, $column );
2541
		if ( null !== $charset ) {
2542
			return $charset;
2543
		}
2544
2545
		// Skip this entirely if this isn't a MySQL database.
2546
		if ( empty( $this->is_mysql ) ) {
2547
			return false;
2548
		}
2549
2550 View Code Duplication
		if ( empty( $this->table_charset[ $tablekey ] ) ) {
2551
			// This primes column information for us.
2552
			$table_charset = $this->get_table_charset( $table );
2553
			if ( is_wp_error( $table_charset ) ) {
2554
				return $table_charset;
2555
			}
2556
		}
2557
2558
		// If still no column information, return the table charset.
2559
		if ( empty( $this->col_meta[ $tablekey ] ) ) {
2560
			return $this->table_charset[ $tablekey ];
2561
		}
2562
2563
		// If this column doesn't exist, return the table charset.
2564
		if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {
2565
			return $this->table_charset[ $tablekey ];
2566
		}
2567
2568
		// Return false when it's not a string column.
2569
		if ( empty( $this->col_meta[ $tablekey ][ $columnkey ]->Collation ) ) {
2570
			return false;
2571
		}
2572
2573
		list( $charset ) = explode( '_', $this->col_meta[ $tablekey ][ $columnkey ]->Collation );
2574
		return $charset;
2575
	}
2576
2577
	/**
2578
	 * Retrieve the maximum string length allowed in a given column.
2579
	 * The length may either be specified as a byte length or a character length.
2580
	 *
2581
	 * @since 4.2.1
2582
	 * @access public
2583
	 *
2584
	 * @param string $table  Table name.
2585
	 * @param string $column Column name.
2586
	 * @return array|false|WP_Error array( 'length' => (int), 'type' => 'byte' | 'char' )
2587
	 *                              false if the column has no length (for example, numeric column)
2588
	 *                              WP_Error object if there was an error.
2589
	 */
2590
	public function get_col_length( $table, $column ) {
2591
		$tablekey = strtolower( $table );
2592
		$columnkey = strtolower( $column );
2593
2594
		// Skip this entirely if this isn't a MySQL database.
2595
		if ( empty( $this->is_mysql ) ) {
2596
			return false;
2597
		}
2598
2599 View Code Duplication
		if ( empty( $this->col_meta[ $tablekey ] ) ) {
2600
			// This primes column information for us.
2601
			$table_charset = $this->get_table_charset( $table );
2602
			if ( is_wp_error( $table_charset ) ) {
2603
				return $table_charset;
2604
			}
2605
		}
2606
2607
		if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {
2608
			return false;
2609
		}
2610
2611
		$typeinfo = explode( '(', $this->col_meta[ $tablekey ][ $columnkey ]->Type );
2612
2613
		$type = strtolower( $typeinfo[0] );
2614
		if ( ! empty( $typeinfo[1] ) ) {
2615
			$length = trim( $typeinfo[1], ')' );
2616
		} else {
2617
			$length = false;
2618
		}
2619
2620
		switch( $type ) {
2621
			case 'char':
2622
			case 'varchar':
2623
				return array(
2624
					'type'   => 'char',
2625
					'length' => (int) $length,
2626
				);
2627
2628
			case 'binary':
2629
			case 'varbinary':
2630
				return array(
2631
					'type'   => 'byte',
2632
					'length' => (int) $length,
2633
				);
2634
2635
			case 'tinyblob':
2636
			case 'tinytext':
2637
				return array(
2638
					'type'   => 'byte',
2639
					'length' => 255,        // 2^8 - 1
2640
				);
2641
2642
			case 'blob':
2643
			case 'text':
2644
				return array(
2645
					'type'   => 'byte',
2646
					'length' => 65535,      // 2^16 - 1
2647
				);
2648
2649
			case 'mediumblob':
2650
			case 'mediumtext':
2651
				return array(
2652
					'type'   => 'byte',
2653
					'length' => 16777215,   // 2^24 - 1
2654
				);
2655
2656
			case 'longblob':
2657
			case 'longtext':
2658
				return array(
2659
					'type'   => 'byte',
2660
					'length' => 4294967295, // 2^32 - 1
2661
				);
2662
2663
			default:
2664
				return false;
2665
		}
2666
	}
2667
2668
	/**
2669
	 * Check if a string is ASCII.
2670
	 *
2671
	 * The negative regex is faster for non-ASCII strings, as it allows
2672
	 * the search to finish as soon as it encounters a non-ASCII character.
2673
	 *
2674
	 * @since 4.2.0
2675
	 * @access protected
2676
	 *
2677
	 * @param string $string String to check.
2678
	 * @return bool True if ASCII, false if not.
2679
	 */
2680
	protected function check_ascii( $string ) {
2681
		if ( function_exists( 'mb_check_encoding' ) ) {
2682
			if ( mb_check_encoding( $string, 'ASCII' ) ) {
2683
				return true;
2684
			}
2685
		} elseif ( ! preg_match( '/[^\x00-\x7F]/', $string ) ) {
2686
			return true;
2687
		}
2688
2689
		return false;
2690
	}
2691
2692
	/**
2693
	 * Check if the query is accessing a collation considered safe on the current version of MySQL.
2694
	 *
2695
	 * @since 4.2.0
2696
	 * @access protected
2697
	 *
2698
	 * @param string $query The query to check.
2699
	 * @return bool True if the collation is safe, false if it isn't.
2700
	 */
2701
	protected function check_safe_collation( $query ) {
2702
		if ( $this->checking_collation ) {
2703
			return true;
2704
		}
2705
2706
		// We don't need to check the collation for queries that don't read data.
2707
		$query = ltrim( $query, "\r\n\t (" );
2708
		if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $query ) ) {
2709
			return true;
2710
		}
2711
2712
		// All-ASCII queries don't need extra checking.
2713
		if ( $this->check_ascii( $query ) ) {
2714
			return true;
2715
		}
2716
2717
		$table = $this->get_table_from_query( $query );
2718
		if ( ! $table ) {
2719
			return false;
2720
		}
2721
2722
		$this->checking_collation = true;
2723
		$collation = $this->get_table_charset( $table );
2724
		$this->checking_collation = false;
2725
2726
		// Tables with no collation, or latin1 only, don't need extra checking.
2727
		if ( false === $collation || 'latin1' === $collation ) {
2728
			return true;
2729
		}
2730
2731
		$table = strtolower( $table );
2732
		if ( empty( $this->col_meta[ $table ] ) ) {
2733
			return false;
2734
		}
2735
2736
		// If any of the columns don't have one of these collations, it needs more sanity checking.
2737
		foreach ( $this->col_meta[ $table ] as $col ) {
2738
			if ( empty( $col->Collation ) ) {
2739
				continue;
2740
			}
2741
2742
			if ( ! in_array( $col->Collation, array( 'utf8_general_ci', 'utf8_bin', 'utf8mb4_general_ci', 'utf8mb4_bin' ), true ) ) {
2743
				return false;
2744
			}
2745
		}
2746
2747
		return true;
2748
	}
2749
2750
	/**
2751
	 * Strips any invalid characters based on value/charset pairs.
2752
	 *
2753
	 * @since 4.2.0
2754
	 * @access protected
2755
	 *
2756
	 * @param array $data Array of value arrays. Each value array has the keys
2757
	 *                    'value' and 'charset'. An optional 'ascii' key can be
2758
	 *                    set to false to avoid redundant ASCII checks.
2759
	 * @return array|WP_Error The $data parameter, with invalid characters removed from
2760
	 *                        each value. This works as a passthrough: any additional keys
2761
	 *                        such as 'field' are retained in each value array. If we cannot
2762
	 *                        remove invalid characters, a WP_Error object is returned.
2763
	 */
2764
	protected function strip_invalid_text( $data ) {
2765
		$db_check_string = false;
2766
2767
		foreach ( $data as &$value ) {
2768
			$charset = $value['charset'];
2769
2770
			if ( is_array( $value['length'] ) ) {
2771
				$length = $value['length']['length'];
2772
				$truncate_by_byte_length = 'byte' === $value['length']['type'];
2773
			} else {
2774
				$length = false;
2775
				// Since we have no length, we'll never truncate.
2776
				// Initialize the variable to false. true would take us
2777
				// through an unnecessary (for this case) codepath below.
2778
				$truncate_by_byte_length = false;
2779
			}
2780
2781
			// There's no charset to work with.
2782
			if ( false === $charset ) {
2783
				continue;
2784
			}
2785
2786
			// Column isn't a string.
2787
			if ( ! is_string( $value['value'] ) ) {
2788
				continue;
2789
			}
2790
2791
			$needs_validation = true;
2792
			if (
2793
				// latin1 can store any byte sequence
2794
				'latin1' === $charset
2795
			||
2796
				// ASCII is always OK.
2797
				( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) )
2798
			) {
2799
				$truncate_by_byte_length = true;
2800
				$needs_validation = false;
2801
			}
2802
2803
			if ( $truncate_by_byte_length ) {
2804
				mbstring_binary_safe_encoding();
2805
				if ( false !== $length && strlen( $value['value'] ) > $length ) {
2806
					$value['value'] = substr( $value['value'], 0, $length );
2807
				}
2808
				reset_mbstring_encoding();
2809
2810
				if ( ! $needs_validation ) {
2811
					continue;
2812
				}
2813
			}
2814
2815
			// utf8 can be handled by regex, which is a bunch faster than a DB lookup.
2816
			if ( ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) && function_exists( 'mb_strlen' ) ) {
2817
				$regex = '/
2818
					(
2819
						(?: [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
2820
						|   [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
2821
						|   \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
2822
						|   [\xE1-\xEC][\x80-\xBF]{2}
2823
						|   \xED[\x80-\x9F][\x80-\xBF]
2824
						|   [\xEE-\xEF][\x80-\xBF]{2}';
2825
2826
				if ( 'utf8mb4' === $charset ) {
2827
					$regex .= '
2828
						|    \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
2829
						|    [\xF1-\xF3][\x80-\xBF]{3}
2830
						|    \xF4[\x80-\x8F][\x80-\xBF]{2}
2831
					';
2832
				}
2833
2834
				$regex .= '){1,40}                          # ...one or more times
2835
					)
2836
					| .                                  # anything else
2837
					/x';
2838
				$value['value'] = preg_replace( $regex, '$1', $value['value'] );
2839
2840
2841
				if ( false !== $length && mb_strlen( $value['value'], 'UTF-8' ) > $length ) {
2842
					$value['value'] = mb_substr( $value['value'], 0, $length, 'UTF-8' );
2843
				}
2844
				continue;
2845
			}
2846
2847
			// We couldn't use any local conversions, send it to the DB.
2848
			$value['db'] = $db_check_string = true;
2849
		}
2850
		unset( $value ); // Remove by reference.
2851
2852
		if ( $db_check_string ) {
2853
			$queries = array();
2854
			foreach ( $data as $col => $value ) {
2855
				if ( ! empty( $value['db'] ) ) {
2856
					// We're going to need to truncate by characters or bytes, depending on the length value we have.
2857
					if ( 'byte' === $value['length']['type'] ) {
2858
						// Using binary causes LEFT() to truncate by bytes.
2859
						$charset = 'binary';
2860
					} else {
2861
						$charset = $value['charset'];
2862
					}
2863
2864
					if ( $this->charset ) {
2865
						$connection_charset = $this->charset;
2866
					} else {
2867
						if ( $this->use_mysqli ) {
2868
							$connection_charset = mysqli_character_set_name( $this->dbh );
2869
						} else {
2870
							$connection_charset = mysql_client_encoding();
2871
						}
2872
					}
2873
2874
					if ( is_array( $value['length'] ) ) {
2875
						$queries[ $col ] = $this->prepare( "CONVERT( LEFT( CONVERT( %s USING $charset ), %.0f ) USING $connection_charset )", $value['value'], $value['length']['length'] );
2876
					} else if ( 'binary' !== $charset ) {
2877
						// If we don't have a length, there's no need to convert binary - it will always return the same result.
2878
						$queries[ $col ] = $this->prepare( "CONVERT( CONVERT( %s USING $charset ) USING $connection_charset )", $value['value'] );
2879
					}
2880
2881
					unset( $data[ $col ]['db'] );
2882
				}
2883
			}
2884
2885
			$sql = array();
2886
			foreach ( $queries as $column => $query ) {
2887
				if ( ! $query ) {
2888
					continue;
2889
				}
2890
2891
				$sql[] = $query . " AS x_$column";
2892
			}
2893
2894
			$this->check_current_query = false;
2895
			$row = $this->get_row( "SELECT " . implode( ', ', $sql ), ARRAY_A );
2896
			if ( ! $row ) {
2897
				return new WP_Error( 'wpdb_strip_invalid_text_failure' );
2898
			}
2899
2900
			foreach ( array_keys( $data ) as $column ) {
2901
				if ( isset( $row["x_$column"] ) ) {
2902
					$data[ $column ]['value'] = $row["x_$column"];
2903
				}
2904
			}
2905
		}
2906
2907
		return $data;
2908
	}
2909
2910
	/**
2911
	 * Strips any invalid characters from the query.
2912
	 *
2913
	 * @since 4.2.0
2914
	 * @access protected
2915
	 *
2916
	 * @param string $query Query to convert.
2917
	 * @return string|WP_Error The converted query, or a WP_Error object if the conversion fails.
2918
	 */
2919
	protected function strip_invalid_text_from_query( $query ) {
2920
		// We don't need to check the collation for queries that don't read data.
2921
		$trimmed_query = ltrim( $query, "\r\n\t (" );
2922
		if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $trimmed_query ) ) {
2923
			return $query;
2924
		}
2925
2926
		$table = $this->get_table_from_query( $query );
2927
		if ( $table ) {
2928
			$charset = $this->get_table_charset( $table );
2929
			if ( is_wp_error( $charset ) ) {
2930
				return $charset;
2931
			}
2932
2933
			// We can't reliably strip text from tables containing binary/blob columns
2934
			if ( 'binary' === $charset ) {
2935
				return $query;
2936
			}
2937
		} else {
2938
			$charset = $this->charset;
2939
		}
2940
2941
		$data = array(
2942
			'value'   => $query,
2943
			'charset' => $charset,
2944
			'ascii'   => false,
2945
			'length'  => false,
2946
		);
2947
2948
		$data = $this->strip_invalid_text( array( $data ) );
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->strip_invalid_text(array($data)); of type WP_Error|array adds the type array to the return on line 2950 which is incompatible with the return type documented by wpdb::strip_invalid_text_from_query of type string|WP_Error.
Loading history...
2949
		if ( is_wp_error( $data ) ) {
2950
			return $data;
2951
		}
2952
2953
		return $data[0]['value'];
2954
	}
2955
2956
	/**
2957
	 * Strips any invalid characters from the string for a given table and column.
2958
	 *
2959
	 * @since 4.2.0
2960
	 * @access public
2961
	 *
2962
	 * @param string $table  Table name.
2963
	 * @param string $column Column name.
2964
	 * @param string $value  The text to check.
2965
	 * @return string|WP_Error The converted string, or a WP_Error object if the conversion fails.
2966
	 */
2967
	public function strip_invalid_text_for_column( $table, $column, $value ) {
2968
		if ( ! is_string( $value ) ) {
2969
			return $value;
2970
		}
2971
2972
		$charset = $this->get_col_charset( $table, $column );
2973
		if ( ! $charset ) {
2974
			// Not a string column.
2975
			return $value;
2976
		} elseif ( is_wp_error( $charset ) ) {
2977
			// Bail on real errors.
2978
			return $charset;
2979
		}
2980
2981
		$data = array(
2982
			$column => array(
2983
				'value'   => $value,
2984
				'charset' => $charset,
2985
				'length'  => $this->get_col_length( $table, $column ),
2986
			)
2987
		);
2988
2989
		$data = $this->strip_invalid_text( $data );
2990
		if ( is_wp_error( $data ) ) {
2991
			return $data;
2992
		}
2993
2994
		return $data[ $column ]['value'];
2995
	}
2996
2997
	/**
2998
	 * Find the first table name referenced in a query.
2999
	 *
3000
	 * @since 4.2.0
3001
	 * @access protected
3002
	 *
3003
	 * @param string $query The query to search.
3004
	 * @return string|false $table The table name found, or false if a table couldn't be found.
3005
	 */
3006
	protected function get_table_from_query( $query ) {
3007
		// Remove characters that can legally trail the table name.
3008
		$query = rtrim( $query, ';/-#' );
3009
3010
		// Allow (select...) union [...] style queries. Use the first query's table name.
3011
		$query = ltrim( $query, "\r\n\t (" );
3012
3013
		// Strip everything between parentheses except nested selects.
3014
		$query = preg_replace( '/\((?!\s*select)[^(]*?\)/is', '()', $query );
3015
3016
		// Quickly match most common queries.
3017
		if ( preg_match( '/^\s*(?:'
3018
				. 'SELECT.*?\s+FROM'
3019
				. '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'
3020
				. '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
3021
				. '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
3022
				. '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'
3023
				. ')\s+((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)/is', $query, $maybe ) ) {
3024
			return str_replace( '`', '', $maybe[1] );
3025
		}
3026
3027
		// SHOW TABLE STATUS and SHOW TABLES
3028
		if ( preg_match( '/^\s*(?:'
3029
				. 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)'
3030
				. '|SHOW\s+(?:FULL\s+)?TABLES.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)'
3031
				. ')\W((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)\W/is', $query, $maybe ) ) {
3032
			return str_replace( '`', '', $maybe[1] );
3033
		}
3034
3035
		// Big pattern for the rest of the table-related queries.
3036
		if ( preg_match( '/^\s*(?:'
3037
				. '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'
3038
				. '|DESCRIBE|DESC|EXPLAIN|HANDLER'
3039
				. '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'
3040
				. '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|REPAIR).*\s+TABLE'
3041
				. '|TRUNCATE(?:\s+TABLE)?'
3042
				. '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'
3043
				. '|ALTER(?:\s+IGNORE)?\s+TABLE'
3044
				. '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'
3045
				. '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'
3046
				. '|DROP\s+INDEX.*\s+ON'
3047
				. '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
3048
				. '|(?:GRANT|REVOKE).*ON\s+TABLE'
3049
				. '|SHOW\s+(?:.*FROM|.*TABLE)'
3050
				. ')\s+\(*\s*((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)\s*\)*/is', $query, $maybe ) ) {
3051
			return str_replace( '`', '', $maybe[1] );
3052
		}
3053
3054
		return false;
3055
	}
3056
3057
	/**
3058
	 * Load the column metadata from the last query.
3059
	 *
3060
	 * @since 3.5.0
3061
	 *
3062
	 * @access protected
3063
	 */
3064
	protected function load_col_info() {
3065
		if ( $this->col_info )
3066
			return;
3067
3068
		if ( $this->use_mysqli ) {
3069
			$num_fields = mysqli_num_fields( $this->result );
3070 View Code Duplication
			for ( $i = 0; $i < $num_fields; $i++ ) {
3071
				$this->col_info[ $i ] = mysqli_fetch_field( $this->result );
3072
			}
3073
		} else {
3074
			$num_fields = mysql_num_fields( $this->result );
3075 View Code Duplication
			for ( $i = 0; $i < $num_fields; $i++ ) {
3076
				$this->col_info[ $i ] = mysql_fetch_field( $this->result, $i );
3077
			}
3078
		}
3079
	}
3080
3081
	/**
3082
	 * Retrieve column metadata from the last query.
3083
	 *
3084
	 * @since 0.71
3085
	 *
3086
	 * @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
3087
	 * @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
3088
	 * @return mixed Column Results
3089
	 */
3090
	public function get_col_info( $info_type = 'name', $col_offset = -1 ) {
3091
		$this->load_col_info();
3092
3093
		if ( $this->col_info ) {
3094
			if ( $col_offset == -1 ) {
3095
				$i = 0;
3096
				$new_array = array();
3097
				foreach ( (array) $this->col_info as $col ) {
3098
					$new_array[$i] = $col->{$info_type};
3099
					$i++;
3100
				}
3101
				return $new_array;
3102
			} else {
3103
				return $this->col_info[$col_offset]->{$info_type};
3104
			}
3105
		}
3106
	}
3107
3108
	/**
3109
	 * Starts the timer, for debugging purposes.
3110
	 *
3111
	 * @since 1.5.0
3112
	 *
3113
	 * @return true
3114
	 */
3115
	public function timer_start() {
3116
		$this->time_start = microtime( true );
0 ignored issues
show
The property time_start does not exist on object<wpdb>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3117
		return true;
3118
	}
3119
3120
	/**
3121
	 * Stops the debugging timer.
3122
	 *
3123
	 * @since 1.5.0
3124
	 *
3125
	 * @return float Total time spent on the query, in seconds
3126
	 */
3127
	public function timer_stop() {
3128
		return ( microtime( true ) - $this->time_start );
0 ignored issues
show
The property time_start does not exist on object<wpdb>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3129
	}
3130
3131
	/**
3132
	 * Wraps errors in a nice header and footer and dies.
3133
	 *
3134
	 * Will not die if wpdb::$show_errors is false.
3135
	 *
3136
	 * @since 1.5.0
3137
	 *
3138
	 * @param string $message    The Error message
3139
	 * @param string $error_code Optional. A Computer readable string to identify the error.
3140
	 * @return false|void
3141
	 */
3142
	public function bail( $message, $error_code = '500' ) {
3143
		if ( !$this->show_errors ) {
3144
			if ( class_exists( 'WP_Error', false ) ) {
3145
				$this->error = new WP_Error($error_code, $message);
0 ignored issues
show
The property error does not exist on object<wpdb>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3146
			} else {
3147
				$this->error = $message;
0 ignored issues
show
The property error does not exist on object<wpdb>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3148
			}
3149
			return false;
3150
		}
3151
		wp_die($message);
3152
	}
3153
3154
3155
	/**
3156
	 * Closes the current database connection.
3157
	 *
3158
	 * @since 4.5.0
3159
	 * @access public
3160
	 *
3161
	 * @return bool True if the connection was successfully closed, false if it wasn't,
3162
	 *              or the connection doesn't exist.
3163
	 */
3164
	public function close() {
3165
		if ( ! $this->dbh ) {
3166
			return false;
3167
		}
3168
3169
		if ( $this->use_mysqli ) {
3170
			$closed = mysqli_close( $this->dbh );
3171
		} else {
3172
			$closed = mysql_close( $this->dbh );
3173
		}
3174
3175
		if ( $closed ) {
3176
			$this->dbh = null;
3177
			$this->ready = false;
3178
			$this->has_connected = false;
3179
		}
3180
3181
		return $closed;
3182
	}
3183
3184
	/**
3185
	 * Whether MySQL database is at least the required minimum version.
3186
	 *
3187
	 * @since 2.5.0
3188
	 *
3189
	 * @global string $wp_version
3190
	 * @global string $required_mysql_version
3191
	 *
3192
	 * @return WP_Error|void
3193
	 */
3194
	public function check_database_version() {
3195
		global $wp_version, $required_mysql_version;
3196
		// Make sure the server has the required MySQL version
3197
		if ( version_compare($this->db_version(), $required_mysql_version, '<') )
3198
			return new WP_Error('database_version', sprintf( __( '<strong>ERROR</strong>: WordPress %1$s requires MySQL %2$s or higher' ), $wp_version, $required_mysql_version ));
3199
	}
3200
3201
	/**
3202
	 * Whether the database supports collation.
3203
	 *
3204
	 * Called when WordPress is generating the table scheme.
3205
	 *
3206
	 * Use `wpdb::has_cap( 'collation' )`.
3207
	 *
3208
	 * @since 2.5.0
3209
	 * @deprecated 3.5.0 Use wpdb::has_cap()
3210
	 *
3211
	 * @return bool True if collation is supported, false if version does not
3212
	 */
3213
	public function supports_collation() {
3214
		_deprecated_function( __FUNCTION__, '3.5.0', 'wpdb::has_cap( \'collation\' )' );
3215
		return $this->has_cap( 'collation' );
3216
	}
3217
3218
	/**
3219
	 * The database character collate.
3220
	 *
3221
	 * @since 3.5.0
3222
	 *
3223
	 * @return string The database character collate.
3224
	 */
3225
	public function get_charset_collate() {
3226
		$charset_collate = '';
3227
3228
		if ( ! empty( $this->charset ) )
3229
			$charset_collate = "DEFAULT CHARACTER SET $this->charset";
3230
		if ( ! empty( $this->collate ) )
3231
			$charset_collate .= " COLLATE $this->collate";
3232
3233
		return $charset_collate;
3234
	}
3235
3236
	/**
3237
	 * Determine if a database supports a particular feature.
3238
	 *
3239
	 * @since 2.7.0
3240
	 * @since 4.1.0 Added support for the 'utf8mb4' feature.
3241
	 * @since 4.6.0 Added support for the 'utf8mb4_520' feature.
3242
	 *
3243
	 * @see wpdb::db_version()
3244
	 *
3245
	 * @param string $db_cap The feature to check for. Accepts 'collation',
3246
	 *                       'group_concat', 'subqueries', 'set_charset',
3247
	 *                       or 'utf8mb4'.
3248
	 * @return int|false Whether the database feature is supported, false otherwise.
3249
	 */
3250
	public function has_cap( $db_cap ) {
3251
		$version = $this->db_version();
3252
3253
		switch ( strtolower( $db_cap ) ) {
3254
			case 'collation' :    // @since 2.5.0
3255
			case 'group_concat' : // @since 2.7.0
3256
			case 'subqueries' :   // @since 2.7.0
3257
				return version_compare( $version, '4.1', '>=' );
3258
			case 'set_charset' :
3259
				return version_compare( $version, '5.0.7', '>=' );
3260
			case 'utf8mb4' :      // @since 4.1.0
3261
				if ( version_compare( $version, '5.5.3', '<' ) ) {
3262
					return false;
3263
				}
3264
				if ( $this->use_mysqli ) {
3265
					$client_version = mysqli_get_client_info();
3266
				} else {
3267
					$client_version = mysql_get_client_info();
3268
				}
3269
3270
				/*
3271
				 * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server.
3272
				 * mysqlnd has supported utf8mb4 since 5.0.9.
3273
				 */
3274
				if ( false !== strpos( $client_version, 'mysqlnd' ) ) {
3275
					$client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $client_version );
3276
					return version_compare( $client_version, '5.0.9', '>=' );
3277
				} else {
3278
					return version_compare( $client_version, '5.5.3', '>=' );
3279
				}
3280
			case 'utf8mb4_520' : // @since 4.6.0
3281
				return version_compare( $version, '5.6', '>=' );
3282
		}
3283
3284
		return false;
3285
	}
3286
3287
	/**
3288
	 * Retrieve the name of the function that called wpdb.
3289
	 *
3290
	 * Searches up the list of functions until it reaches
3291
	 * the one that would most logically had called this method.
3292
	 *
3293
	 * @since 2.5.0
3294
	 *
3295
	 * @return string|array The name of the calling function
3296
	 */
3297
	public function get_caller() {
3298
		return wp_debug_backtrace_summary( __CLASS__ );
3299
	}
3300
3301
	/**
3302
	 * Retrieves the MySQL server version.
3303
	 *
3304
	 * @since 2.7.0
3305
	 *
3306
	 * @return null|string Null on failure, version number on success.
3307
	 */
3308
	public function db_version() {
3309
		if ( $this->use_mysqli ) {
3310
			$server_info = mysqli_get_server_info( $this->dbh );
3311
		} else {
3312
			$server_info = mysql_get_server_info( $this->dbh );
3313
		}
3314
		return preg_replace( '/[^0-9.].*/', '', $server_info );
3315
	}
3316
}
3317