├── .gitignore ├── .htaccess ├── inc ├── .htaccess ├── index.php ├── error-handler.php ├── config.php ├── interface-wp-db-driver.php └── db-driver.php ├── drivers ├── .htaccess ├── index.php ├── mysql.php ├── mysqli.php └── pdo_mysql.php ├── wp-content ├── .htaccess ├── index.php └── db.php ├── index.php ├── assets ├── psd │ └── icon.psd ├── icon-128x128.png ├── icon-256x256.png ├── screenshot-1.png ├── banner-1544x500.png └── banner-772x250.png ├── db.php ├── bin ├── changes.diff ├── changes-old.diff └── install-wp-tests.sh ├── .travis.yml ├── readme.txt └── wp-db-driver.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all 2 | -------------------------------------------------------------------------------- /inc/.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all 2 | -------------------------------------------------------------------------------- /drivers/.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all 2 | -------------------------------------------------------------------------------- /wp-content/.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all 2 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inc/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /drivers/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wp-content/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/psd/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markoheijnen/wp-db-driver/HEAD/assets/psd/icon.psd -------------------------------------------------------------------------------- /assets/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markoheijnen/wp-db-driver/HEAD/assets/icon-128x128.png -------------------------------------------------------------------------------- /assets/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markoheijnen/wp-db-driver/HEAD/assets/icon-256x256.png -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markoheijnen/wp-db-driver/HEAD/assets/screenshot-1.png -------------------------------------------------------------------------------- /assets/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markoheijnen/wp-db-driver/HEAD/assets/banner-1544x500.png -------------------------------------------------------------------------------- /assets/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markoheijnen/wp-db-driver/HEAD/assets/banner-772x250.png -------------------------------------------------------------------------------- /db.php: -------------------------------------------------------------------------------- 1 | dbh = $wpdb->dbh; 14 | -------------------------------------------------------------------------------- /wp-content/db.php: -------------------------------------------------------------------------------- 1 | get_var( "SELECT ID FROM $wpdb->users LIMIT 1" ); 7 | $this->assertGreaterThan( 0, $var ); 8 | 9 | - if ( $wpdb->use_mysqli ) { 10 | - mysqli_close( $wpdb->dbh ); 11 | - } else { 12 | - mysql_close( $wpdb->dbh ); 13 | - } 14 | - unset( $wpdb->dbh ); 15 | + $wpdb->close(); 16 | 17 | $var = $wpdb->get_var( "SELECT ID FROM $wpdb->users LIMIT 1" ); 18 | $this->assertGreaterThan( 0, $var ); 19 | Index: tests/phpunit/includes/utils.php 20 | =================================================================== 21 | --- tests/phpunit/includes/utils.php (revision 33328) 22 | +++ tests/phpunit/includes/utils.php (working copy) 23 | @@ -375,7 +375,7 @@ 24 | /** 25 | * Special class for exposing protected wpdb methods we need to access 26 | */ 27 | -class wpdb_exposed_methods_for_testing extends wpdb { 28 | +class wpdb_exposed_methods_for_testing extends wpdb_drivers { 29 | public function __construct() { 30 | global $wpdb; 31 | $this->dbh = $wpdb->dbh; 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | php: 6 | - 5.2 7 | - 5.6 8 | - 7.0 9 | 10 | env: 11 | - WP_VERSION=master WP_MULTISITE=0 WPDB_DRIVER=mysql 12 | - WP_VERSION=4.4 WP_MULTISITE=0 WPDB_DRIVER=mysql 13 | - WP_VERSION=4.3 WP_MULTISITE=0 WPDB_DRIVER=mysql 14 | - WP_VERSION=4.2 WP_MULTISITE=0 WPDB_DRIVER=mysql 15 | - WP_VERSION=4.1 WP_MULTISITE=0 WPDB_DRIVER=mysql 16 | - WP_VERSION=master WP_MULTISITE=0 WPDB_DRIVER=mysqli 17 | - WP_VERSION=4.4 WP_MULTISITE=0 WPDB_DRIVER=mysqli 18 | - WP_VERSION=4.3 WP_MULTISITE=0 WPDB_DRIVER=mysqli 19 | - WP_VERSION=4.2 WP_MULTISITE=0 WPDB_DRIVER=mysqli 20 | - WP_VERSION=4.1 WP_MULTISITE=0 WPDB_DRIVER=mysqli 21 | - WP_VERSION=master WP_MULTISITE=0 WPDB_DRIVER=pdo_mysql 22 | - WP_VERSION=4.4 WP_MULTISITE=0 WPDB_DRIVER=pdo_mysql 23 | - WP_VERSION=4.3 WP_MULTISITE=0 WPDB_DRIVER=pdo_mysql 24 | - WP_VERSION=4.2 WP_MULTISITE=0 WPDB_DRIVER=pdo_mysql 25 | - WP_VERSION=4.1 WP_MULTISITE=0 WPDB_DRIVER=pdo_mysql 26 | 27 | matrix: 28 | allow_failures: 29 | - env: WP_VERSION=4.1 WP_MULTISITE=0 WPDB_DRIVER=mysql 30 | - env: WP_VERSION=4.1 WP_MULTISITE=0 WPDB_DRIVER=mysqli 31 | - env: WP_VERSION=4.1 WP_MULTISITE=0 WPDB_DRIVER=pdo_mysql 32 | 33 | before_script: 34 | - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION $WP_MULTISITE $WPDB_DRIVER 35 | 36 | script: 37 | - cd /tmp/wordpress 38 | - phpunit --group wpdb 39 | -------------------------------------------------------------------------------- /inc/config.php: -------------------------------------------------------------------------------- 1 | $driver_folder . '/pdo_mysql.php', 12 | 'wpdb_driver_mysqli' => $driver_folder . '/mysqli.php', 13 | 'wpdb_driver_mysql' => $driver_folder . '/mysql.php', 14 | ); 15 | 16 | if ( isset( $wp_custom_drivers ) && is_array( $wp_custom_drivers ) ) { 17 | $drivers = $wp_custom_drivers + $drivers; 18 | } 19 | 20 | return $drivers; 21 | } 22 | 23 | /** 24 | * Getting the driver that is the best possible option. 25 | * 26 | * @return string The classname of the driver 27 | */ 28 | public static function get_current_driver() { 29 | $driver = false; 30 | $drivers = self::get_drivers(); 31 | 32 | if ( defined( 'WPDB_DRIVER' ) ) { 33 | $driver = WPDB_DRIVER; 34 | 35 | switch( $driver ) { 36 | case 'pdo_mysql': 37 | $driver = 'wpdb_driver_pdo_mysql'; 38 | break; 39 | case 'mysqli': 40 | $driver = 'wpdb_driver_mysqli'; 41 | break; 42 | case 'mysql': 43 | $driver = 'wpdb_driver_mysql'; 44 | break; 45 | } 46 | 47 | if ( isset( $drivers[ $driver ] ) ) { 48 | include_once $drivers[ $driver ]; 49 | } 50 | 51 | if ( self::class_is_driver_and_supported( $driver ) ) { 52 | return $driver; 53 | } 54 | } 55 | 56 | if ( defined( 'WP_USE_EXT_MYSQL' ) && WP_USE_EXT_MYSQL ) { 57 | $drivers = array( 'wpdb_driver_mysql' => $drivers['wpdb_driver_mysql'] ) + $drivers; 58 | } 59 | 60 | foreach ( $drivers as $class => $file ) { 61 | include_once $file; 62 | 63 | if ( self::class_is_driver_and_supported( $class ) ) { 64 | return $class; 65 | } 66 | } 67 | 68 | return false; 69 | } 70 | 71 | private static function class_is_driver_and_supported( $class ) { 72 | if ( class_exists( $class ) && call_user_func( array( $class, 'is_supported' ) ) ) { 73 | return true; 74 | } 75 | 76 | return false; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /bin/install-wp-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -lt 3 ]; then 4 | echo "usage: $0 [db-host] [wp-version]" 5 | exit 1 6 | fi 7 | 8 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 9 | DIR="$(dirname "$DIR")" 10 | 11 | DB_NAME=$1 12 | DB_USER=$2 13 | DB_PASS=$3 14 | DB_HOST=${4-localhost} 15 | WP_VERSION=${5-master} 16 | WP_MULTISITE=${6-0} 17 | WPDB_DRIVER=${7-mysql} 18 | 19 | WP_CORE_DIR=/tmp/wordpress/ 20 | 21 | set -ex 22 | 23 | install_wp() { 24 | rm -Rf $WP_CORE_DIR 25 | mkdir -p $WP_CORE_DIR 26 | 27 | git clone --depth=50 --branch="$WP_VERSION" git://develop.git.wordpress.org/ $WP_CORE_DIR 28 | 29 | cd $WP_CORE_DIR 30 | rm tests/phpunit/tests/formatting/WpReplaceInHtmlTags.php; 31 | } 32 | 33 | install_test_suite() { 34 | # portable in-place argument for both GNU sed and Mac OSX sed 35 | if [[ $(uname -s) == 'Darwin' ]]; then 36 | local ioption='-i .bak' 37 | else 38 | local ioption='-i' 39 | fi 40 | 41 | # set up testing suite 42 | cd $WP_CORE_DIR 43 | 44 | cp wp-tests-config-sample.php wp-tests-config.php 45 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php 46 | sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php 47 | sed $ioption "s/yourpasswordhere/$DB_PASS/" wp-tests-config.php 48 | sed $ioption "s|localhost|${DB_HOST}|" wp-tests-config.php 49 | 50 | echo "define( 'WPDB_DRIVER', '$WPDB_DRIVER'); " >> wp-tests-config.php 51 | 52 | if [ $WP_VERSION == 'master' ]; then 53 | patch -p0 < "$DIR/bin/changes.diff" 54 | else 55 | patch -p0 < "$DIR/bin/changes-old.diff" 56 | fi 57 | } 58 | 59 | install_db() { 60 | # parse DB_HOST for port or socket references 61 | local PARTS=(${DB_HOST//\:/ }) 62 | local DB_HOSTNAME=${PARTS[0]}; 63 | local DB_SOCK_OR_PORT=${PARTS[1]}; 64 | local EXTRA="" 65 | 66 | if ! [ -z $DB_HOSTNAME ] ; then 67 | if [[ "$DB_SOCK_OR_PORT" =~ ^[0-9]+$ ]] ; then 68 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 69 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then 70 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 71 | elif ! [ -z $DB_HOSTNAME ] ; then 72 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 73 | fi 74 | fi 75 | 76 | # create database 77 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 78 | } 79 | 80 | install_plugin() { 81 | cp -rf $DIR "$WP_CORE_DIR/src/wp-content/plugins/wp-db-driver" 82 | cp "$DIR/wp-content/db.php" "$WP_CORE_DIR/src/wp-content/db.php" 83 | } 84 | 85 | 86 | install_wp 87 | install_test_suite 88 | install_db 89 | install_plugin 90 | -------------------------------------------------------------------------------- /inc/interface-wp-db-driver.php: -------------------------------------------------------------------------------- 1 | db_version(); 63 | 64 | switch ( strtolower( $db_cap ) ) { 65 | case 'collation' : // @since 2.5.0 66 | case 'group_concat' : // @since 2.7.0 67 | case 'subqueries' : // @since 2.7.0 68 | return version_compare( $version, '4.1', '>=' ); 69 | case 'set_charset' : 70 | return version_compare( $version, '5.0.7', '>=' ); 71 | case 'utf8mb4' : // @since 4.1.0 72 | return version_compare( $version, '5.5.3', '>=' ); 73 | } 74 | 75 | return false; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WP DB Driver === 2 | Contributors: markoheijnen, kurtpayne 3 | Donate link: https://markoheijnen.com/donate 4 | Tags: database, backend, pdo, mysqli, mysql 5 | Requires at least: 4.2.0 6 | Tested up to: 4.5.0 7 | Stable tag: 2.1.0 8 | License: GPLv2 or later 9 | 10 | An improved database layer for WordPress 11 | 12 | == Description == 13 | 14 | This plugin adds an improved database layer to WordPress. It allows you to do more then the default one and is always up-to-date with the changes core makes. 15 | 16 | **mysql_* functions** 17 | 18 | The mysql_* functions are officially deprecated for PHP 5.5 and are throwing E_DEPRECATED errors. 19 | On http://core.trac.wordpress.org/ticket/21663 there is discussion on this topic. 20 | 21 | This plugin reflects those discussions. 22 | 23 | **Why should I use this plugin?** 24 | 25 | You should use this plugin if you want to start using PDO / MySQLi for WordPress. 26 | 27 | == Installation == 28 | 29 | 1. Verify that you have PDO or MySQLi 30 | 2. Go to the settings page to install db.php or copy `wp-content/db.php` to your WordPress content directory (`wp-content/` by default. 31 | 3. Done! 32 | 33 | == Screenshots == 34 | 35 | 1. The main settings page reports on what database drivers your PHP installation supports and lets you enable or disable the custom db.php drop-in for this plugin. 36 | 37 | == Frequently Asked Questions == 38 | **Help, I've broken my site!** 39 | 40 | You can visit (replace yoursite.com with your real WordPress blog address) to temporarily disable this plugin. 41 | Then you can login to your admin to deactivate the plugin and restore your site's functionality. 42 | 43 | If you need to uninstall manually, you should remove the plugin folder as well as `wp-content/db.php`. 44 | 45 | **In what order are the drivers picked?** 46 | 47 | PDO > MySQLi > MySQL 48 | 49 | **How do I specify a driver?** 50 | 51 | In your wp-config.php, add a new constant: 52 | 53 | `define( 'WPDB_DRIVER', 'wpdb_driver_pdo_mysql' );` 54 | 55 | You can specify `wpdb_driver_pdo_mysql`, `wpdb_driver_mysqli`, or `wpdb_driver_mysql`. Any other driver will cause an error. 56 | 57 | **Which driver is best for my site?** 58 | 59 | They should all function equally well for WordPress. The MySQL extension is being retired. In PHP 5.5, using this extension issues E_DEPRECATED errors. 60 | In PHP 7.0, it will no longer be available. The two alternative drivers are PDO and MySQLi. 61 | 62 | 63 | 64 | **How to configure SSL?** 65 | You can set defined in your wp-config.php to make it work. This only works for MySQLi and PDO. 66 | These defines are: DB_SSL_KEY, DB_SSL_CERT, DB_SSL_CA, DB_SSL_CA_PATH and DB_SSL_CIPHER. 67 | 68 | In case of a different port number then you can pass this to your database host like: 127.0.0.1: 69 | 70 | For more information see: 71 | - http://dev.mysql.com/doc/refman/5.5/en/ssl-connections.html 72 | 73 | == Upgrade Notice == 74 | 75 | Added emergency override 76 | 77 | == Changelog == 78 | 79 | = 2.1.1 = 80 | * Change global $custom_drivers to $wp_custom_drivers 81 | 82 | = 2.1.0 (2015-12-27) = 83 | * Sync with 4.4 ( Changeset 35787) 84 | * Changed is_mysql logic. 85 | * Change constant WPDB_DRIVERS to a global $custom_drivers 86 | 87 | = 2.0.1 (2015-07-25) = 88 | * Reupload code from GitHub 89 | 90 | = 2.0 (2015-07-25) = 91 | * Sync with 4.2.3 ( Changeset 33310) 92 | * Increased minimal WordPress version to 4.2 93 | * Extending wpdb back again 94 | * Add ability to extend it with more drivers through the constant 'WPDB_DRIVERS' 95 | * Fully compatible with the unit tests of WordPress except HHVM PDO 96 | 97 | = 1.9.3 (2015-05-07) = 98 | * Sync with 4.2.2 99 | 100 | = 1.9.2 (2015-04-27) = 101 | * Sync with 4.2.1 102 | 103 | = 1.9.1 (2015-04-23) = 104 | * Fix setting charset and SQL mode for PDO 105 | 106 | = 1.9 (2015-04-23) = 107 | * Sync to changeset 32261 108 | 109 | = 1.8.1 (2014-08-08) = 110 | * Fix setting charset and SQL mode for PDO 111 | 112 | = 1.8 (2014-08-07) = 113 | * Synced with trunk to Changeset 29165 excluding 27075 114 | * Ensure compatibility with MySQL 5.6 which has stricter SQL modes by default 115 | * Throw an incorrect usage notice when the query argument of wpdb::prepare() does not include a placeholder. 116 | * When the MySQL server has "gone away," attempt to reconnect and retry the query. 117 | * Don't extend wpdb anymore to be on the safe side 118 | * Works with socket connections 119 | * More abstraction from the main db class to our interface. 120 | * Added a banner image for WordPress.org. Thanks to Marcel van der Horst 121 | 122 | = 1.7 (2014-01-30) = 123 | * Synced with trunk to Changeset 25703 124 | * Works when plugins folder has been changed 125 | * Added network support 126 | * Security enhanchement when using a network installation 127 | * Updated readme 128 | 129 | = 1.6 (2013-09-18) = 130 | * Fix returning incorrect number of rows for some queries. Props markmont 131 | * Add error_handler 132 | * Trowing doing_it_wrong message for all mysql_* functions 133 | 134 | = 1.5 (2013-08-04) = 135 | * Fix dbDelta() to create tables when the tables do not exists 136 | * Fix fatal error when database can't get selected by PDO 137 | * Fix notices when using MySQLi query() when $this->result isn't an object 138 | * When database can't get selected show the default message instead of installation screen 139 | 140 | = 1.4 (2013-08-02) = 141 | * Fix notices due changes in WordPress 3.6. 142 | * Add SSL support. Props hypertextranch. 143 | 144 | = 1.3 (2013-07-09) = 145 | * Show install button when db.php is different. 146 | * Don't show remove button when mysql extension isn't installed. 147 | * Compatibility fixes for unit tests. 148 | 149 | = 1.2 (2013-06-30) = 150 | * Added emergency override. 151 | * Updated readme. 152 | 153 | = 1.1 ( 2013-06-28 ) = 154 | * Fixes for MySQLi driver, PDO driver. 155 | * Uses WP_Filesystem for writing / removing db.php when possible. 156 | * Added deactivate / uninstall code. 157 | 158 | = 1.0 ( 2013-06-28 ) = 159 | * First version that supports PDO and MySQLi. Props kurtpayne and scribu. -------------------------------------------------------------------------------- /drivers/mysql.php: -------------------------------------------------------------------------------- 1 | dbh ); 52 | } 53 | 54 | /** 55 | * Get the latest error message from the DB driver 56 | * 57 | * @return string 58 | */ 59 | public function get_error_message() { 60 | return mysql_error( $this->dbh ); 61 | } 62 | 63 | /** 64 | * Free memory associated with the resultset 65 | * 66 | * @return void 67 | */ 68 | public function flush() { 69 | if ( is_resource( $this->result ) ) { 70 | mysql_free_result( $this->result ); 71 | } 72 | 73 | $this->result = null; 74 | $this->col_info = null; 75 | } 76 | 77 | /** 78 | * Check if server is still connected 79 | * @return bool 80 | */ 81 | public function is_connected() { 82 | if ( ! $this->dbh || 2006 == mysql_errno( $this->dbh ) ) { 83 | return false; 84 | } 85 | 86 | return true; 87 | } 88 | 89 | /** 90 | * Connect to database 91 | * @return bool 92 | */ 93 | public function connect( $host, $user, $pass, $port = 3306, $options = array() ) { 94 | $new_link = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true; 95 | $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0; 96 | $this->dbh = mysql_connect( "$host:$port", $user, $pass, $new_link, $client_flags ); 97 | 98 | return ( false !== $this->dbh ); 99 | } 100 | 101 | /** 102 | * Closes the current database connection. 103 | * 104 | * @since 4.5.0 105 | * @access public 106 | * 107 | * @return bool True if the connection was successfully closed, false if it wasn't, 108 | * or the connection doesn't exist. 109 | */ 110 | public function close() { 111 | if ( ! $this->dbh ) { 112 | return false; 113 | } 114 | 115 | $closed = mysql_close( $this->dbh ); 116 | 117 | if ( $closed ) { 118 | $this->dbh = null; 119 | } 120 | 121 | return $closed; 122 | } 123 | 124 | /** 125 | * Ping a server connection or reconnect if there is no connection 126 | * @return bool 127 | */ 128 | public function ping() { 129 | return mysql_ping( $this->dbh ); 130 | } 131 | 132 | /** 133 | * Sets the connection's character set. 134 | * 135 | * @param resource $dbh The resource given by the driver 136 | * @param string $charset Optional. The character set. Default null. 137 | * @param string $collate Optional. The collation. Default null. 138 | */ 139 | public function set_charset( $charset = null, $collate = null ) { 140 | if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) { 141 | if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset' ) ) { 142 | mysql_set_charset( $charset, $this->dbh ); 143 | 144 | return true; 145 | } 146 | } 147 | 148 | return false; 149 | } 150 | 151 | /** 152 | * Get the name of the current character set. 153 | * 154 | * @return string Returns the name of the character set 155 | */ 156 | public function connection_charset() { 157 | return mysql_client_encoding( $this->dbh ); 158 | } 159 | 160 | /** 161 | * Select database 162 | * @return void 163 | */ 164 | public function select( $db ) { 165 | return mysql_select_db( $db, $this->dbh ); 166 | } 167 | 168 | /** 169 | * Perform a MySQL database query, using current database connection. 170 | * @param string $query Database query 171 | * @return int|false Number of rows affected/selected or false on error 172 | */ 173 | public function query( $query ) { 174 | $return_val = 0; 175 | $this->result = mysql_query( $query, $this->dbh ); 176 | 177 | if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) { 178 | $return_val = $this->result; 179 | } 180 | elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) { 181 | $return_val = $this->affected_rows(); 182 | } 183 | elseif ( preg_match( '/^\s*select\s/i', $query ) ) { 184 | return is_resource( $this->result ) ? mysql_num_rows( $this->result ) : false; 185 | } 186 | 187 | return $return_val; 188 | } 189 | 190 | /** 191 | * Get result data. 192 | * @param int The row number from the result that's being retrieved. Row numbers start at 0. 193 | * @param int The offset of the field being retrieved. 194 | * @return array|false The contents of one cell from a MySQL result set on success, or false on failure. 195 | */ 196 | public function query_result( $row, $field = 0 ) { 197 | return mysql_result( $this->result, $row, $field ); 198 | } 199 | 200 | /** 201 | * Get number of rows affected 202 | * @return int 203 | */ 204 | public function affected_rows() { 205 | return mysql_affected_rows( $this->dbh ); 206 | } 207 | 208 | /** 209 | * Get last insert id 210 | * @return int 211 | */ 212 | public function insert_id() { 213 | return mysql_insert_id( $this->dbh ); 214 | } 215 | 216 | /** 217 | * Get results 218 | * @return array 219 | */ 220 | public function get_results() { 221 | $ret = array(); 222 | while ( $row = mysql_fetch_object( $this->result ) ) { 223 | $ret[] = $row; 224 | } 225 | return $ret; 226 | } 227 | 228 | /** 229 | * Load the column metadata from the last query. 230 | * @return array 231 | */ 232 | public function load_col_info() { 233 | if ( $this->col_info ) { 234 | return $this->col_info; 235 | } 236 | 237 | $num_fields = mysql_num_fields( $this->result ); 238 | 239 | for ( $i = 0; $i < $num_fields; $i++ ) { 240 | $this->col_info[ $i ] = mysql_fetch_field( $this->result, $i ); 241 | } 242 | 243 | return $this->col_info; 244 | } 245 | 246 | /** 247 | * Retrieves the MySQL server version. 248 | * @return false|string false on failure, version number on success 249 | */ 250 | public function db_version() { 251 | return preg_replace( '/[^0-9.].*/', '', mysql_get_server_info( $this->dbh ) ); 252 | } 253 | 254 | 255 | /** 256 | * Determine if a database supports a particular feature. 257 | */ 258 | public function has_cap( $db_cap ) { 259 | $db_cap = strtolower( $db_cap ); 260 | 261 | $version = parent::has_cap( $db_cap ); 262 | 263 | if ( $version && 'utf8mb4' === $db_cap ) { 264 | return version_compare( mysql_get_client_info(), '5.5.3', '>=' ); 265 | } 266 | 267 | return $version; 268 | } 269 | 270 | } 271 | -------------------------------------------------------------------------------- /wp-db-driver.php: -------------------------------------------------------------------------------- 1 | delete( $wp_filesystem->wp_content_dir() . '/db.php' ); 70 | } 71 | } 72 | } 73 | 74 | public function add_page() { 75 | if ( is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) { 76 | add_submenu_page( 77 | 'settings.php', 78 | __( 'WP DB Driver', 'wp-db-driver' ), 79 | __( 'WP DB Driver', 'wp-db-driver' ), 80 | 'manage_options', 81 | 'wp-db-driver', 82 | array( $this, 'page_overview' ) 83 | ); 84 | } 85 | else { 86 | add_management_page( 87 | __( 'WP DB Driver', 'wp-db-driver' ), 88 | __( 'WP DB Driver', 'wp-db-driver' ), 89 | 'manage_options', 90 | 'wp-db-driver', 91 | array( $this, 'page_overview' ) 92 | ); 93 | } 94 | } 95 | 96 | public function page_overview() { 97 | if ( ! current_user_can( 'manage_options' ) ) { 98 | wp_die( __( 'You do not have sufficient permissions to access this page.' ) ); 99 | } 100 | 101 | // Load if our custom wpdb wasn't loaded yet. 102 | require_once dirname( __FILE__ ) . '/inc/config.php'; 103 | require_once dirname( __FILE__ ) . '/inc/interface-wp-db-driver.php'; 104 | 105 | echo '
'; 106 | 107 | screen_icon('options-general'); 108 | echo '

' . esc_html( get_admin_page_title() ) . '

'; 109 | 110 | // Don't force a specific file system method 111 | $method = ''; 112 | 113 | // Define any extra pass-thru fields (none) 114 | $form_fields = array(); 115 | 116 | // Define the URL to post back to (this one) 117 | $url = $_SERVER['REQUEST_URI']; 118 | 119 | // Install flags 120 | $do_install = ( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'install-db-nonce' ) ); 121 | $do_uninstall = ( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'uninstall-db-nonce' ) ); 122 | 123 | if ( is_super_admin() && ( $do_install || $do_uninstall ) ) { 124 | 125 | // Ask for credentials, if necessary 126 | if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, $form_fields ) ) ) { 127 | 128 | return true; 129 | } elseif ( ! WP_Filesystem($creds) ) { 130 | 131 | // The credentials are bad, ask again 132 | request_filesystem_credentials( $url, $method, true, false, $form_fields ); 133 | return true; 134 | } else { 135 | // Once we get here, we should have credentials, do the file system operations 136 | global $wp_filesystem; 137 | 138 | // Install 139 | if ( $do_install ) { 140 | if ( $wp_filesystem->put_contents( $wp_filesystem->wp_content_dir() . '/db.php' , file_get_contents( dirname( __FILE__ ) .'/wp-content/db.php' ), FS_CHMOD_FILE ) ) { 141 | echo '

' . __( 'db.php has been installed.', 'wp-db-driver' ) .'

'; 142 | } else { 143 | echo '

' . __( "db.php couldn't be installed. Please try is manually", 'wp-db-driver' ) .'

'; 144 | } 145 | 146 | // Remove 147 | } elseif ( $do_uninstall ) { 148 | if ( $wp_filesystem->delete( $wp_filesystem->wp_content_dir() . '/db.php' ) ) { 149 | echo '

' . __( 'db.php has been removed.', 'wp-db-driver' ) .'

'; 150 | } else { 151 | echo '

' . __( "db.php couldn't be removed. Please try is manually", 'wp-db-driver' ) .'

'; 152 | } 153 | 154 | } 155 | } 156 | } 157 | 158 | echo '

' . __( 'Current driver', 'wp-db-driver' ) . '

'; 159 | 160 | if ( file_exists( WP_CONTENT_DIR . '/db.php' ) ) { 161 | $crc1 = md5_file( dirname( __FILE__ ) . '/wp-content/db.php' ); 162 | $crc2 = md5_file( WP_CONTENT_DIR . '/db.php' ); 163 | 164 | if ( $crc1 === $crc2 ) { 165 | echo '
'; 166 | wp_nonce_field('uninstall-db-nonce'); 167 | 168 | echo '

' . call_user_func( array( WP_DB_Driver_Config::get_current_driver(), 'get_name' ) ) . '   '; 169 | 170 | if ( function_exists( 'mysql' ) && is_super_admin() ) { 171 | submit_button( __( 'Remove', 'wp-db-driver' ), 'primary', 'install-db-php', false ); 172 | } 173 | 174 | echo '

'; 175 | 176 | echo '
'; 177 | 178 | } else { 179 | echo '
'; 180 | wp_nonce_field('install-db-nonce'); 181 | 182 | echo '

' . __( 'Another db.php is installed', 'wp-db-driver' ) . '   '; 183 | 184 | if ( is_super_admin() ) { 185 | submit_button( __( 'Install', 'wp-db-driver' ), 'primary', 'install-db-php', false ); 186 | } 187 | 188 | echo '

'; 189 | 190 | echo '
'; 191 | } 192 | } 193 | else { 194 | echo '
'; 195 | wp_nonce_field('install-db-nonce'); 196 | 197 | echo '

' . __( 'No custom db.php installed', 'wp-db-driver' ) . '   '; 198 | 199 | if ( is_super_admin() ) { 200 | submit_button( __( 'Install', 'wp-db-driver' ), 'primary', 'install-db-php', false ); 201 | } 202 | 203 | echo '

'; 204 | 205 | echo '
'; 206 | } 207 | 208 | echo '

' . __( 'Supported drivers', 'wp-db-driver' ) . '

'; 209 | 210 | echo ''; 211 | 212 | $drivers = WP_DB_Driver_Config::get_drivers(); 213 | foreach ( $drivers as $driver => $file ) { 214 | include_once $file; 215 | 216 | echo ''; 217 | echo ''; 218 | echo ''; 228 | echo ''; 229 | } 230 | 231 | echo '
' . call_user_func( array( $driver, 'get_name' ) ) . ''; 219 | 220 | if ( call_user_func( array( $driver, 'is_supported' ) ) ) { 221 | _e( 'Installed', 'wp-db-driver' ); 222 | } 223 | else { 224 | _e( 'Not installed', 'wp-db-driver' ); 225 | } 226 | 227 | echo '
'; 232 | } 233 | 234 | } 235 | 236 | if ( is_admin() ) { 237 | $GLOBALS['wp_db_driver_plugin'] = new WP_DB_Driver_Plugin; 238 | } 239 | 240 | register_deactivation_hook( __FILE__, array( 'WP_DB_Driver_Plugin', 'deactivate' ) ); 241 | register_uninstall_hook( __FILE__, array( 'WP_DB_Driver_Plugin', 'uninstall' ) ); -------------------------------------------------------------------------------- /drivers/mysqli.php: -------------------------------------------------------------------------------- 1 | dbh->real_escape_string( $string ); 52 | } 53 | 54 | /** 55 | * Get the latest error message from the DB driver 56 | * 57 | * @return string 58 | */ 59 | public function get_error_message() { 60 | return $this->dbh->error; 61 | } 62 | 63 | /** 64 | * Free memory associated with the resultset 65 | * 66 | * @return void 67 | */ 68 | public function flush() { 69 | if ( $this->result instanceof mysqli_stmt ) { 70 | $this->result->free_result(); 71 | } 72 | 73 | $this->result = null; 74 | $this->col_info = null; 75 | 76 | // Sanity check before using the handle 77 | if ( empty( $this->dbh ) || ! ( $this->dbh instanceof mysqli ) ) { 78 | return; 79 | } 80 | 81 | // Clear out any results from a multi-query 82 | while ( mysqli_more_results( $this->dbh ) ) { 83 | mysqli_next_result( $this->dbh ); 84 | } 85 | } 86 | 87 | /** 88 | * Check if server is still connected 89 | * @return bool 90 | */ 91 | public function is_connected() { 92 | if ( ! $this->dbh || 2006 == $this->dbh->connect_errno ) { 93 | return false; 94 | } 95 | 96 | return true; 97 | } 98 | 99 | /** 100 | * Connect to database 101 | * @return bool 102 | */ 103 | public function connect( $host, $user, $pass, $port = 3306, $options = array() ) { 104 | $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0; 105 | 106 | $this->dbh = mysqli_init(); 107 | 108 | $socket = null; 109 | $port_or_socket = strstr( $host, ':' ); 110 | 111 | if ( ! empty( $port_or_socket ) ) { 112 | $host = substr( $host, 0, strpos( $host, ':' ) ); 113 | $port_or_socket = substr( $port_or_socket, 1 ); 114 | 115 | if ( 0 !== strpos( $port_or_socket, '/' ) ) { 116 | $port = intval( $port_or_socket ); 117 | $maybe_socket = strstr( $port_or_socket, ':' ); 118 | 119 | if ( ! empty( $maybe_socket ) ) { 120 | $socket = substr( $maybe_socket, 1 ); 121 | } 122 | } else { 123 | $socket = $port_or_socket; 124 | $port = null; 125 | } 126 | } 127 | 128 | $this->dbh->real_connect( $host, $user, $pass, null, $port, $socket, $client_flags ); 129 | 130 | if ( ! empty( $options['key'] ) && ! empty( $options['cert'] ) && ! empty( $options['ca'] ) ) { 131 | $this->dbh->ssl_set( 132 | $options['key'], 133 | $options['cert'], 134 | $options['ca'], 135 | $options['ca_path'], 136 | $options['cipher'] 137 | ); 138 | } 139 | 140 | return ( ! mysqli_connect_error() ); 141 | } 142 | 143 | /** 144 | * Closes the current database connection. 145 | * 146 | * @since 4.5.0 147 | * @access public 148 | * 149 | * @return bool True if the connection was successfully closed, false if it wasn't, 150 | * or the connection doesn't exist. 151 | */ 152 | public function close() { 153 | if ( ! $this->dbh ) { 154 | return false; 155 | } 156 | 157 | $closed = $this->dbh->close(); 158 | 159 | if ( $closed ) { 160 | $this->dbh = null; 161 | } 162 | 163 | return $closed; 164 | } 165 | 166 | /** 167 | * Ping a server connection or reconnect if there is no connection 168 | * @return bool 169 | */ 170 | public function ping() { 171 | if ( ! $this->dbh ) { 172 | return false; 173 | } 174 | 175 | return $this->dbh->ping(); 176 | } 177 | 178 | /** 179 | * Sets the connection's character set. 180 | * 181 | * @param resource $dbh The resource given by the driver 182 | * @param string $charset Optional. The character set. Default null. 183 | * @param string $collate Optional. The collation. Default null. 184 | */ 185 | public function set_charset( $charset = null, $collate = null ) { 186 | if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) { 187 | if ( function_exists( 'mysqli_set_charset' ) && $this->has_cap( 'set_charset' ) ) { 188 | mysqli_set_charset( $this->dbh, $charset ); 189 | 190 | return true; 191 | } 192 | } 193 | 194 | return false; 195 | } 196 | 197 | /** 198 | * Get the name of the current character set. 199 | * 200 | * @return string Returns the name of the character set 201 | */ 202 | public function connection_charset() { 203 | return mysqli_character_set_name( $this->dbh ); 204 | } 205 | 206 | /** 207 | * Select database 208 | * @return void 209 | */ 210 | public function select( $db ) { 211 | return $this->dbh->select_db( $db ); 212 | } 213 | 214 | /** 215 | * Perform a MySQL database query, using current database connection. 216 | * @param string $query Database query 217 | * @return int|false Number of rows affected/selected or false on error 218 | */ 219 | public function query( $query ) { 220 | $return_val = 0; 221 | 222 | if ( ! $this->dbh ) { 223 | return false; 224 | } 225 | 226 | $this->result = $this->dbh->query( $query ); 227 | 228 | if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) { 229 | $return_val = $this->result; 230 | } 231 | elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) { 232 | $return_val = $this->affected_rows(); 233 | } 234 | elseif ( preg_match( '/^\s*select\s/i', $query ) ) { 235 | return is_object( $this->result ) ? $this->result->num_rows : false; 236 | } 237 | 238 | return $return_val; 239 | } 240 | 241 | /** 242 | * Get result data. 243 | * @param int The row number from the result that's being retrieved. Row numbers start at 0. 244 | * @param int The offset of the field being retrieved. 245 | * @return array|false The contents of one cell from a MySQL result set on success, or false on failure. 246 | */ 247 | public function query_result( $row, $field = 0 ) { 248 | if( 0 == $this->result->num_rows ) { 249 | return false; 250 | } 251 | 252 | $this->result->data_seek( $row ); 253 | $datarow = $this->result->fetch_array(); 254 | 255 | return $datarow[ $field ]; 256 | } 257 | 258 | /** 259 | * Get number of rows affected 260 | * @return int 261 | */ 262 | public function affected_rows() { 263 | return $this->dbh->affected_rows; 264 | } 265 | 266 | /** 267 | * Get last insert id 268 | * @return int 269 | */ 270 | public function insert_id() { 271 | return $this->dbh->insert_id; 272 | } 273 | 274 | /** 275 | * Get results 276 | * @return array 277 | */ 278 | public function get_results() { 279 | $ret = array(); 280 | 281 | if( is_object( $this->result ) ) { 282 | while ( $row = $this->result->fetch_object() ) { 283 | $ret[] = $row; 284 | } 285 | } 286 | 287 | return $ret; 288 | } 289 | 290 | /** 291 | * Load the column metadata from the last query. 292 | * @return array 293 | */ 294 | public function load_col_info() { 295 | if ( $this->col_info ) { 296 | return $this->col_info; 297 | } 298 | 299 | $num_fields = $this->result->field_count; 300 | 301 | for ( $i = 0; $i < $num_fields; $i++ ) { 302 | $this->col_info[ $i ] = $this->result->fetch_field_direct( $i ); 303 | } 304 | 305 | return $this->col_info; 306 | } 307 | 308 | /** 309 | * Retrieves the MySQL server version. 310 | * @return false|string false on failure, version number on success 311 | */ 312 | public function db_version() { 313 | return preg_replace( '/[^0-9.].*/', '', $this->dbh->server_version ); 314 | } 315 | 316 | 317 | /** 318 | * Determine if a database supports a particular feature. 319 | */ 320 | public function has_cap( $db_cap ) { 321 | $db_cap = strtolower( $db_cap ); 322 | 323 | $version = parent::has_cap( $db_cap ); 324 | 325 | if ( $version && 'utf8mb4' === $db_cap ) { 326 | return version_compare( mysqli_get_client_info(), '5.5.3', '>=' ); 327 | } 328 | 329 | return $version; 330 | } 331 | 332 | } 333 | -------------------------------------------------------------------------------- /drivers/pdo_mysql.php: -------------------------------------------------------------------------------- 1 | dbh->quote( $string ), 1, -1 ); 60 | } 61 | 62 | /** 63 | * Get the latest error message from the DB driver 64 | * 65 | * @return string 66 | */ 67 | public function get_error_message() { 68 | $error = $this->dbh->errorInfo(); 69 | if ( isset( $error[2] ) ) { 70 | return $error[2]; 71 | } 72 | return ''; 73 | } 74 | 75 | /** 76 | * Free memory associated with the resultset 77 | * 78 | * @return void 79 | */ 80 | public function flush() { 81 | if ( $this->result instanceof PDOStatement ) { 82 | $this->result->closeCursor(); 83 | } 84 | $this->result = null; 85 | $this->col_info = null; 86 | $this->fetched_rows = array(); 87 | } 88 | 89 | /** 90 | * Check if server is still connected 91 | * @return bool 92 | */ 93 | public function is_connected() { 94 | if ( ! $this->dbh || 2006 == $this->dbh->errorCode() ) { 95 | return false; 96 | } 97 | 98 | return true; 99 | } 100 | 101 | /** 102 | * Connect to database 103 | * @return bool 104 | */ 105 | public function connect( $host, $user, $pass, $port = 3306, $options = array() ) { 106 | if( '.sock' === substr( $port, -5 ) ) { 107 | $dsn = sprintf( 'mysql:host=%1$s;unix_socket=%2$s', $host, $port ); 108 | } 109 | else { 110 | $dsn = sprintf( 'mysql:host=%1$s;port=%2$d', $host, $port ); 111 | } 112 | 113 | try { 114 | $pdo_options = array(); 115 | 116 | if ( ! empty( $options['key'] ) && ! empty( $options['cert'] ) && ! empty( $options['ca'] ) ) { 117 | $pdo_options[ PDO::MYSQL_ATTR_SSL_KEY ] = $options['key']; 118 | $pdo_options[ PDO::MYSQL_ATTR_SSL_CERT ] = $options['cert']; 119 | $pdo_options[ PDO::MYSQL_ATTR_SSL_CA ] = $options['ca']; 120 | $pdo_options[ PDO::MYSQL_ATTR_SSL_CAPATH ] = $options['ca_path']; 121 | $pdo_options[ PDO::MYSQL_ATTR_SSL_CIPHER ] = $options['cipher']; 122 | 123 | // Cleanup empty values 124 | $pdo_options = array_filter( $pdo_options ); 125 | } 126 | 127 | $this->dbh = new PDO( $dsn, $user, $pass, $pdo_options ); 128 | $this->dbh->setAttribute ( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); 129 | } 130 | catch ( Exception $e ) { 131 | return false; 132 | } 133 | 134 | return true; 135 | } 136 | 137 | /** 138 | * Closes the current database connection. 139 | * 140 | * @since 4.5.0 141 | * @access public 142 | * 143 | * @return bool True if the connection was successfully closed, false if it wasn't, 144 | * or the connection doesn't exist. 145 | */ 146 | public function close() { 147 | if ( ! $this->dbh ) { 148 | return false; 149 | } 150 | 151 | $this->dbh = null; 152 | 153 | return true; 154 | } 155 | 156 | /** 157 | * Ping a server connection or reconnect if there is no connection 158 | * @return bool 159 | */ 160 | public function ping() { 161 | return (bool) $this->query('SELECT 1'); 162 | } 163 | 164 | /** 165 | * Sets the connection's character set. 166 | * 167 | * @param resource $dbh The resource given by the driver 168 | * @param string $charset Optional. The character set. Default null. 169 | * @param string $collate Optional. The collation. Default null. 170 | */ 171 | public function set_charset( $charset = null, $collate = null ) { 172 | if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) { 173 | if ( $this->has_cap( 'set_charset' ) ) { 174 | $this->dbh->exec( "set names " . $charset ); 175 | 176 | return true; 177 | } 178 | } 179 | 180 | return false; 181 | } 182 | 183 | /** 184 | * Get the name of the current character set. 185 | * 186 | * @return string Returns the name of the character set 187 | */ 188 | public function connection_charset() { 189 | $result = $this->dbh->query("SHOW VARIABLES LIKE 'character_set_connection'"); 190 | return $result->fetchColumn(1); 191 | } 192 | 193 | /** 194 | * Select database 195 | * @return void 196 | */ 197 | public function select( $db ) { 198 | try { 199 | $this->dbh->exec( sprintf( 'USE `%s`', $db ) ); 200 | } catch ( Exception $e ) { 201 | return false; 202 | } 203 | 204 | return true; 205 | } 206 | 207 | /** 208 | * Perform a MySQL database query, using current database connection. 209 | * @param string $query Database query 210 | * @return int|false Number of rows affected/selected or false on error 211 | */ 212 | public function query( $query ) { 213 | $return_val = 0; 214 | 215 | if ( ! $this->dbh ) { 216 | return false; 217 | } 218 | 219 | try { 220 | $this->result = $this->dbh->query( $query ); 221 | } 222 | catch ( Exception $e ) { 223 | if ( WP_DEBUG) { 224 | global $wpdb; 225 | error_log( "Error executing query: " . $e->getCode() . " - " . $e->getMessage() . " in query " . $query ); 226 | } 227 | return false; 228 | } 229 | 230 | if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) { 231 | $return_val = $this->result; 232 | } 233 | elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) { 234 | $return_val = $this->affected_rows(); 235 | } 236 | elseif ( preg_match( '/^\s*select\s/i', $query ) ) { 237 | $this->get_results(); 238 | return count( $this->fetched_rows ); 239 | } 240 | 241 | return $return_val; 242 | } 243 | 244 | /** 245 | * Get result data. 246 | * @param int The row number from the result that's being retrieved. Row numbers start at 0. 247 | * @param int The offset of the field being retrieved. 248 | * @return array|false The contents of one cell from a MySQL result set on success, or false on failure. 249 | */ 250 | public function query_result( $row, $field = 0 ) { 251 | if( $row > 1 ) { 252 | $this->result->fetch( PDO::FETCH_ASSOC,PDO::FETCH_ORI_NEXT, $row ); 253 | } 254 | 255 | return $this->result->fetchColumn( $field ); 256 | } 257 | 258 | /** 259 | * Get number of rows affected 260 | * @return int 261 | */ 262 | public function affected_rows() { 263 | if ( $this->result instanceof PDOStatement ) { 264 | return $this->result->rowCount(); 265 | } 266 | return 0; 267 | } 268 | 269 | /** 270 | * Get last insert id 271 | * @return int 272 | */ 273 | public function insert_id() { 274 | return $this->dbh->lastInsertId(); 275 | } 276 | 277 | /** 278 | * Get results 279 | * @return array 280 | */ 281 | public function get_results() { 282 | if ( !empty( $this->fetched_rows ) ) { 283 | return $this->fetched_rows; 284 | } 285 | $this->fetched_rows = array(); 286 | 287 | if ( !empty( $this->result ) && $this->result->rowCount() > 0 ) { 288 | try { 289 | while ( $row = $this->result->fetchObject() ) { 290 | $this->fetched_rows[] = $row; 291 | } 292 | } catch ( Exception $e ) { 293 | } 294 | } 295 | 296 | return $this->fetched_rows; 297 | } 298 | 299 | /** 300 | * Load the column metadata from the last query. 301 | * @return array 302 | */ 303 | public function load_col_info() { 304 | if ( $this->col_info ) { 305 | return $this->col_info; 306 | } 307 | 308 | $num_fields = $this->result->columnCount(); 309 | 310 | for ( $i = 0; $i < $num_fields; $i++ ) { 311 | $this->col_info[ $i ] = (object) $this->result->getColumnMeta( $i ); 312 | } 313 | 314 | return $this->col_info; 315 | } 316 | 317 | /** 318 | * Retrieves the MySQL server version. 319 | * @return false|string false on failure, version number on success 320 | */ 321 | public function db_version() { 322 | return preg_replace( '/[^0-9.].*/', '', $this->dbh->getAttribute( PDO::ATTR_SERVER_VERSION ) ); 323 | } 324 | 325 | 326 | /** 327 | * Determine if a database supports a particular feature. 328 | */ 329 | public function has_cap( $db_cap ) { 330 | $db_cap = strtolower( $db_cap ); 331 | 332 | $version = parent::has_cap( $db_cap ); 333 | 334 | if ( $version && 'utf8mb4' === $db_cap ) { 335 | return version_compare( $this->dbh->getAttribute( PDO::ATTR_CLIENT_VERSION ), '5.5.3', '>=' ); 336 | } 337 | 338 | return $version; 339 | } 340 | 341 | 342 | /** 343 | * Don't save any state. The db wrapper should call connect() again. 344 | * @return array 345 | */ 346 | public function __sleep() { 347 | return array(); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /inc/db-driver.php: -------------------------------------------------------------------------------- 1 | '%d' 439 | * 440 | * @since 2.8.0 441 | * @see wpdb::prepare() 442 | * @see wpdb::insert() 443 | * @see wpdb::update() 444 | * @see wpdb::delete() 445 | * @see wp_set_wpdb_vars() 446 | * @access public 447 | * @var array 448 | */ 449 | public $field_types = array(); 450 | 451 | /** 452 | * Database table columns charset 453 | * 454 | * @since 2.2.0 455 | * @access public 456 | * @var string 457 | */ 458 | public $charset; 459 | 460 | /** 461 | * Database table columns collate 462 | * 463 | * @since 2.2.0 464 | * @access public 465 | * @var string 466 | */ 467 | public $collate; 468 | 469 | /** 470 | * Database Username 471 | * 472 | * @since 2.9.0 473 | * @access protected 474 | * @var string 475 | */ 476 | protected $dbuser; 477 | 478 | /** 479 | * Database Password 480 | * 481 | * @since 3.1.0 482 | * @access protected 483 | * @var string 484 | */ 485 | protected $dbpassword; 486 | 487 | /** 488 | * Database Name 489 | * 490 | * @since 3.1.0 491 | * @access protected 492 | * @var string 493 | */ 494 | protected $dbname; 495 | 496 | /** 497 | * Database Host 498 | * 499 | * @since 3.1.0 500 | * @access protected 501 | * @var string 502 | */ 503 | protected $dbhost; 504 | 505 | /** 506 | * Database driver object 507 | * 508 | * Allow for simple, and backwards compatible, pluggable database drivers 509 | * like PDO_mysql and mysqli to power wpdb 510 | * 511 | * @access protected 512 | * @since 0.71 513 | * @var wpdb_driver 514 | */ 515 | protected $dbh; 516 | 517 | /** 518 | * A textual description of the last query/get_row/get_var call 519 | * 520 | * @since 3.0.0 521 | * @access public 522 | * @var string 523 | */ 524 | public $func_call; 525 | 526 | /** 527 | * Whether MySQL is used as the database engine. 528 | * 529 | * Set in WPDB::db_connect(). This is used when checking against the required 530 | * MySQL version for WordPress. Normally, a replacement database drop-in (db.php) 531 | * will skip these checks, but setting this to true will force the checks to occur. 532 | * 533 | * @since 3.3.0 534 | * @access public 535 | * @var bool 536 | */ 537 | public $is_mysql = true; 538 | 539 | /** 540 | * A list of incompatible SQL modes. 541 | * 542 | * @since 3.9.0 543 | * @access protected 544 | * @var array 545 | */ 546 | protected $incompatible_modes = array( 'NO_ZERO_DATE', 'ONLY_FULL_GROUP_BY', 547 | 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'TRADITIONAL' ); 548 | 549 | /** 550 | * Whether we've managed to successfully connect at some point 551 | * 552 | * @since 3.9.0 553 | * @access private 554 | * @var bool 555 | */ 556 | private $has_connected = false; 557 | 558 | /** 559 | * Pick the adapter to be used for performing the actual queries. 560 | * 561 | * @since 3.6.0 562 | */ 563 | private function set_driver() { 564 | $driver = WP_DB_Driver_Config::get_current_driver(); 565 | 566 | if ( ! $driver ) { 567 | 568 | wp_load_translations_early(); 569 | 570 | // Load custom DB error template, if present. 571 | if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) { 572 | require_once( WP_CONTENT_DIR . '/db-error.php' ); 573 | die(); 574 | } 575 | 576 | $this->bail( __( " 577 |

No database drivers found

. 578 |

WordPress requires the mysql, mysqli, or pdo_mysql extension to talk to your database.

579 |

If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.

580 | "), 'db_connect_fail' ); 581 | 582 | return false; 583 | } 584 | else { 585 | $this->dbh = new $driver(); 586 | } 587 | 588 | return true; 589 | } 590 | 591 | /** 592 | * Connects to the database server and selects a database 593 | * 594 | * PHP5 style constructor for compatibility with PHP5. Does 595 | * the actual setting up of the class properties and connection 596 | * to the database. 597 | * 598 | * @link https://core.trac.wordpress.org/ticket/3354 599 | * @since 2.0.8 600 | * 601 | * @global string $wp_version 602 | * 603 | * @param string $dbuser MySQL database user 604 | * @param string $dbpassword MySQL database password 605 | * @param string $dbname MySQL database name 606 | * @param string $dbhost MySQL database host 607 | */ 608 | public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) { 609 | if ( ! $this->set_driver() ) { 610 | return; 611 | } 612 | 613 | register_shutdown_function( array( $this, '__destruct' ) ); 614 | 615 | if ( WP_DEBUG && WP_DEBUG_DISPLAY ) { 616 | $this->show_errors(); 617 | } 618 | 619 | $this->init_charset(); 620 | 621 | $this->dbuser = $dbuser; 622 | $this->dbpassword = $dbpassword; 623 | $this->dbname = $dbname; 624 | $this->dbhost = $dbhost; 625 | 626 | $this->db_connect(); 627 | } 628 | 629 | /** 630 | * PHP5 style destructor and will run when database object is destroyed. 631 | * 632 | * @see wpdb::__construct() 633 | * @since 2.0.8 634 | * @return true 635 | */ 636 | public function __destruct() { 637 | return true; 638 | } 639 | 640 | /** 641 | * PHP5 style magic getter, used to lazy-load expensive data. 642 | * 643 | * @since 3.5.0 644 | * 645 | * @param string $name The private member to get, and optionally process 646 | * @return mixed The private member 647 | */ 648 | public function __get( $name ) { 649 | if ( 'col_info' === $name ) { 650 | $this->load_col_info(); 651 | } 652 | 653 | if ( isset( $this->$name ) ) { 654 | return $this->$name; 655 | } 656 | 657 | return false; 658 | } 659 | 660 | /** 661 | * Magic function, for backwards compatibility. 662 | * 663 | * @since 3.5.0 664 | * 665 | * @param string $name The private member to set 666 | * @param mixed $value The value to set 667 | */ 668 | public function __set( $name, $value ) { 669 | $protected_members = array( 670 | 'col_meta', 671 | 'table_charset', 672 | 'check_current_query', 673 | ); 674 | 675 | if ( in_array( $name, $protected_members, true ) ) { 676 | return; 677 | } 678 | 679 | $this->$name = $value; 680 | } 681 | 682 | /** 683 | * Magic function, for backwards compatibility. 684 | * 685 | * @since 3.5.0 686 | * 687 | * @param string $name The private member to check 688 | * 689 | * @return bool If the member is set or not 690 | */ 691 | public function __isset( $name ) { 692 | return isset( $this->$name ); 693 | } 694 | 695 | /** 696 | * Magic function, for backwards compatibility. 697 | * 698 | * @since 3.5.0 699 | * 700 | * @param string $name The private member to unset 701 | */ 702 | public function __unset( $name ) { 703 | unset( $this->$name ); 704 | } 705 | 706 | /** 707 | * Set $this->charset and $this->collate 708 | * 709 | * @since 3.1.0 710 | */ 711 | public function init_charset() { 712 | if ( function_exists('is_multisite') && is_multisite() ) { 713 | $this->charset = 'utf8'; 714 | if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) { 715 | $this->collate = DB_COLLATE; 716 | } 717 | else { 718 | $this->collate = 'utf8_general_ci'; 719 | } 720 | } elseif ( defined( 'DB_COLLATE' ) ) { 721 | $this->collate = DB_COLLATE; 722 | } 723 | 724 | if ( defined( 'DB_CHARSET' ) ) { 725 | $this->charset = DB_CHARSET; 726 | } 727 | 728 | if ( ! $this->dbh || ! $this->dbh->is_connected() ) { 729 | return; 730 | } 731 | 732 | if ( 'utf8' === $this->charset && $this->has_cap( 'utf8mb4' ) ) { 733 | $this->charset = 'utf8mb4'; 734 | } 735 | 736 | if ( 'utf8mb4' === $this->charset && ( ! $this->collate || stripos( $this->collate, 'utf8_' ) === 0 ) ) { 737 | $this->collate = 'utf8mb4_unicode_ci'; 738 | } 739 | } 740 | 741 | /** 742 | * Sets the connection's character set. 743 | * 744 | * @since 3.1.0 745 | * 746 | * @param resource $dbh The resource given by the driver 747 | * @param string $charset Optional. The character set. Default null. 748 | * @param string $collate Optional. The collation. Default null. 749 | */ 750 | public function set_charset( $dbh, $charset = null, $collate = null ) { 751 | if ( ! isset( $charset ) ) { 752 | $charset = $this->charset; 753 | } 754 | 755 | if ( ! isset( $collate ) ) { 756 | $collate = $this->collate; 757 | } 758 | 759 | if ( ! $dbh->set_charset( $charset, $collate ) ) { 760 | if ( $this->has_cap( 'collation' ) && ! empty( $charset ) ) { 761 | $query = $this->prepare( 'SET NAMES %s', $charset ); 762 | 763 | if ( ! empty( $collate ) ) { 764 | $query .= $this->prepare( ' COLLATE %s', $collate ); 765 | } 766 | 767 | $this->query( $query ); 768 | } 769 | } 770 | } 771 | 772 | /** 773 | * Change the current SQL mode, and ensure its WordPress compatibility. 774 | * 775 | * If no modes are passed, it will ensure the current MySQL server 776 | * modes are compatible. 777 | * 778 | * @since 3.9.0 779 | * 780 | * @param array $modes Optional. A list of SQL modes to set. 781 | */ 782 | public function set_sql_mode( $modes = array() ) { 783 | if ( empty( $modes ) ) { 784 | $res = $this->dbh->query( 'SELECT @@SESSION.sql_mode;' ); 785 | 786 | if ( ! $res ) { 787 | return; 788 | } 789 | 790 | $modes_str = $this->dbh->query_result( 0 ); 791 | 792 | if ( empty( $modes_str ) ) { 793 | return; 794 | } 795 | 796 | $modes = explode( ',', $modes_str ); 797 | } 798 | 799 | $modes = array_change_key_case( $modes, CASE_UPPER ); 800 | 801 | /** 802 | * Filter the list of incompatible SQL modes to exclude. 803 | * 804 | * @since 3.9.0 805 | * 806 | * @param array $incompatible_modes An array of incompatible modes. 807 | */ 808 | $incompatible_modes = (array) apply_filters( 'incompatible_sql_modes', $this->incompatible_modes ); 809 | 810 | foreach ( $modes as $i => $mode ) { 811 | if ( in_array( $mode, $incompatible_modes ) ) { 812 | unset( $modes[ $i ] ); 813 | } 814 | } 815 | 816 | $modes_str = implode( ',', $modes ); 817 | 818 | $this->dbh->query( "SET SESSION sql_mode='$modes_str';" ); 819 | } 820 | 821 | /** 822 | * Sets the table prefix for the WordPress tables. 823 | * 824 | * @since 2.5.0 825 | * 826 | * @param string $prefix Alphanumeric name for the new prefix. 827 | * @param bool $set_table_names Optional. Whether the table names, e.g. wpdb::$posts, should be updated or not. 828 | * @return string|WP_Error Old prefix or WP_Error on error 829 | */ 830 | public function set_prefix( $prefix, $set_table_names = true ) { 831 | 832 | if ( preg_match( '|[^a-z0-9_]|i', $prefix ) ) 833 | return new WP_Error('invalid_db_prefix', 'Invalid database prefix' ); 834 | 835 | $old_prefix = is_multisite() ? '' : $prefix; 836 | 837 | if ( isset( $this->base_prefix ) ) 838 | $old_prefix = $this->base_prefix; 839 | 840 | $this->base_prefix = $prefix; 841 | 842 | if ( $set_table_names ) { 843 | foreach ( $this->tables( 'global' ) as $table => $prefixed_table ) 844 | $this->$table = $prefixed_table; 845 | 846 | if ( is_multisite() && empty( $this->blogid ) ) 847 | return $old_prefix; 848 | 849 | $this->prefix = $this->get_blog_prefix(); 850 | 851 | foreach ( $this->tables( 'blog' ) as $table => $prefixed_table ) 852 | $this->$table = $prefixed_table; 853 | 854 | foreach ( $this->tables( 'old' ) as $table => $prefixed_table ) 855 | $this->$table = $prefixed_table; 856 | } 857 | return $old_prefix; 858 | } 859 | 860 | /** 861 | * Sets blog id. 862 | * 863 | * @since 3.0.0 864 | * @access public 865 | * 866 | * @param int $blog_id 867 | * @param int $site_id Optional. 868 | * @return int previous blog id 869 | */ 870 | public function set_blog_id( $blog_id, $site_id = 0 ) { 871 | if ( ! empty( $site_id ) ) 872 | $this->siteid = $site_id; 873 | 874 | $old_blog_id = $this->blogid; 875 | $this->blogid = $blog_id; 876 | 877 | $this->prefix = $this->get_blog_prefix(); 878 | 879 | foreach ( $this->tables( 'blog' ) as $table => $prefixed_table ) 880 | $this->$table = $prefixed_table; 881 | 882 | foreach ( $this->tables( 'old' ) as $table => $prefixed_table ) 883 | $this->$table = $prefixed_table; 884 | 885 | return $old_blog_id; 886 | } 887 | 888 | /** 889 | * Gets blog prefix. 890 | * 891 | * @since 3.0.0 892 | * @param int $blog_id Optional. 893 | * @return string Blog prefix. 894 | */ 895 | public function get_blog_prefix( $blog_id = null ) { 896 | if ( is_multisite() ) { 897 | if ( null === $blog_id ) { 898 | $blog_id = $this->blogid; 899 | } 900 | 901 | $blog_id = (int) $blog_id; 902 | 903 | if ( defined( 'MULTISITE' ) && ( 0 == $blog_id || 1 == $blog_id ) ) { 904 | return $this->base_prefix; 905 | } 906 | else { 907 | return $this->base_prefix . $blog_id . '_'; 908 | } 909 | } else { 910 | return $this->base_prefix; 911 | } 912 | } 913 | 914 | /** 915 | * Returns an array of WordPress tables. 916 | * 917 | * Also allows for the CUSTOM_USER_TABLE and CUSTOM_USER_META_TABLE to 918 | * override the WordPress users and usermeta tables that would otherwise 919 | * be determined by the prefix. 920 | * 921 | * The scope argument can take one of the following: 922 | * 923 | * 'all' - returns 'all' and 'global' tables. No old tables are returned. 924 | * 'blog' - returns the blog-level tables for the queried blog. 925 | * 'global' - returns the global tables for the installation, returning multisite tables only if running multisite. 926 | * 'ms_global' - returns the multisite global tables, regardless if current installation is multisite. 927 | * 'old' - returns tables which are deprecated. 928 | * 929 | * @since 3.0.0 930 | * @uses wpdb::$tables 931 | * @uses wpdb::$old_tables 932 | * @uses wpdb::$global_tables 933 | * @uses wpdb::$ms_global_tables 934 | * 935 | * @param string $scope Optional. Can be all, global, ms_global, blog, or old tables. Defaults to all. 936 | * @param bool $prefix Optional. Whether to include table prefixes. Default true. If blog 937 | * prefix is requested, then the custom users and usermeta tables will be mapped. 938 | * @param int $blog_id Optional. The blog_id to prefix. Defaults to wpdb::$blogid. Used only when prefix is requested. 939 | * @return array Table names. When a prefix is requested, the key is the unprefixed table name. 940 | */ 941 | public function tables( $scope = 'all', $prefix = true, $blog_id = 0 ) { 942 | switch ( $scope ) { 943 | case 'all' : 944 | $tables = array_merge( $this->global_tables, $this->tables ); 945 | 946 | if ( is_multisite() ) { 947 | $tables = array_merge( $tables, $this->ms_global_tables ); 948 | } 949 | break; 950 | case 'blog' : 951 | $tables = $this->tables; 952 | break; 953 | case 'global' : 954 | $tables = $this->global_tables; 955 | 956 | if ( is_multisite() ) { 957 | $tables = array_merge( $tables, $this->ms_global_tables ); 958 | } 959 | break; 960 | case 'ms_global' : 961 | $tables = $this->ms_global_tables; 962 | break; 963 | case 'old' : 964 | $tables = $this->old_tables; 965 | break; 966 | default : 967 | return array(); 968 | } 969 | 970 | if ( $prefix ) { 971 | if ( ! $blog_id ) { 972 | $blog_id = $this->blogid; 973 | } 974 | 975 | $blog_prefix = $this->get_blog_prefix( $blog_id ); 976 | $base_prefix = $this->base_prefix; 977 | $global_tables = array_merge( $this->global_tables, $this->ms_global_tables ); 978 | 979 | foreach ( $tables as $k => $table ) { 980 | if ( in_array( $table, $global_tables ) ) { 981 | $tables[ $table ] = $base_prefix . $table; 982 | } 983 | else { 984 | $tables[ $table ] = $blog_prefix . $table; 985 | } 986 | 987 | unset( $tables[ $k ] ); 988 | } 989 | 990 | if ( isset( $tables['users'] ) && defined( 'CUSTOM_USER_TABLE' ) ) { 991 | $tables['users'] = CUSTOM_USER_TABLE; 992 | } 993 | 994 | if ( isset( $tables['usermeta'] ) && defined( 'CUSTOM_USER_META_TABLE' ) ) { 995 | $tables['usermeta'] = CUSTOM_USER_META_TABLE; 996 | } 997 | } 998 | 999 | return $tables; 1000 | } 1001 | 1002 | /** 1003 | * Selects a database using the current database connection. 1004 | * 1005 | * The database name will be changed based on the current database 1006 | * connection. On failure, the execution will bail and display an DB error. 1007 | * 1008 | * @since 0.71 1009 | * 1010 | * @param string $db Database driver 1011 | * @param resource|null $dbh Optional link identifier. 1012 | */ 1013 | public function select( $db, $dbh = null ) { 1014 | if ( is_null( $dbh ) ) { 1015 | $dbh = $this->dbh; 1016 | } 1017 | 1018 | $success = $dbh->select( $db ); 1019 | 1020 | if ( ! $success ) { 1021 | $this->ready = false; 1022 | 1023 | if ( ! did_action( 'template_redirect' ) ) { 1024 | wp_load_translations_early(); 1025 | 1026 | $message = '

' . __( 'Can’t select database' ) . "

\n"; 1027 | 1028 | $message .= '

' . sprintf( 1029 | /* translators: %s: database name */ 1030 | __( '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.' ), 1031 | '' . htmlspecialchars( $db, ENT_QUOTES ) . '' 1032 | ) . "

\n"; 1033 | 1034 | $message .= "
    \n"; 1035 | $message .= '
  • ' . __( 'Are you sure it exists?' ) . "
  • \n"; 1036 | 1037 | $message .= '
  • ' . sprintf( 1038 | /* translators: 1: database user, 2: database name */ 1039 | __( 'Does the user %1$s have permission to use the %2$s database?' ), 1040 | '' . htmlspecialchars( $this->dbuser, ENT_QUOTES ) . '', 1041 | '' . htmlspecialchars( $db, ENT_QUOTES ) . '' 1042 | ) . "
  • \n"; 1043 | 1044 | $message .= '
  • ' . sprintf( 1045 | /* translators: %s: database name */ 1046 | __( 'On some systems the name of your database is prefixed with your username, so it would be like username_%1$s. Could that be the problem?' ), 1047 | htmlspecialchars( $db, ENT_QUOTES ) 1048 | ). "
  • \n"; 1049 | 1050 | $message .= "
\n"; 1051 | 1052 | $message .= '

' . sprintf( 1053 | /* translators: %s: support forums URL */ 1054 | __( 'If you don’t know how to set up a database you should contact your host. If all else fails you may find help at the WordPress Support Forums.' ), 1055 | __( 'https://wordpress.org/support/' ) 1056 | ) . "

\n"; 1057 | 1058 | $this->bail( $message, 'db_select_fail' ); 1059 | } 1060 | } 1061 | } 1062 | 1063 | /** 1064 | * Do not use, deprecated. 1065 | * 1066 | * Use esc_sql() or wpdb::prepare() instead. 1067 | * 1068 | * @since 2.8.0 1069 | * @deprecated 3.6.0 Use wpdb::prepare() 1070 | * @see wpdb::prepare 1071 | * @see esc_sql() 1072 | * @access private 1073 | * 1074 | * @param string $string 1075 | * @return string 1076 | */ 1077 | function _weak_escape( $string ) { 1078 | if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) ) { 1079 | _deprecated_function( __METHOD__, '3.6', 'wpdb::prepare() or esc_sql()' ); 1080 | } 1081 | 1082 | return addslashes( $string ); 1083 | } 1084 | 1085 | /** 1086 | * Real escape 1087 | * 1088 | * @since 2.8.0 1089 | * @access private 1090 | * 1091 | * @param string $string to escape 1092 | * @return string escaped 1093 | */ 1094 | function _real_escape( $string ) { 1095 | if ( $this->dbh ) { 1096 | return $this->dbh->escape( $string ); 1097 | } 1098 | 1099 | $class = get_class( $this ); 1100 | 1101 | if ( function_exists( '__' ) ) { 1102 | /* translators: %s: database access abstraction class, usually wpdb or a class extending wpdb */ 1103 | _doing_it_wrong( $class, sprintf( __( '%s must set a database connection for use with escaping.' ), $class ), E_USER_NOTICE ); 1104 | } else { 1105 | _doing_it_wrong( $class, sprintf( '%s must set a database connection for use with escaping.', $class ), E_USER_NOTICE ); 1106 | } 1107 | 1108 | return addslashes( $string ); 1109 | } 1110 | 1111 | /** 1112 | * Escape data. Works on arrays. 1113 | * 1114 | * @uses wpdb::_real_escape() 1115 | * @since 2.8.0 1116 | * @access private 1117 | * 1118 | * @param string|array $data 1119 | * @return string|array escaped 1120 | */ 1121 | function _escape( $data ) { 1122 | if ( is_array( $data ) ) { 1123 | foreach ( $data as $k => $v ) { 1124 | if ( is_array($v) ) 1125 | $data[$k] = $this->_escape( $v ); 1126 | else 1127 | $data[$k] = $this->_real_escape( $v ); 1128 | } 1129 | } else { 1130 | $data = $this->_real_escape( $data ); 1131 | } 1132 | 1133 | return $data; 1134 | } 1135 | 1136 | /** 1137 | * Do not use, deprecated. 1138 | * 1139 | * Use esc_sql() or wpdb::prepare() instead. 1140 | * 1141 | * @since 0.71 1142 | * @deprecated 3.6.0 Use wpdb::prepare() 1143 | * @see wpdb::prepare() 1144 | * @see esc_sql() 1145 | * 1146 | * @param mixed $data 1147 | * @return mixed 1148 | */ 1149 | public function escape( $data ) { 1150 | if ( func_num_args() === 1 && function_exists( '_deprecated_function' ) ) 1151 | _deprecated_function( __METHOD__, '3.6', 'wpdb::prepare() or esc_sql()' ); 1152 | if ( is_array( $data ) ) { 1153 | foreach ( $data as $k => $v ) { 1154 | if ( is_array( $v ) ) 1155 | $data[$k] = $this->escape( $v, 'recursive' ); 1156 | else 1157 | $data[$k] = $this->_weak_escape( $v, 'internal' ); 1158 | } 1159 | } else { 1160 | $data = $this->_weak_escape( $data, 'internal' ); 1161 | } 1162 | 1163 | return $data; 1164 | } 1165 | 1166 | /** 1167 | * Escapes content by reference for insertion into the database, for security 1168 | * 1169 | * @uses wpdb::_real_escape() 1170 | * 1171 | * @since 2.3.0 1172 | * 1173 | * @param string $string to escape 1174 | */ 1175 | public function escape_by_ref( &$string ) { 1176 | if ( ! is_float( $string ) ) { 1177 | $string = $this->_real_escape( $string ); 1178 | } 1179 | } 1180 | 1181 | /** 1182 | * Prepares a SQL query for safe execution. Uses sprintf()-like syntax. 1183 | * 1184 | * The following directives can be used in the query format string: 1185 | * %d (integer) 1186 | * %f (float) 1187 | * %s (string) 1188 | * %% (literal percentage sign - no argument needed) 1189 | * 1190 | * All of %d, %f, and %s are to be left unquoted in the query string and they need an argument passed for them. 1191 | * Literals (%) as parts of the query must be properly written as %%. 1192 | * 1193 | * This function only supports a small subset of the sprintf syntax; it only supports %d (integer), %f (float), and %s (string). 1194 | * Does not support sign, padding, alignment, width or precision specifiers. 1195 | * Does not support argument numbering/swapping. 1196 | * 1197 | * May be called like {@link http://php.net/sprintf sprintf()} or like {@link http://php.net/vsprintf vsprintf()}. 1198 | * 1199 | * Both %d and %s should be left unquoted in the query string. 1200 | * 1201 | * wpdb::prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d", 'foo', 1337 ) 1202 | * wpdb::prepare( "SELECT DATE_FORMAT(`field`, '%%c') FROM `table` WHERE `column` = %s", 'foo' ); 1203 | * 1204 | * @link http://php.net/sprintf Description of syntax. 1205 | * @since 2.3.0 1206 | * 1207 | * @param string $query Query statement with sprintf()-like placeholders 1208 | * @param array|mixed $args The array of variables to substitute into the query's placeholders if being called like 1209 | * {@link http://php.net/vsprintf vsprintf()}, or the first variable to substitute into the query's placeholders if 1210 | * being called like {@link http://php.net/sprintf sprintf()}. 1211 | * @param mixed $args,... further variables to substitute into the query's placeholders if being called like 1212 | * {@link http://php.net/sprintf sprintf()}. 1213 | * @return string|void Sanitized query string, if there is a query to prepare. 1214 | */ 1215 | public function prepare( $query, $args ) { 1216 | if ( is_null( $query ) ) 1217 | return; 1218 | 1219 | // This is not meant to be foolproof -- but it will catch obviously incorrect usage. 1220 | if ( strpos( $query, '%' ) === false ) { 1221 | _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9' ); 1222 | } 1223 | 1224 | $args = func_get_args(); 1225 | array_shift( $args ); 1226 | // If args were passed as an array (as in vsprintf), move them up 1227 | if ( isset( $args[0] ) && is_array($args[0]) ) 1228 | $args = $args[0]; 1229 | $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it 1230 | $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting 1231 | $query = preg_replace( '|(?esc_like( $find ) . $wild; 1246 | * $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE %s", $like ); 1247 | * 1248 | * Example Escape Chain: 1249 | * $sql = esc_sql( $wpdb->esc_like( $input ) ); 1250 | * 1251 | * @since 4.0.0 1252 | * @access public 1253 | * 1254 | * @param string $text The raw text to be escaped. The input typed by the user should have no 1255 | * extra or deleted slashes. 1256 | * @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare() 1257 | * or real_escape next. 1258 | */ 1259 | public function esc_like( $text ) { 1260 | return addcslashes( $text, '_%\\' ); 1261 | } 1262 | 1263 | /** 1264 | * Print SQL/DB error. 1265 | * 1266 | * @since 0.71 1267 | * @global array $EZSQL_ERROR Stores error information of query and error string 1268 | * 1269 | * @param string $str The error to display 1270 | * @return false|void False if the showing of errors is disabled. 1271 | */ 1272 | public function print_error( $str = '' ) { 1273 | global $EZSQL_ERROR; 1274 | 1275 | if ( !$str ) 1276 | $str = $this->dbh->get_error_message(); 1277 | $EZSQL_ERROR[] = array( 'query' => $this->last_query, 'error_str' => $str ); 1278 | 1279 | if ( $this->suppress_errors ) 1280 | return false; 1281 | 1282 | wp_load_translations_early(); 1283 | 1284 | if ( $caller = $this->get_caller() ) 1285 | $error_str = sprintf( __( 'WordPress database error %1$s for query %2$s made by %3$s' ), $str, $this->last_query, $caller ); 1286 | else 1287 | $error_str = sprintf( __( 'WordPress database error %1$s for query %2$s' ), $str, $this->last_query ); 1288 | 1289 | error_log( $error_str ); 1290 | 1291 | // Are we showing errors? 1292 | if ( ! $this->show_errors ) 1293 | return false; 1294 | 1295 | // If there is an error then take note of it 1296 | if ( is_multisite() ) { 1297 | $msg = sprintf( 1298 | "%s [%s]\n%s\n", 1299 | __( 'WordPress database error:' ), 1300 | $str, 1301 | $this->last_query 1302 | ); 1303 | 1304 | if ( defined( 'ERRORLOGFILE' ) ) { 1305 | error_log( $msg, 3, ERRORLOGFILE ); 1306 | } 1307 | 1308 | if ( defined( 'DIEONDBERROR' ) ) { 1309 | wp_die( $msg ); 1310 | } 1311 | } else { 1312 | $str = htmlspecialchars( $str, ENT_QUOTES ); 1313 | $query = htmlspecialchars( $this->last_query, ENT_QUOTES ); 1314 | 1315 | printf( 1316 | '

%s [%s]
%s

', 1317 | __( 'WordPress database error:' ), 1318 | $str, 1319 | $query 1320 | ); 1321 | } 1322 | } 1323 | 1324 | /** 1325 | * Enables showing of database errors. 1326 | * 1327 | * This function should be used only to enable showing of errors. 1328 | * wpdb::hide_errors() should be used instead for hiding of errors. However, 1329 | * this function can be used to enable and disable showing of database 1330 | * errors. 1331 | * 1332 | * @since 0.71 1333 | * @see wpdb::hide_errors() 1334 | * 1335 | * @param bool $show Whether to show or hide errors 1336 | * @return bool Old value for showing errors. 1337 | */ 1338 | public function show_errors( $show = true ) { 1339 | $errors = $this->show_errors; 1340 | $this->show_errors = $show; 1341 | return $errors; 1342 | } 1343 | 1344 | /** 1345 | * Disables showing of database errors. 1346 | * 1347 | * By default database errors are not shown. 1348 | * 1349 | * @since 0.71 1350 | * @see wpdb::show_errors() 1351 | * 1352 | * @return bool Whether showing of errors was active 1353 | */ 1354 | public function hide_errors() { 1355 | $show = $this->show_errors; 1356 | $this->show_errors = false; 1357 | return $show; 1358 | } 1359 | 1360 | /** 1361 | * Whether to suppress database errors. 1362 | * 1363 | * By default database errors are suppressed, with a simple 1364 | * call to this function they can be enabled. 1365 | * 1366 | * @since 2.5.0 1367 | * @see wpdb::hide_errors() 1368 | * @param bool $suppress Optional. New value. Defaults to true. 1369 | * @return bool Old value 1370 | */ 1371 | public function suppress_errors( $suppress = true ) { 1372 | $errors = $this->suppress_errors; 1373 | $this->suppress_errors = (bool) $suppress; 1374 | 1375 | return $errors; 1376 | } 1377 | 1378 | /** 1379 | * Kill cached query results. 1380 | * 1381 | * @since 0.71 1382 | */ 1383 | public function flush() { 1384 | $this->last_result = array(); 1385 | $this->col_info = null; 1386 | $this->last_query = null; 1387 | $this->rows_affected = $this->num_rows = 0; 1388 | $this->last_error = ''; 1389 | 1390 | if ( $this->dbh ) { 1391 | $this->dbh->flush(); 1392 | } 1393 | } 1394 | 1395 | /** 1396 | * Connect to and select database. 1397 | * 1398 | * If $allow_bail is false, the lack of database connection will need 1399 | * to be handled manually. 1400 | * 1401 | * @since 3.0.0 1402 | * @since 3.9.0 $allow_bail parameter added. 1403 | * 1404 | * @param bool $allow_bail Optional. Allows the function to bail. Default true. 1405 | * 1406 | * @return bool True with a successful connection, false on failure. 1407 | */ 1408 | public function db_connect( $allow_bail = true ) { 1409 | if ( ! $this->dbh ) { 1410 | return false; 1411 | } 1412 | 1413 | $this->is_mysql = $this->dbh->is_mysql(); 1414 | 1415 | if ( false !== strpos( $this->dbhost, ':' ) ) { 1416 | list( $host, $port ) = explode( ':', $this->dbhost ); 1417 | } 1418 | else { 1419 | $host = $this->dbhost; 1420 | $port = 3306; 1421 | } 1422 | 1423 | $options = array(); 1424 | $options['key'] = defined( 'DB_SSL_KEY' ) ? DB_SSL_KEY : null; 1425 | $options['cert'] = defined( 'DB_SSL_CERT' ) ? DB_SSL_CERT : null; 1426 | $options['ca'] = defined( 'DB_SSL_CA' ) ? DB_SSL_CA : null; 1427 | $options['ca_path'] = defined( 'DB_SSL_CA_PATH' ) ? DB_SSL_CA_PATH : null; 1428 | $options['cipher'] = defined( 'DB_SSL_CIPHER' ) ? DB_SSL_CIPHER : null; 1429 | 1430 | $is_connected = $this->dbh->connect( $host, $this->dbuser, $this->dbpassword, $port, $options ); 1431 | 1432 | if ( ! $is_connected && ! $this->dbh instanceof wpdb_driver_mysql ) { 1433 | $this->dbh = null; 1434 | 1435 | $attempt_fallback = true; 1436 | 1437 | if ( $this->has_connected ) { 1438 | $attempt_fallback = false; 1439 | } elseif ( defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL ) { 1440 | $attempt_fallback = false; 1441 | } 1442 | 1443 | $drivers = WP_DB_Driver_Config::get_drivers(); 1444 | $driver = 'wpdb_driver_mysql'; 1445 | 1446 | include_once $drivers[ $driver ]; 1447 | 1448 | if ( $attempt_fallback && call_user_func( array( $driver, 'is_supported' ) ) ) { 1449 | $this->dbh = new $driver(); 1450 | 1451 | return $this->db_connect( $allow_bail ); 1452 | } 1453 | } 1454 | 1455 | if ( ! $is_connected && $allow_bail ) { 1456 | wp_load_translations_early(); 1457 | 1458 | // Load custom DB error template, if present. 1459 | if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) { 1460 | require_once( WP_CONTENT_DIR . '/db-error.php' ); 1461 | die(); 1462 | } 1463 | 1464 | $message = '

' . __( 'Error establishing a database connection' ) . "

\n"; 1465 | 1466 | $message .= '

' . sprintf( 1467 | /* translators: 1: wp-config.php. 2: database host */ 1468 | __( 'This either means that the username and password information in your %1$s file is incorrect or we can’t contact the database server at %2$s. This could mean your host’s database server is down.' ), 1469 | 'wp-config.php', 1470 | '' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '' 1471 | ) . "

\n"; 1472 | 1473 | $message .= "
    \n"; 1474 | $message .= '
  • ' . __( 'Are you sure you have the correct username and password?' ) . "
  • \n"; 1475 | $message .= '
  • ' . __( 'Are you sure that you have typed the correct hostname?' ) . "
  • \n"; 1476 | $message .= '
  • ' . __( 'Are you sure that the database server is running?' ) . "
  • \n"; 1477 | $message .= "
\n"; 1478 | 1479 | $message .= '

' . sprintf( 1480 | /* translators: %s: support forums URL */ 1481 | __( 'If you’re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.' ), 1482 | __( 'https://wordpress.org/support/' ) 1483 | ) . "

\n"; 1484 | 1485 | $this->bail( $message, 'db_connect_fail' ); 1486 | 1487 | return false; 1488 | } 1489 | elseif ( $is_connected ) { 1490 | $this->has_connected = true; 1491 | $this->ready = true; 1492 | 1493 | $this->set_charset( $this->dbh ); 1494 | $this->set_sql_mode(); 1495 | $this->select( $this->dbname, $this->dbh ); 1496 | 1497 | return true; 1498 | } 1499 | 1500 | return false; 1501 | } 1502 | 1503 | /** 1504 | * Closes the current database connection. 1505 | * 1506 | * @since 4.5.0 1507 | * @access public 1508 | * 1509 | * @return bool True if the connection was successfully closed, false if it wasn't, 1510 | * or the connection doesn't exist. 1511 | */ 1512 | public function close() { 1513 | if ( $this->dbh ) { 1514 | return $this->dbh->close(); 1515 | } 1516 | 1517 | return false; 1518 | } 1519 | 1520 | /** 1521 | * Check that the connection to the database is still up. If not, try to reconnect. 1522 | * 1523 | * If this function is unable to reconnect, it will forcibly die, or if after the 1524 | * the template_redirect hook has been fired, return false instead. 1525 | * 1526 | * If $allow_bail is false, the lack of database connection will need 1527 | * to be handled manually. 1528 | * 1529 | * @since 3.9.0 1530 | * 1531 | * @param bool $allow_bail Optional. Allows the function to bail. Default true. 1532 | * 1533 | * @return bool|void True if the connection is up. 1534 | */ 1535 | public function check_connection( $allow_bail = true ) { 1536 | if ( $this->dbh->ping() ) { 1537 | return true; 1538 | } 1539 | 1540 | $error_reporting = false; 1541 | 1542 | // Disable warnings, as we don't want to see a multitude of "unable to connect" messages 1543 | if ( WP_DEBUG ) { 1544 | $error_reporting = error_reporting(); 1545 | error_reporting( $error_reporting & ~E_WARNING ); 1546 | } 1547 | 1548 | for ( $tries = 1; $tries <= $this->reconnect_retries; $tries++ ) { 1549 | // On the last try, re-enable warnings. We want to see a single instance of the 1550 | // "unable to connect" message on the bail() screen, if it appears. 1551 | if ( $this->reconnect_retries === $tries && WP_DEBUG ) { 1552 | error_reporting( $error_reporting ); 1553 | } 1554 | 1555 | if ( $this->db_connect( false ) ) { 1556 | if ( $error_reporting ) { 1557 | error_reporting( $error_reporting ); 1558 | } 1559 | 1560 | return true; 1561 | } 1562 | 1563 | sleep( 1 ); 1564 | } 1565 | 1566 | // If template_redirect has already happened, it's too late for wp_die()/dead_db(). 1567 | // Let's just return and hope for the best. 1568 | if ( did_action( 'template_redirect' ) ) { 1569 | return false; 1570 | } 1571 | 1572 | if ( ! $allow_bail ) { 1573 | return false; 1574 | } 1575 | 1576 | wp_load_translations_early(); 1577 | 1578 | $message = '

' . __( 'Error reconnecting to the database' ) . "

\n"; 1579 | 1580 | $message .= '

' . sprintf( 1581 | /* translators: %s: database host */ 1582 | __( 'This means that we lost contact with the database server at %s. This could mean your host’s database server is down.' ), 1583 | '' . htmlspecialchars( $this->dbhost, ENT_QUOTES ) . '' 1584 | ) . "

\n"; 1585 | 1586 | $message .= "
    \n"; 1587 | $message .= '
  • ' . __( 'Are you sure that the database server is running?' ) . "
  • \n"; 1588 | $message .= '
  • ' . __( 'Are you sure that the database server is not under particularly heavy load?' ) . "
  • \n"; 1589 | $message .= "
\n"; 1590 | 1591 | $message .= '

' . sprintf( 1592 | /* translators: %s: support forums URL */ 1593 | __( 'If you’re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.' ), 1594 | __( 'https://wordpress.org/support/' ) 1595 | ) . "

\n"; 1596 | 1597 | // We weren't able to reconnect, so we better bail. 1598 | $this->bail( $message, 'db_connect_fail' ); 1599 | 1600 | // Call dead_db() if bail didn't die, because this database is no more. It has ceased to be (at least temporarily). 1601 | dead_db(); 1602 | } 1603 | 1604 | /** 1605 | * Perform a MySQL database query, using current database connection. 1606 | * 1607 | * More information can be found on the codex page. 1608 | * 1609 | * @since 0.71 1610 | * 1611 | * @param string $query Database query 1612 | * @return int|false Number of rows affected/selected or false on error 1613 | */ 1614 | public function query( $query ) { 1615 | if ( ! $this->ready ) { 1616 | $this->check_current_query = true; 1617 | return false; 1618 | } 1619 | 1620 | /** 1621 | * Filter the database query. 1622 | * 1623 | * Some queries are made before the plugins have been loaded 1624 | * and thus cannot be filtered with this method. 1625 | * 1626 | * @since 2.1.0 1627 | * 1628 | * @param string $query Database query. 1629 | */ 1630 | $query = apply_filters( 'query', $query ); 1631 | 1632 | $this->flush(); 1633 | 1634 | // Log how the function was called 1635 | $this->func_call = "\$db->query(\"$query\")"; 1636 | 1637 | // If we're writing to the database, make sure the query will write safely. 1638 | if ( $this->check_current_query && ! $this->check_ascii( $query ) ) { 1639 | $stripped_query = $this->strip_invalid_text_from_query( $query ); 1640 | 1641 | // strip_invalid_text_from_query() can perform queries, so we need 1642 | // to flush again, just to make sure everything is clear. 1643 | $this->flush(); 1644 | 1645 | if ( $stripped_query !== $query ) { 1646 | $this->insert_id = 0; 1647 | return false; 1648 | } 1649 | } 1650 | 1651 | $this->check_current_query = true; 1652 | 1653 | // Keep track of the last query for debug.. 1654 | $this->last_query = $query; 1655 | 1656 | $this->_do_query( $query ); 1657 | 1658 | // MySQL server has gone away, try to reconnect 1659 | if ( ! $this->dbh->is_connected() ) { 1660 | if ( $this->check_connection() ) { 1661 | $this->flush(); 1662 | $this->_do_query( $query ); 1663 | } 1664 | else { 1665 | $this->insert_id = 0; 1666 | return false; 1667 | } 1668 | } 1669 | 1670 | // If there is an error then take note of it.. 1671 | if ( $this->last_error = $this->dbh->get_error_message() ) { 1672 | // Clear insert_id on a subsequent failed insert. 1673 | if ( $this->insert_id && preg_match( '/^\s*(insert|replace)\s/i', $query ) ) { 1674 | $this->insert_id = 0; 1675 | } 1676 | 1677 | $this->print_error(); 1678 | return false; 1679 | } 1680 | 1681 | if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) { 1682 | $return_val = $this->result; 1683 | } elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) { 1684 | $this->rows_affected = $this->dbh->affected_rows(); 1685 | // Take note of the insert_id 1686 | if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) { 1687 | $this->insert_id = $this->dbh->insert_id(); 1688 | } 1689 | // Return number of rows affected 1690 | $return_val = $this->rows_affected; 1691 | } else { 1692 | $return_val = $this->num_rows = count( $this->result ); 1693 | } 1694 | 1695 | $this->last_result = $this->dbh->get_results(); 1696 | 1697 | return $return_val; 1698 | } 1699 | 1700 | /** 1701 | * Internal function to perform the query call 1702 | * 1703 | * @since 3.9.0 1704 | * 1705 | * @access private 1706 | * @see wpdb::query() 1707 | * 1708 | * @param string $query The query to run. 1709 | */ 1710 | private function _do_query( $query ) { 1711 | if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { 1712 | $this->timer_start(); 1713 | } 1714 | 1715 | $this->result = $this->dbh->query( $query ); 1716 | $this->num_queries++; 1717 | 1718 | if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { 1719 | $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() ); 1720 | } 1721 | } 1722 | 1723 | /** 1724 | * Insert a row into a table. 1725 | * 1726 | * wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 'bar' ) ) 1727 | * wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) ) 1728 | * 1729 | * @since 2.5.0 1730 | * @see wpdb::prepare() 1731 | * @see wpdb::$field_types 1732 | * @see wp_set_wpdb_vars() 1733 | * 1734 | * @param string $table Table name 1735 | * @param array $data Data to insert (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped). 1736 | * Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case. 1737 | * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data. 1738 | * If string, that format will be used for all of the values in $data. 1739 | * A format is one of '%d', '%f', '%s' (integer, float, string). 1740 | * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types. 1741 | * @return int|false The number of rows inserted, or false on error. 1742 | */ 1743 | public function insert( $table, $data, $format = null ) { 1744 | return $this->_insert_replace_helper( $table, $data, $format, 'INSERT' ); 1745 | } 1746 | 1747 | /** 1748 | * Replace a row into a table. 1749 | * 1750 | * wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 'bar' ) ) 1751 | * wpdb::replace( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) ) 1752 | * 1753 | * @since 3.0.0 1754 | * @see wpdb::prepare() 1755 | * @see wpdb::$field_types 1756 | * @see wp_set_wpdb_vars() 1757 | * 1758 | * @param string $table Table name 1759 | * @param array $data Data to insert (in column => value pairs). 1760 | * Both $data columns and $data values should be "raw" (neither should be SQL escaped). 1761 | * Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case. 1762 | * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data. 1763 | * If string, that format will be used for all of the values in $data. 1764 | * A format is one of '%d', '%f', '%s' (integer, float, string). 1765 | * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types. 1766 | * @return int|false The number of rows affected, or false on error. 1767 | */ 1768 | public function replace( $table, $data, $format = null ) { 1769 | return $this->_insert_replace_helper( $table, $data, $format, 'REPLACE' ); 1770 | } 1771 | 1772 | /** 1773 | * Helper function for insert and replace. 1774 | * 1775 | * Runs an insert or replace query based on $type argument. 1776 | * 1777 | * @access private 1778 | * @since 3.0.0 1779 | * @see wpdb::prepare() 1780 | * @see wpdb::$field_types 1781 | * @see wp_set_wpdb_vars() 1782 | * 1783 | * @param string $table Table name 1784 | * @param array $data Data to insert (in column => value pairs). 1785 | * Both $data columns and $data values should be "raw" (neither should be SQL escaped). 1786 | * Sending a null value will cause the column to be set to NULL - the corresponding format is ignored in this case. 1787 | * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data. 1788 | * If string, that format will be used for all of the values in $data. 1789 | * A format is one of '%d', '%f', '%s' (integer, float, string). 1790 | * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types. 1791 | * @param string $type Optional. What type of operation is this? INSERT or REPLACE. Defaults to INSERT. 1792 | * @return int|false The number of rows affected, or false on error. 1793 | */ 1794 | function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) { 1795 | $this->insert_id = 0; 1796 | 1797 | if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) { 1798 | return false; 1799 | } 1800 | 1801 | $data = $this->process_fields( $table, $data, $format ); 1802 | if ( false === $data ) { 1803 | return false; 1804 | } 1805 | 1806 | $formats = $values = array(); 1807 | foreach ( $data as $value ) { 1808 | if ( is_null( $value['value'] ) ) { 1809 | $formats[] = 'NULL'; 1810 | continue; 1811 | } 1812 | $formats[] = $value['format']; 1813 | $values[] = $value['value']; 1814 | } 1815 | 1816 | $fields = '`' . implode( '`, `', array_keys( $data ) ) . '`'; 1817 | $formats = implode( ', ', $formats ); 1818 | 1819 | $sql = "$type INTO `$table` ($fields) VALUES ($formats)"; 1820 | 1821 | $this->check_current_query = false; 1822 | 1823 | return $this->query( $this->prepare( $sql, $values ) ); 1824 | } 1825 | 1826 | /** 1827 | * Update a row in the table 1828 | * 1829 | * wpdb::update( 'table', array( 'column' => 'foo', 'field' => 'bar' ), array( 'ID' => 1 ) ) 1830 | * wpdb::update( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( 'ID' => 1 ), array( '%s', '%d' ), array( '%d' ) ) 1831 | * 1832 | * @since 2.5.0 1833 | * @see wpdb::prepare() 1834 | * @see wpdb::$field_types 1835 | * @see wp_set_wpdb_vars() 1836 | * 1837 | * @param string $table Table name 1838 | * @param array $data Data to update (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped). 1839 | * Sending a null value will cause the column to be set to NULL - the corresponding 1840 | * format is ignored in this case. 1841 | * @param array $where A named array of WHERE clauses (in column => value pairs). 1842 | * Multiple clauses will be joined with ANDs. 1843 | * Both $where columns and $where values should be "raw". 1844 | * Sending a null value will create an IS NULL comparison - the corresponding format will be ignored in this case. 1845 | * @param array|string $format Optional. An array of formats to be mapped to each of the values in $data. 1846 | * If string, that format will be used for all of the values in $data. 1847 | * A format is one of '%d', '%f', '%s' (integer, float, string). 1848 | * If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types. 1849 | * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where. 1850 | * If string, that format will be used for all of the items in $where. 1851 | * A format is one of '%d', '%f', '%s' (integer, float, string). 1852 | * If omitted, all values in $where will be treated as strings. 1853 | * @return int|false The number of rows updated, or false on error. 1854 | */ 1855 | public function update( $table, $data, $where, $format = null, $where_format = null ) { 1856 | if ( ! is_array( $data ) || ! is_array( $where ) ) { 1857 | return false; 1858 | } 1859 | 1860 | $data = $this->process_fields( $table, $data, $format ); 1861 | if ( false === $data ) { 1862 | return false; 1863 | } 1864 | 1865 | $where = $this->process_fields( $table, $where, $where_format ); 1866 | if ( false === $where ) { 1867 | return false; 1868 | } 1869 | 1870 | $fields = $conditions = $values = array(); 1871 | 1872 | foreach ( $data as $field => $value ) { 1873 | if ( is_null( $value['value'] ) ) { 1874 | $fields[] = "`$field` = NULL"; 1875 | continue; 1876 | } 1877 | 1878 | $fields[] = "`$field` = " . $value['format']; 1879 | $values[] = $value['value']; 1880 | } 1881 | 1882 | foreach ( $where as $field => $value ) { 1883 | if ( is_null( $value['value'] ) ) { 1884 | $conditions[] = "`$field` IS NULL"; 1885 | continue; 1886 | } 1887 | 1888 | $conditions[] = "`$field` = " . $value['format']; 1889 | $values[] = $value['value']; 1890 | } 1891 | 1892 | $fields = implode( ', ', $fields ); 1893 | $conditions = implode( ' AND ', $conditions ); 1894 | 1895 | $sql = "UPDATE `$table` SET $fields WHERE $conditions"; 1896 | 1897 | $this->check_current_query = false; 1898 | 1899 | return $this->query( $this->prepare( $sql, $values ) ); 1900 | } 1901 | 1902 | /** 1903 | * Delete a row in the table 1904 | * 1905 | * wpdb::delete( 'table', array( 'ID' => 1 ) ) 1906 | * wpdb::delete( 'table', array( 'ID' => 1 ), array( '%d' ) ) 1907 | * 1908 | * @since 3.4.0 1909 | * @see wpdb::prepare() 1910 | * @see wpdb::$field_types 1911 | * @see wp_set_wpdb_vars() 1912 | * 1913 | * @param string $table Table name 1914 | * @param array $where A named array of WHERE clauses (in column => value pairs). Multiple clauses will be joined with ANDs. Both $where columns and $where values should be "raw". 1915 | * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where. 1916 | * If string, that format will be used for all of the items in $where. 1917 | * A format is one of '%d', '%f', '%s' (integer, float, string). 1918 | * If omitted, all values in $where will be treated as strings unless otherwise specified in wpdb::$field_types. 1919 | * @return int|false The number of rows updated, or false on error. 1920 | */ 1921 | public function delete( $table, $where, $where_format = null ) { 1922 | if ( ! is_array( $where ) ) { 1923 | return false; 1924 | } 1925 | 1926 | $where = $this->process_fields( $table, $where, $where_format ); 1927 | if ( false === $where ) { 1928 | return false; 1929 | } 1930 | 1931 | $conditions = $values = array(); 1932 | foreach ( $where as $field => $value ) { 1933 | if ( is_null( $value['value'] ) ) { 1934 | $conditions[] = "`$field` IS NULL"; 1935 | continue; 1936 | } 1937 | 1938 | $conditions[] = "`$field` = " . $value['format']; 1939 | $values[] = $value['value']; 1940 | } 1941 | 1942 | $conditions = implode( ' AND ', $conditions ); 1943 | 1944 | $sql = "DELETE FROM `$table` WHERE $conditions"; 1945 | 1946 | $this->check_current_query = false; 1947 | 1948 | return $this->query( $this->prepare( $sql, $values ) ); 1949 | } 1950 | 1951 | /** 1952 | * Processes arrays of field/value pairs and field formats. 1953 | * 1954 | * This is a helper method for wpdb's CRUD methods, which take field/value 1955 | * pairs for inserts, updates, and where clauses. This method first pairs 1956 | * each value with a format. Then it determines the charset of that field, 1957 | * using that to determine if any invalid text would be stripped. If text is 1958 | * stripped, then field processing is rejected and the query fails. 1959 | * 1960 | * @since 4.2.0 1961 | * @access protected 1962 | * 1963 | * @param string $table Table name. 1964 | * @param array $data Field/value pair. 1965 | * @param mixed $format Format for each field. 1966 | * @return array|false Returns an array of fields that contain paired values 1967 | * and formats. Returns false for invalid values. 1968 | */ 1969 | protected function process_fields( $table, $data, $format ) { 1970 | $data = $this->process_field_formats( $data, $format ); 1971 | 1972 | if ( false === $data ) { 1973 | return false; 1974 | } 1975 | 1976 | $data = $this->process_field_charsets( $data, $table ); 1977 | 1978 | if ( false === $data ) { 1979 | return false; 1980 | } 1981 | 1982 | $data = $this->process_field_lengths( $data, $table ); 1983 | 1984 | if ( false === $data ) { 1985 | return false; 1986 | } 1987 | 1988 | $converted_data = $this->strip_invalid_text( $data ); 1989 | 1990 | if ( $data !== $converted_data ) { 1991 | return false; 1992 | } 1993 | 1994 | return $data; 1995 | } 1996 | 1997 | /** 1998 | * Prepares arrays of value/format pairs as passed to wpdb CRUD methods. 1999 | * 2000 | * @since 4.2.0 2001 | * @access protected 2002 | * 2003 | * @param array $data Array of fields to values. 2004 | * @param mixed $format Formats to be mapped to the values in $data. 2005 | * @return array Array, keyed by field names with values being an array 2006 | * of 'value' and 'format' keys. 2007 | */ 2008 | protected function process_field_formats( $data, $format ) { 2009 | $formats = $original_formats = (array) $format; 2010 | 2011 | foreach ( $data as $field => $value ) { 2012 | $value = array( 2013 | 'value' => $value, 2014 | 'format' => '%s', 2015 | ); 2016 | 2017 | if ( ! empty( $format ) ) { 2018 | $value['format'] = array_shift( $formats ); 2019 | 2020 | if ( ! $value['format'] ) { 2021 | $value['format'] = reset( $original_formats ); 2022 | } 2023 | } elseif ( isset( $this->field_types[ $field ] ) ) { 2024 | $value['format'] = $this->field_types[ $field ]; 2025 | } 2026 | 2027 | $data[ $field ] = $value; 2028 | } 2029 | 2030 | return $data; 2031 | } 2032 | 2033 | /** 2034 | * Adds field charsets to field/value/format arrays generated by 2035 | * the wpdb::process_field_formats() method. 2036 | * 2037 | * @since 4.2.0 2038 | * @access protected 2039 | * 2040 | * @param array $data As it comes from the wpdb::process_field_formats() method. 2041 | * @param string $table Table name. 2042 | * @return array|false The same array as $data with additional 'charset' keys. 2043 | */ 2044 | protected function process_field_charsets( $data, $table ) { 2045 | foreach ( $data as $field => $value ) { 2046 | if ( '%d' === $value['format'] || '%f' === $value['format'] ) { 2047 | /* 2048 | * We can skip this field if we know it isn't a string. 2049 | * This checks %d/%f versus ! %s because its sprintf() could take more. 2050 | */ 2051 | $value['charset'] = false; 2052 | } else { 2053 | $value['charset'] = $this->get_col_charset( $table, $field ); 2054 | 2055 | if ( is_wp_error( $value['charset'] ) ) { 2056 | return false; 2057 | } 2058 | } 2059 | 2060 | $data[ $field ] = $value; 2061 | } 2062 | 2063 | return $data; 2064 | } 2065 | 2066 | /** 2067 | * For string fields, record the maximum string length that field can safely save. 2068 | * 2069 | * @since 4.2.1 2070 | * @access protected 2071 | * 2072 | * @param array $data As it comes from the wpdb::process_field_charsets() method. 2073 | * @param string $table Table name. 2074 | * @return array|false The same array as $data with additional 'length' keys, or false if 2075 | * any of the values were too long for their corresponding field. 2076 | */ 2077 | protected function process_field_lengths( $data, $table ) { 2078 | foreach ( $data as $field => $value ) { 2079 | if ( '%d' === $value['format'] || '%f' === $value['format'] ) { 2080 | /* 2081 | * We can skip this field if we know it isn't a string. 2082 | * This checks %d/%f versus ! %s because its sprintf() could take more. 2083 | */ 2084 | $value['length'] = false; 2085 | } else { 2086 | $value['length'] = $this->get_col_length( $table, $field ); 2087 | 2088 | if ( is_wp_error( $value['length'] ) ) { 2089 | return false; 2090 | } 2091 | } 2092 | 2093 | $data[ $field ] = $value; 2094 | } 2095 | 2096 | return $data; 2097 | } 2098 | 2099 | 2100 | /** 2101 | * Retrieve one variable from the database. 2102 | * 2103 | * Executes a SQL query and returns the value from the SQL result. 2104 | * 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. 2105 | * If $query is null, this function returns the value in the specified column and row from the previous SQL result. 2106 | * 2107 | * @since 0.71 2108 | * 2109 | * @param string|null $query Optional. SQL query. Defaults to null, use the result from the previous query. 2110 | * @param int $x Optional. Column of value to return. Indexed from 0. 2111 | * @param int $y Optional. Row of value to return. Indexed from 0. 2112 | * @return string|null Database query result (as string), or null on failure 2113 | */ 2114 | public function get_var( $query = null, $x = 0, $y = 0 ) { 2115 | $this->func_call = "\$db->get_var(\"$query\", $x, $y)"; 2116 | 2117 | if ( $this->check_current_query && $this->check_safe_collation( $query ) ) { 2118 | $this->check_current_query = false; 2119 | } 2120 | 2121 | if ( $query ) { 2122 | $this->query( $query ); 2123 | } 2124 | 2125 | // Extract var out of cached results based x,y vals 2126 | if ( ! empty( $this->last_result[$y] ) ) { 2127 | $values = array_values( get_object_vars( $this->last_result[$y] ) ); 2128 | } 2129 | 2130 | // If there is a value return it else return null 2131 | return ( isset( $values[$x] ) && $values[$x] !== '' ) ? $values[$x] : null; 2132 | } 2133 | 2134 | /** 2135 | * Retrieve one row from the database. 2136 | * 2137 | * Executes a SQL query and returns the row from the SQL result. 2138 | * 2139 | * @since 0.71 2140 | * 2141 | * @param string|null $query SQL query. 2142 | * @param string $output Optional. one of ARRAY_A | ARRAY_N | OBJECT constants. 2143 | * Return an associative array (column => value, ...), 2144 | * a numerically indexed array (0 => value, ...) or 2145 | * an object ( ->column = value ), respectively. 2146 | * @param int $y Optional. Row to return. Indexed from 0. 2147 | * @return array|object|null|void Database query result in format specified by $output or null on failure 2148 | */ 2149 | public function get_row( $query = null, $output = OBJECT, $y = 0 ) { 2150 | $this->func_call = "\$db->get_row(\"$query\",$output,$y)"; 2151 | 2152 | if ( $this->check_current_query && $this->check_safe_collation( $query ) ) { 2153 | $this->check_current_query = false; 2154 | } 2155 | 2156 | if ( $query ) { 2157 | $this->query( $query ); 2158 | } 2159 | else { 2160 | return null; 2161 | } 2162 | 2163 | if ( ! isset( $this->last_result[$y] ) ) { 2164 | return null; 2165 | } 2166 | 2167 | if ( $output == OBJECT ) { 2168 | return $this->last_result[$y] ? $this->last_result[$y] : null; 2169 | } elseif ( $output == ARRAY_A ) { 2170 | return $this->last_result[$y] ? get_object_vars( $this->last_result[$y] ) : null; 2171 | } elseif ( $output == ARRAY_N ) { 2172 | return $this->last_result[$y] ? array_values( get_object_vars( $this->last_result[$y] ) ) : null; 2173 | } elseif ( strtoupper( $output ) === OBJECT ) { 2174 | // Back compat for OBJECT being previously case insensitive. 2175 | return $this->last_result[$y] ? $this->last_result[$y] : null; 2176 | } else { 2177 | $this->print_error( " \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N" ); 2178 | } 2179 | } 2180 | 2181 | /** 2182 | * Retrieve one column from the database. 2183 | * 2184 | * Executes a SQL query and returns the column from the SQL result. 2185 | * If the SQL result contains more than one column, this function returns the column specified. 2186 | * If $query is null, this function returns the specified column from the previous SQL result. 2187 | * 2188 | * @since 0.71 2189 | * 2190 | * @param string|null $query Optional. SQL query. Defaults to previous query. 2191 | * @param int $x Optional. Column to return. Indexed from 0. 2192 | * @return array Database query result. Array indexed from 0 by SQL result row number. 2193 | */ 2194 | public function get_col( $query = null , $x = 0 ) { 2195 | if ( $this->check_current_query && $this->check_safe_collation( $query ) ) { 2196 | $this->check_current_query = false; 2197 | } 2198 | 2199 | if ( $query ) { 2200 | $this->query( $query ); 2201 | } 2202 | 2203 | $new_array = array(); 2204 | // Extract the column values 2205 | for ( $i = 0, $j = count( $this->last_result ); $i < $j; $i++ ) { 2206 | $new_array[$i] = $this->get_var( null, $x, $i ); 2207 | } 2208 | 2209 | return $new_array; 2210 | } 2211 | 2212 | /** 2213 | * Retrieve an entire SQL result set from the database (i.e., many rows) 2214 | * 2215 | * Executes a SQL query and returns the entire SQL result. 2216 | * 2217 | * @since 0.71 2218 | * 2219 | * @param string $query SQL query. 2220 | * @param string $output Optional. Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants. 2221 | * With one of the first three, return an array of rows indexed from 0 by SQL result row number. 2222 | * Each row is an associative array (column => value, ...), a numerically indexed array (0 => value, ...), or an object. ( ->column = value ), respectively. 2223 | * With OBJECT_K, return an associative array of row objects keyed by the value of each row's first column's value. 2224 | * Duplicate keys are discarded. 2225 | * @return array|object|null Database query results 2226 | */ 2227 | public function get_results( $query = null, $output = OBJECT ) { 2228 | $this->func_call = "\$db->get_results(\"$query\", $output)"; 2229 | 2230 | if ( $this->check_current_query && $this->check_safe_collation( $query ) ) { 2231 | $this->check_current_query = false; 2232 | } 2233 | 2234 | if ( $query ) { 2235 | $this->query( $query ); 2236 | } 2237 | else { 2238 | return null; 2239 | } 2240 | 2241 | $new_array = array(); 2242 | 2243 | if ( $output == OBJECT ) { 2244 | // Return an integer-keyed array of row objects 2245 | return $this->last_result; 2246 | } elseif ( $output == OBJECT_K ) { 2247 | // Return an array of row objects with keys from column 1 2248 | // (Duplicates are discarded) 2249 | foreach ( $this->last_result as $row ) { 2250 | $var_by_ref = get_object_vars( $row ); 2251 | $key = array_shift( $var_by_ref ); 2252 | 2253 | if ( ! isset( $new_array[ $key ] ) ) { 2254 | $new_array[ $key ] = $row; 2255 | } 2256 | } 2257 | return $new_array; 2258 | } elseif ( $output == ARRAY_A || $output == ARRAY_N ) { 2259 | // Return an integer-keyed array of... 2260 | if ( $this->last_result ) { 2261 | foreach ( (array) $this->last_result as $row ) { 2262 | if ( $output == ARRAY_N ) { 2263 | // ...integer-keyed row arrays 2264 | $new_array[] = array_values( get_object_vars( $row ) ); 2265 | } else { 2266 | // ...column name-keyed row arrays 2267 | $new_array[] = get_object_vars( $row ); 2268 | } 2269 | } 2270 | } 2271 | return $new_array; 2272 | } elseif ( strtoupper( $output ) === OBJECT ) { 2273 | // Back compat for OBJECT being previously case insensitive 2274 | return $this->last_result; 2275 | } 2276 | 2277 | return null; 2278 | } 2279 | 2280 | /** 2281 | * Retrieves the character set for the given table. 2282 | * 2283 | * @since 4.2.0 2284 | * @access protected 2285 | * 2286 | * @param string $table Table name. 2287 | * @return string|WP_Error Table character set, WP_Error object if it couldn't be found. 2288 | */ 2289 | protected function get_table_charset( $table ) { 2290 | $tablekey = strtolower( $table ); 2291 | 2292 | /** 2293 | * Filter the table charset value before the DB is checked. 2294 | * 2295 | * Passing a non-null value to the filter will effectively short-circuit 2296 | * checking the DB for the charset, returning that value instead. 2297 | * 2298 | * @since 4.2.0 2299 | * 2300 | * @param string $charset The character set to use. Default null. 2301 | * @param string $table The name of the table being checked. 2302 | */ 2303 | $charset = apply_filters( 'pre_get_table_charset', null, $table ); 2304 | if ( null !== $charset ) { 2305 | return $charset; 2306 | } 2307 | 2308 | if ( isset( $this->table_charset[ $tablekey ] ) ) { 2309 | return $this->table_charset[ $tablekey ]; 2310 | } 2311 | 2312 | $charsets = $columns = array(); 2313 | 2314 | $table_parts = explode( '.', $table ); 2315 | $table = '`' . implode( '`.`', $table_parts ) . '`'; 2316 | $results = $this->get_results( "SHOW FULL COLUMNS FROM $table" ); 2317 | if ( ! $results ) { 2318 | return new WP_Error( 'wpdb_get_table_charset_failure' ); 2319 | } 2320 | 2321 | foreach ( $results as $column ) { 2322 | $columns[ strtolower( $column->Field ) ] = $column; 2323 | } 2324 | 2325 | $this->col_meta[ $tablekey ] = $columns; 2326 | 2327 | foreach ( $columns as $column ) { 2328 | if ( ! empty( $column->Collation ) ) { 2329 | list( $charset ) = explode( '_', $column->Collation ); 2330 | 2331 | // If the current connection can't support utf8mb4 characters, let's only send 3-byte utf8 characters. 2332 | if ( 'utf8mb4' === $charset && ! $this->has_cap( 'utf8mb4' ) ) { 2333 | $charset = 'utf8'; 2334 | } 2335 | 2336 | $charsets[ strtolower( $charset ) ] = true; 2337 | } 2338 | 2339 | list( $type ) = explode( '(', $column->Type ); 2340 | 2341 | // A binary/blob means the whole query gets treated like this. 2342 | if ( in_array( strtoupper( $type ), array( 'BINARY', 'VARBINARY', 'TINYBLOB', 'MEDIUMBLOB', 'BLOB', 'LONGBLOB' ) ) ) { 2343 | $this->table_charset[ $tablekey ] = 'binary'; 2344 | return 'binary'; 2345 | } 2346 | } 2347 | 2348 | // utf8mb3 is an alias for utf8. 2349 | if ( isset( $charsets['utf8mb3'] ) ) { 2350 | $charsets['utf8'] = true; 2351 | unset( $charsets['utf8mb3'] ); 2352 | } 2353 | 2354 | // Check if we have more than one charset in play. 2355 | $count = count( $charsets ); 2356 | if ( 1 === $count ) { 2357 | $charset = key( $charsets ); 2358 | } elseif ( 0 === $count ) { 2359 | // No charsets, assume this table can store whatever. 2360 | $charset = false; 2361 | } else { 2362 | // More than one charset. Remove latin1 if present and recalculate. 2363 | unset( $charsets['latin1'] ); 2364 | $count = count( $charsets ); 2365 | 2366 | if ( 1 === $count ) { 2367 | // Only one charset (besides latin1). 2368 | $charset = key( $charsets ); 2369 | } elseif ( 2 === $count && isset( $charsets['utf8'], $charsets['utf8mb4'] ) ) { 2370 | // Two charsets, but they're utf8 and utf8mb4, use utf8. 2371 | $charset = 'utf8'; 2372 | } else { 2373 | // Two mixed character sets. ascii. 2374 | $charset = 'ascii'; 2375 | } 2376 | } 2377 | 2378 | $this->table_charset[ $tablekey ] = $charset; 2379 | 2380 | return $charset; 2381 | } 2382 | 2383 | /** 2384 | * Retrieves the character set for the given column. 2385 | * 2386 | * @since 4.2.0 2387 | * @access public 2388 | * 2389 | * @param string $table Table name. 2390 | * @param string $column Column name. 2391 | * @return string|false|WP_Error Column character set as a string. False if the column has no 2392 | * character set. WP_Error object if there was an error. 2393 | */ 2394 | public function get_col_charset( $table, $column ) { 2395 | $tablekey = strtolower( $table ); 2396 | $columnkey = strtolower( $column ); 2397 | 2398 | /** 2399 | * Filter the column charset value before the DB is checked. 2400 | * 2401 | * Passing a non-null value to the filter will short-circuit 2402 | * checking the DB for the charset, returning that value instead. 2403 | * 2404 | * @since 4.2.0 2405 | * 2406 | * @param string $charset The character set to use. Default null. 2407 | * @param string $table The name of the table being checked. 2408 | * @param string $column The name of the column being checked. 2409 | */ 2410 | $charset = apply_filters( 'pre_get_col_charset', null, $table, $column ); 2411 | 2412 | if ( null !== $charset ) { 2413 | return $charset; 2414 | } 2415 | 2416 | // Skip this entirely if this isn't a MySQL database. 2417 | if ( ! $this->is_mysql ) { 2418 | return false; 2419 | } 2420 | 2421 | if ( empty( $this->table_charset[ $tablekey ] ) ) { 2422 | // This primes column information for us. 2423 | $table_charset = $this->get_table_charset( $table ); 2424 | 2425 | if ( is_wp_error( $table_charset ) ) { 2426 | return $table_charset; 2427 | } 2428 | } 2429 | 2430 | // If still no column information, return the table charset. 2431 | if ( empty( $this->col_meta[ $tablekey ] ) ) { 2432 | return $this->table_charset[ $tablekey ]; 2433 | } 2434 | 2435 | // If this column doesn't exist, return the table charset. 2436 | if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) { 2437 | return $this->table_charset[ $tablekey ]; 2438 | } 2439 | 2440 | // Return false when it's not a string column. 2441 | if ( empty( $this->col_meta[ $tablekey ][ $columnkey ]->Collation ) ) { 2442 | return false; 2443 | } 2444 | 2445 | list( $charset ) = explode( '_', $this->col_meta[ $tablekey ][ $columnkey ]->Collation ); 2446 | 2447 | return $charset; 2448 | } 2449 | 2450 | /** 2451 | * Retrieve the maximum string length allowed in a given column. 2452 | * The length may either be specified as a byte length or a character length. 2453 | * 2454 | * @since 4.2.1 2455 | * @access public 2456 | * 2457 | * @param string $table Table name. 2458 | * @param string $column Column name. 2459 | * @return array|false|WP_Error array( 'length' => (int), 'type' => 'byte' | 'char' ) 2460 | * false if the column has no length (for example, numeric column) 2461 | * WP_Error object if there was an error. 2462 | */ 2463 | public function get_col_length( $table, $column ) { 2464 | global $wp_db_version; 2465 | 2466 | $tablekey = strtolower( $table ); 2467 | $columnkey = strtolower( $column ); 2468 | 2469 | // Skip this entirely if this isn't a MySQL database. 2470 | if ( ! $this->is_mysql ) { 2471 | return false; 2472 | } 2473 | 2474 | if ( empty( $this->col_meta[ $tablekey ] ) ) { 2475 | // This primes column information for us. 2476 | $table_charset = $this->get_table_charset( $table ); 2477 | 2478 | if ( is_wp_error( $table_charset ) ) { 2479 | return $table_charset; 2480 | } 2481 | } 2482 | 2483 | if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) { 2484 | return false; 2485 | } 2486 | 2487 | $typeinfo = explode( '(', $this->col_meta[ $tablekey ][ $columnkey ]->Type ); 2488 | 2489 | $type = strtolower( $typeinfo[0] ); 2490 | if ( ! empty( $typeinfo[1] ) ) { 2491 | $length = trim( $typeinfo[1], ')' ); 2492 | } else { 2493 | $length = false; 2494 | } 2495 | 2496 | $object = false; 2497 | 2498 | switch( $type ) { 2499 | case 'char': 2500 | case 'varchar': 2501 | $object = array( 2502 | 'type' => 'char', 2503 | 'length' => (int) $length, 2504 | ); 2505 | break; 2506 | case 'binary': 2507 | case 'varbinary': 2508 | $object = array( 2509 | 'type' => 'byte', 2510 | 'length' => (int) $length, 2511 | ); 2512 | break; 2513 | case 'tinyblob': 2514 | case 'tinytext': 2515 | $object = array( 2516 | 'type' => 'byte', 2517 | 'length' => 255, // 2^8 - 1 2518 | ); 2519 | break; 2520 | case 'blob': 2521 | case 'text': 2522 | $object = array( 2523 | 'type' => 'byte', 2524 | 'length' => 65535, // 2^16 - 1 2525 | ); 2526 | break; 2527 | case 'mediumblob': 2528 | case 'mediumtext': 2529 | $object = array( 2530 | 'type' => 'byte', 2531 | 'length' => 16777215, // 2^24 - 1 2532 | ); 2533 | break; 2534 | case 'longblob': 2535 | case 'longtext': 2536 | $object = array( 2537 | 'type' => 'byte', 2538 | 'length' => 4294967295, // 2^32 - 1 2539 | ); 2540 | break; 2541 | } 2542 | 2543 | if ( $object && $wp_db_version < 31534 ) { 2544 | return $object['length']; 2545 | } 2546 | 2547 | return $object; 2548 | } 2549 | 2550 | /** 2551 | * Check if a string is ASCII. 2552 | * 2553 | * The negative regex is faster for non-ASCII strings, as it allows 2554 | * the search to finish as soon as it encounters a non-ASCII character. 2555 | * 2556 | * @since 4.2.0 2557 | * @access protected 2558 | * 2559 | * @param string $string String to check. 2560 | * @return bool True if ASCII, false if not. 2561 | */ 2562 | protected function check_ascii( $string ) { 2563 | if ( function_exists( 'mb_check_encoding' ) ) { 2564 | if ( mb_check_encoding( $string, 'ASCII' ) ) { 2565 | return true; 2566 | } 2567 | } elseif ( ! preg_match( '/[^\x00-\x7F]/', $string ) ) { 2568 | return true; 2569 | } 2570 | 2571 | return false; 2572 | } 2573 | 2574 | /** 2575 | * Check if the query is accessing a collation considered safe on the current version of MySQL. 2576 | * 2577 | * @since 4.2.0 2578 | * @access protected 2579 | * 2580 | * @param string $query The query to check. 2581 | * @return bool True if the collation is safe, false if it isn't. 2582 | */ 2583 | protected function check_safe_collation( $query ) { 2584 | if ( $this->checking_collation ) { 2585 | return true; 2586 | } 2587 | 2588 | // We don't need to check the collation for queries that don't read data. 2589 | $query = ltrim( $query, "\r\n\t (" ); 2590 | if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $query ) ) { 2591 | return true; 2592 | } 2593 | 2594 | // All-ASCII queries don't need extra checking. 2595 | if ( $this->check_ascii( $query ) ) { 2596 | return true; 2597 | } 2598 | 2599 | $table = $this->get_table_from_query( $query ); 2600 | 2601 | if ( ! $table ) { 2602 | return false; 2603 | } 2604 | 2605 | $this->checking_collation = true; 2606 | $collation = $this->get_table_charset( $table ); 2607 | $this->checking_collation = false; 2608 | 2609 | // Tables with no collation, or latin1 only, don't need extra checking. 2610 | if ( false === $collation || 'latin1' === $collation ) { 2611 | return true; 2612 | } 2613 | 2614 | $table = strtolower( $table ); 2615 | 2616 | if ( empty( $this->col_meta[ $table ] ) ) { 2617 | return false; 2618 | } 2619 | 2620 | // If any of the columns don't have one of these collations, it needs more sanity checking. 2621 | foreach ( $this->col_meta[ $table ] as $col ) { 2622 | if ( empty( $col->Collation ) ) { 2623 | continue; 2624 | } 2625 | 2626 | if ( ! in_array( $col->Collation, array( 'utf8_general_ci', 'utf8_bin', 'utf8mb4_general_ci', 'utf8mb4_bin' ), true ) ) { 2627 | return false; 2628 | } 2629 | } 2630 | 2631 | return true; 2632 | } 2633 | 2634 | /** 2635 | * Strips any invalid characters based on value/charset pairs. 2636 | * 2637 | * @since 4.2.0 2638 | * @access protected 2639 | * 2640 | * @param array $data Array of value arrays. Each value array has the keys 2641 | * 'value' and 'charset'. An optional 'ascii' key can be 2642 | * set to false to avoid redundant ASCII checks. 2643 | * @return array|WP_Error The $data parameter, with invalid characters removed from 2644 | * each value. This works as a passthrough: any additional keys 2645 | * such as 'field' are retained in each value array. If we cannot 2646 | * remove invalid characters, a WP_Error object is returned. 2647 | */ 2648 | protected function strip_invalid_text( $data ) { 2649 | $db_check_string = false; 2650 | 2651 | foreach ( $data as &$value ) { 2652 | $charset = $value['charset']; 2653 | 2654 | if ( is_array( $value['length'] ) ) { 2655 | $length = $value['length']['length']; 2656 | $truncate_by_byte_length = 'byte' === $value['length']['type']; 2657 | } else { 2658 | $length = false; 2659 | 2660 | // Since we have no length, we'll never truncate. 2661 | // Initialize the variable to false. true would take us 2662 | // through an unnecessary (for this case) codepath below. 2663 | $truncate_by_byte_length = false; 2664 | } 2665 | 2666 | // There's no charset to work with. 2667 | if ( false === $charset ) { 2668 | continue; 2669 | } 2670 | 2671 | // Column isn't a string. 2672 | if ( ! is_string( $value['value'] ) ) { 2673 | continue; 2674 | } 2675 | 2676 | $needs_validation = true; 2677 | if ( 2678 | // latin1 can store any byte sequence 2679 | 'latin1' === $charset 2680 | || 2681 | // ASCII is always OK. 2682 | ( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) ) 2683 | ) { 2684 | $truncate_by_byte_length = true; 2685 | $needs_validation = false; 2686 | } 2687 | 2688 | if ( $truncate_by_byte_length ) { 2689 | mbstring_binary_safe_encoding(); 2690 | if ( false !== $length && strlen( $value['value'] ) > $length ) { 2691 | $value['value'] = substr( $value['value'], 0, $length ); 2692 | } 2693 | reset_mbstring_encoding(); 2694 | 2695 | if ( ! $needs_validation ) { 2696 | continue; 2697 | } 2698 | } 2699 | 2700 | // utf8 can be handled by regex, which is a bunch faster than a DB lookup. 2701 | if ( ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) && function_exists( 'mb_strlen' ) ) { 2702 | $regex = '/ 2703 | ( 2704 | (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx 2705 | | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx 2706 | | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2 2707 | | [\xE1-\xEC][\x80-\xBF]{2} 2708 | | \xED[\x80-\x9F][\x80-\xBF] 2709 | | [\xEE-\xEF][\x80-\xBF]{2}'; 2710 | 2711 | if ( 'utf8mb4' === $charset ) { 2712 | $regex .= ' 2713 | | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3 2714 | | [\xF1-\xF3][\x80-\xBF]{3} 2715 | | \xF4[\x80-\x8F][\x80-\xBF]{2} 2716 | '; 2717 | } 2718 | 2719 | $regex .= '){1,40} # ...one or more times 2720 | ) 2721 | | . # anything else 2722 | /x'; 2723 | $value['value'] = preg_replace( $regex, '$1', $value['value'] ); 2724 | 2725 | 2726 | if ( false !== $length && mb_strlen( $value['value'], 'UTF-8' ) > $length ) { 2727 | $value['value'] = mb_substr( $value['value'], 0, $length, 'UTF-8' ); 2728 | } 2729 | continue; 2730 | } 2731 | 2732 | // We couldn't use any local conversions, send it to the DB. 2733 | $value['db'] = $db_check_string = true; 2734 | } 2735 | unset( $value ); // Remove by reference. 2736 | 2737 | if ( $db_check_string ) { 2738 | $queries = array(); 2739 | foreach ( $data as $col => $value ) { 2740 | if ( ! empty( $value['db'] ) ) { 2741 | // We're going to need to truncate by characters or bytes, depending on the length value we have. 2742 | if ( 'byte' === $value['length']['type'] ) { 2743 | // Using binary causes LEFT() to truncate by bytes. 2744 | $charset = 'binary'; 2745 | } else { 2746 | $charset = $value['charset']; 2747 | } 2748 | 2749 | if ( $this->charset ) { 2750 | $connection_charset = $this->charset; 2751 | } 2752 | else { 2753 | $connection_charset = $this->dbh->connection_charset(); 2754 | } 2755 | 2756 | if ( is_array( $value['length'] ) ) { 2757 | $queries[ $col ] = $this->prepare( "CONVERT( LEFT( CONVERT( %s USING $charset ), %.0f ) USING $connection_charset )", $value['value'], $value['length']['length'] ); 2758 | } else if ( 'binary' !== $charset ) { 2759 | // If we don't have a length, there's no need to convert binary - it will always return the same result. 2760 | $queries[ $col ] = $this->prepare( "CONVERT( CONVERT( %s USING $charset ) USING $connection_charset )", $value['value'] ); 2761 | } 2762 | 2763 | unset( $data[ $col ]['db'] ); 2764 | } 2765 | } 2766 | 2767 | $sql = array(); 2768 | foreach ( $queries as $column => $query ) { 2769 | if ( ! $query ) { 2770 | continue; 2771 | } 2772 | 2773 | $sql[] = $query . " AS x_$column"; 2774 | } 2775 | 2776 | $this->check_current_query = false; 2777 | $row = $this->get_row( "SELECT " . implode( ', ', $sql ), ARRAY_A ); 2778 | if ( ! $row ) { 2779 | return new WP_Error( 'wpdb_strip_invalid_text_failure' ); 2780 | } 2781 | 2782 | foreach ( array_keys( $data ) as $column ) { 2783 | if ( isset( $row["x_$column"] ) ) { 2784 | $data[ $column ]['value'] = $row["x_$column"]; 2785 | } 2786 | } 2787 | } 2788 | 2789 | return $data; 2790 | } 2791 | 2792 | /** 2793 | * Strips any invalid characters from the query. 2794 | * 2795 | * @since 4.2.0 2796 | * @access protected 2797 | * 2798 | * @param string $query Query to convert. 2799 | * @return string|WP_Error The converted query, or a WP_Error object if the conversion fails. 2800 | */ 2801 | protected function strip_invalid_text_from_query( $query ) { 2802 | // We don't need to check the collation for queries that don't read data. 2803 | $trimmed_query = ltrim( $query, "\r\n\t (" ); 2804 | if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $trimmed_query ) ) { 2805 | return $query; 2806 | } 2807 | 2808 | $table = $this->get_table_from_query( $query ); 2809 | if ( $table ) { 2810 | $charset = $this->get_table_charset( $table ); 2811 | 2812 | if ( is_wp_error( $charset ) ) { 2813 | return $charset; 2814 | } 2815 | 2816 | // We can't reliably strip text from tables containing binary/blob columns 2817 | if ( 'binary' === $charset ) { 2818 | return $query; 2819 | } 2820 | } else { 2821 | $charset = $this->charset; 2822 | } 2823 | 2824 | $data = array( 2825 | 'value' => $query, 2826 | 'charset' => $charset, 2827 | 'ascii' => false, 2828 | 'length' => false, 2829 | ); 2830 | 2831 | $data = $this->strip_invalid_text( array( $data ) ); 2832 | 2833 | if ( is_wp_error( $data ) ) { 2834 | return $data; 2835 | } 2836 | 2837 | return $data[0]['value']; 2838 | } 2839 | 2840 | /** 2841 | * Strips any invalid characters from the string for a given table and column. 2842 | * 2843 | * @since 4.2.0 2844 | * @access public 2845 | * 2846 | * @param string $table Table name. 2847 | * @param string $column Column name. 2848 | * @param string $value The text to check. 2849 | * @return string|WP_Error The converted string, or a `WP_Error` object if the conversion fails. 2850 | */ 2851 | public function strip_invalid_text_for_column( $table, $column, $value ) { 2852 | if ( ! is_string( $value ) ) { 2853 | return $value; 2854 | } 2855 | 2856 | $charset = $this->get_col_charset( $table, $column ); 2857 | 2858 | if ( ! $charset ) { 2859 | // Not a string column. 2860 | return $value; 2861 | } elseif ( is_wp_error( $charset ) ) { 2862 | // Bail on real errors. 2863 | return $charset; 2864 | } 2865 | 2866 | $data = array( 2867 | $column => array( 2868 | 'value' => $value, 2869 | 'charset' => $charset, 2870 | 'length' => $this->get_col_length( $table, $column ), 2871 | ) 2872 | ); 2873 | 2874 | $data = $this->strip_invalid_text( $data ); 2875 | 2876 | if ( is_wp_error( $data ) ) { 2877 | return $data; 2878 | } 2879 | 2880 | return $data[ $column ]['value']; 2881 | } 2882 | 2883 | /** 2884 | * Find the first table name referenced in a query. 2885 | * 2886 | * @since 4.2.0 2887 | * @access protected 2888 | * 2889 | * @param string $query The query to search. 2890 | * @return string|false $table The table name found, or false if a table couldn't be found. 2891 | */ 2892 | protected function get_table_from_query( $query ) { 2893 | // Remove characters that can legally trail the table name. 2894 | $query = rtrim( $query, ';/-#' ); 2895 | 2896 | // Allow (select...) union [...] style queries. Use the first query's table name. 2897 | $query = ltrim( $query, "\r\n\t (" ); 2898 | 2899 | // Strip everything between parentheses except nested selects. 2900 | $query = preg_replace( '/\((?!\s*select)[^(]*?\)/is', '()', $query ); 2901 | 2902 | // Quickly match most common queries. 2903 | if ( preg_match( '/^\s*(?:' 2904 | . 'SELECT.*?\s+FROM' 2905 | . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?' 2906 | . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?' 2907 | . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?' 2908 | . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?' 2909 | . ')\s+((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)/is', $query, $maybe ) ) { 2910 | return str_replace( '`', '', $maybe[1] ); 2911 | } 2912 | 2913 | // SHOW TABLE STATUS and SHOW TABLES 2914 | if ( preg_match( '/^\s*(?:' 2915 | . 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)' 2916 | . '|SHOW\s+(?:FULL\s+)?TABLES.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)' 2917 | . ')\W((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)\W/is', $query, $maybe ) ) { 2918 | return str_replace( '`', '', $maybe[1] ); 2919 | } 2920 | 2921 | // Big pattern for the rest of the table-related queries. 2922 | if ( preg_match( '/^\s*(?:' 2923 | . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM' 2924 | . '|DESCRIBE|DESC|EXPLAIN|HANDLER' 2925 | . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?' 2926 | . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|REPAIR).*\s+TABLE' 2927 | . '|TRUNCATE(?:\s+TABLE)?' 2928 | . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?' 2929 | . '|ALTER(?:\s+IGNORE)?\s+TABLE' 2930 | . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?' 2931 | . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON' 2932 | . '|DROP\s+INDEX.*\s+ON' 2933 | . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE' 2934 | . '|(?:GRANT|REVOKE).*ON\s+TABLE' 2935 | . '|SHOW\s+(?:.*FROM|.*TABLE)' 2936 | . ')\s+\(*\s*((?:[0-9a-zA-Z$_.`-]|[\xC2-\xDF][\x80-\xBF])+)\s*\)*/is', $query, $maybe ) ) { 2937 | return str_replace( '`', '', $maybe[1] ); 2938 | } 2939 | 2940 | return false; 2941 | } 2942 | 2943 | /** 2944 | * Load the column metadata from the last query. 2945 | * 2946 | * @since 3.5.0 2947 | * 2948 | * @access protected 2949 | */ 2950 | protected function load_col_info() { 2951 | $this->col_info = $this->dbh->load_col_info(); 2952 | } 2953 | 2954 | /** 2955 | * Retrieve column metadata from the last query. 2956 | * 2957 | * @since 0.71 2958 | * 2959 | * @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 2960 | * @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 2961 | * @return mixed Column Results 2962 | */ 2963 | public function get_col_info( $info_type = 'name', $col_offset = -1 ) { 2964 | $this->load_col_info(); 2965 | 2966 | if ( $this->col_info ) { 2967 | if ( $col_offset == -1 ) { 2968 | $i = 0; 2969 | $new_array = array(); 2970 | foreach ( (array) $this->col_info as $col ) { 2971 | $new_array[$i] = $col->{$info_type}; 2972 | $i++; 2973 | } 2974 | return $new_array; 2975 | } else { 2976 | return $this->col_info[$col_offset]->{$info_type}; 2977 | } 2978 | } 2979 | } 2980 | 2981 | /** 2982 | * Starts the timer, for debugging purposes. 2983 | * 2984 | * @since 1.5.0 2985 | * 2986 | * @return true 2987 | */ 2988 | public function timer_start() { 2989 | $this->time_start = microtime( true ); 2990 | return true; 2991 | } 2992 | 2993 | /** 2994 | * Stops the debugging timer. 2995 | * 2996 | * @since 1.5.0 2997 | * 2998 | * @return float Total time spent on the query, in seconds 2999 | */ 3000 | public function timer_stop() { 3001 | return ( microtime( true ) - $this->time_start ); 3002 | } 3003 | 3004 | /** 3005 | * Wraps errors in a nice header and footer and dies. 3006 | * 3007 | * Will not die if wpdb::$show_errors is false. 3008 | * 3009 | * @since 1.5.0 3010 | * 3011 | * @param string $message The Error message 3012 | * @param string $error_code Optional. A Computer readable string to identify the error. 3013 | * @return false|void 3014 | */ 3015 | public function bail( $message, $error_code = '500' ) { 3016 | if ( ! $this->show_errors ) { 3017 | if ( class_exists( 'WP_Error', false ) ) { 3018 | $this->error = new WP_Error($error_code, $message); 3019 | } else { 3020 | $this->error = $message; 3021 | } 3022 | 3023 | return false; 3024 | } 3025 | 3026 | wp_die($message); 3027 | } 3028 | 3029 | /** 3030 | * Whether MySQL database is at least the required minimum version. 3031 | * 3032 | * @since 2.5.0 3033 | * 3034 | * @uses string $wp_version 3035 | * @uses string $required_mysql_version 3036 | * 3037 | * @return WP_Error|void 3038 | */ 3039 | public function check_database_version() { 3040 | global $wp_version, $required_mysql_version; 3041 | // Make sure the server has the required MySQL version 3042 | if ( version_compare($this->db_version(), $required_mysql_version, '<') ) 3043 | return new WP_Error('database_version', sprintf( __( 'ERROR: WordPress %1$s requires MySQL %2$s or higher' ), $wp_version, $required_mysql_version )); 3044 | } 3045 | 3046 | /** 3047 | * Whether the database supports collation. 3048 | * 3049 | * Called when WordPress is generating the table scheme. 3050 | * 3051 | * Use `wpdb::has_cap( 'collation' )`. 3052 | * 3053 | * @since 2.5.0 3054 | * @deprecated 3.5.0 Use wpdb::has_cap() 3055 | * 3056 | * @return bool True if collation is supported, false if version does not 3057 | */ 3058 | public function supports_collation() { 3059 | _deprecated_function( __FUNCTION__, '3.5', 'wpdb::has_cap( \'collation\' )' ); 3060 | return $this->has_cap( 'collation' ); 3061 | } 3062 | 3063 | /** 3064 | * The database character collate. 3065 | * 3066 | * @since 3.5.0 3067 | * 3068 | * @return string The database character collate. 3069 | */ 3070 | public function get_charset_collate() { 3071 | $charset_collate = ''; 3072 | 3073 | if ( ! empty( $this->charset ) ) 3074 | $charset_collate = "DEFAULT CHARACTER SET $this->charset"; 3075 | if ( ! empty( $this->collate ) ) 3076 | $charset_collate .= " COLLATE $this->collate"; 3077 | 3078 | return $charset_collate; 3079 | } 3080 | 3081 | /** 3082 | * Determine if a database supports a particular feature. 3083 | * 3084 | * @since 2.7.0 3085 | * @since 4.1.0 Support was added for the 'utf8mb4' feature. 3086 | * 3087 | * @see wpdb::db_version() 3088 | * 3089 | * @param string $db_cap The feature to check for. Accepts 'collation', 3090 | * 'group_concat', 'subqueries', 'set_charset', 3091 | * or 'utf8mb4'. 3092 | * @return int|false Whether the database feature is supported, false otherwise. 3093 | */ 3094 | public function has_cap( $db_cap ) { 3095 | return $this->dbh->has_cap( $db_cap ); 3096 | } 3097 | 3098 | /** 3099 | * Retrieve the name of the function that called wpdb. 3100 | * 3101 | * Searches up the list of functions until it reaches 3102 | * the one that would most logically had called this method. 3103 | * 3104 | * @since 2.5.0 3105 | * 3106 | * @return string|array The name of the calling function 3107 | */ 3108 | public function get_caller() { 3109 | return wp_debug_backtrace_summary( __CLASS__ ); 3110 | } 3111 | 3112 | /** 3113 | * Retrieves the MySQL server version. 3114 | * 3115 | * @since 2.7.0 3116 | * 3117 | * @return null|string Null on failure, version number on success. 3118 | */ 3119 | public function db_version() { 3120 | return $this->dbh->db_version(); 3121 | } 3122 | 3123 | } 3124 | 3125 | $wpdb = new wpdb_drivers( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ); --------------------------------------------------------------------------------