Files

303 lines
9.1 KiB
PHP

<?php
/**
* Class: Custom DB Class
*
* Test the DB connection.
* It uses wpdb WordPress DB Class.
*
* @package wsal
*/
namespace WSAL\Entities\DBConnection;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( '\WSAL\Entities\DBConnection\MySQL_Connection' ) ) {
/**
* Test the DB connection.
* It uses wpdb WordPress DB Class.
*
* @package wsal
*/
class MySQL_Connection extends \wpdb {
/**
* Holds the database connection error string
*
* @var string
*
* @since 4.6.0
*/
public static $error_string = null;
/**
* Overwrite wpdb class for set $allow_bail to false
* and hide the print of the error
*
* @global string $wp_version
* @param string $dbuser - MySQL database user.
* @param string $dbpassword - MySQL database password.
* @param string $dbname - MySQL database name.
* @param string $dbhost - MySQL database host.
* @param bool $is_ssl - Set if connection is SSL encrypted.
* @param bool $is_cc - Set if connection has client certificates.
* @param string $ssl_ca - Certificate Authority.
* @param string $ssl_cert - Client Certificate.
* @param string $ssl_key - Client Key.
* @param string $dbport - MySQL database host.
*/
public function __construct( $dbuser, $dbpassword, $dbname, $dbhost, $is_ssl, $is_cc, $ssl_ca, $ssl_cert, $ssl_key, $dbport = '' ) {
if ( WP_DEBUG && WP_DEBUG_DISPLAY ) {
$this->show_errors();
}
if ( function_exists( 'mysqli_connect' ) ) {
if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
$this->use_mysqli = ! \WP_USE_EXT_MYSQL;
} elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) {
$this->use_mysqli = true;
} elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
$this->use_mysqli = true;
}
}
$this->dbuser = $dbuser;
$this->dbpassword = $dbpassword;
$this->dbname = $dbname;
$this->dbhost = $dbhost;
$this->dbport = $dbport ?? false;
// wp-config.php creation will manually connect when ready.
if ( defined( 'WP_SETUP_CONFIG' ) ) {
return;
}
if ( $is_ssl && ! defined( 'MYSQL_CLIENT_FLAGS' ) ) {
define( 'MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT );
}
if ( $is_cc ) {
if ( ! empty( $ssl_key ) && is_file( $ssl_key ) && ! defined( 'MYSQL_SSL_KEY' ) ) {
define( 'MYSQL_SSL_KEY', $ssl_key );
}
if ( ! empty( $ssl_cert ) && is_file( $ssl_cert ) && ! defined( 'MYSQL_SSL_CERT' ) ) {
define( 'MYSQL_SSL_CERT', $ssl_cert );
}
if ( ! empty( $ssl_ca ) && is_file( $ssl_ca ) && ! defined( 'MYSQL_SSL_CA' ) ) {
define( 'MYSQL_SSL_CA', $ssl_ca );
}
}
$this->db_connect( false );
}
/**
* Return an array with the Port and the Socket values for the MySQL connection.
*
* @return array - an array with the port and socket values.
*
* @since 5.5.0
*/
private function get_port_and_socket() {
$port = null;
$socket = null;
$host = $this->dbhost;
// Check if host contains a colon (old WSAL format was: IP:PORT or IP:/socket).
$port_or_socket = strstr( $host, ':' );
if ( ! empty( $this->dbport ) ) {
// Prefer explicit port if provided.
if ( is_numeric( $this->dbport ) ) {
$port = intval( $this->dbport );
} elseif ( strpos( $this->dbport, '/' ) === 0 ) {
$socket = $this->dbport;
}
// Remove port/socket from host if present.
if ( ! empty( $port_or_socket ) ) {
$host = substr( $host, 0, strpos( $host, ':' ) );
}
} elseif ( ! empty( $port_or_socket ) ) {
// Host contains :PORT or :/socket.
$host_part = substr( $host, 0, strpos( $host, ':' ) );
$extra = substr( $port_or_socket, 1 );
$host = $host_part;
if ( strpos( $extra, '/' ) === 0 ) {
// If this is a socket path.
$socket = $extra;
} elseif ( is_numeric( $extra ) ) {
// If it's a port number.
$port = intval( $extra );
} else {
// Rare case: port:socket (e.g., 3306:/tmp/mysql.sock).
$parts = explode( ':', $extra, 2 );
if ( is_numeric( $parts[0] ) ) {
$port = intval( $parts[0] );
}
if ( isset( $parts[1] ) && strpos( $parts[1], '/' ) === 0 ) {
$socket = $parts[1];
}
}
}
return array(
'port' => $port,
'socket' => $socket,
);
}
/**
* Connect to and select database.
*
* If $allow_bail is false, the lack of database connection will need
* to be handled manually.
*
* @param bool $allow_bail Optional. Allows the function to bail. Default true.
*
* @return bool True with a successful connection, false on failure.
* @since 3.0.0
* @since 3.9.0 $allow_bail parameter added.
*/
public function db_connect( $allow_bail = true ) {
$this->is_mysql = true;
$client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;
if ( $this->use_mysqli ) {
/*
* Set the MySQLi error reporting off because WordPress handles its own.
* This is due to the default value change from `MYSQLI_REPORT_OFF`
* to `MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT` in PHP 8.1.
*/
mysqli_report( MYSQLI_REPORT_OFF ); // phpcs:ignore -- Direct DB access
$this->dbh = mysqli_init(); // phpcs:ignore
// mysqli_real_connect doesn't support the host param including a port or socket
// like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file.
$port_and_socket = $this->get_port_and_socket();
$port = $port_and_socket['port'] ?? null;
$socket = $port_and_socket['socket'] ?? null;
$host = $this->dbhost;
// Set SSL certs if we want to use secure DB connections.
$ssl_opts = array(
'KEY' => ( defined( 'MYSQL_SSL_KEY' ) && is_file( MYSQL_SSL_KEY ) ) ? MYSQL_SSL_KEY : null,
'CERT' => ( defined( 'MYSQL_SSL_CERT' ) && is_file( MYSQL_SSL_CERT ) ) ? MYSQL_SSL_CERT : null,
'CA' => ( defined( 'MYSQL_SSL_CA' ) && is_file( MYSQL_SSL_CA ) ) ? MYSQL_SSL_CA : null,
'CA_PATH' => ( defined( 'MYSQL_SSL_CA_PATH' ) && is_dir( \MYSQL_SSL_CA_PATH ) ) ? \MYSQL_SSL_CA_PATH : null,
'CIPHER' => ( defined( 'MYSQL_SSL_CIPHER' ) ) ? \MYSQL_SSL_CIPHER : null,
);
$ssl_opts_set = false;
foreach ( $ssl_opts as $ssl_opt_val ) {
if ( ! is_null( $ssl_opt_val ) ) {
$ssl_opts_set = true;
break;
}
}
if ( MYSQLI_CLIENT_SSL !== ( $client_flags & MYSQLI_CLIENT_SSL ) ) {
$ssl_opts_set = false;
}
if ( $ssl_opts_set ) {
mysqli_ssl_set( // phpcs:ignore
$this->dbh,
$ssl_opts['KEY'],
$ssl_opts['CERT'],
$ssl_opts['CA'],
$ssl_opts['CA_PATH'],
$ssl_opts['CIPHER']
);
}
if ( WP_DEBUG ) {
mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags ); // phpcs:ignore
} else {
@mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpassword, null, $port, $socket, $client_flags ); // phpcs:ignore
}
if ( $this->dbh->connect_errno ) {
if ( 2002 === (int) $this->dbh->connect_errno ) {
self::$error_string = __( 'The plugin detected connectivity issues coming from your external database connection. Please check the status of your external database connection and try again!', 'wp-security-audit-log' );
}
}
if ( $this->dbh->connect_errno ) {
$this->dbh = null;
/**
* It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
* - We haven't previously connected, and
* - WP_USE_EXT_MYSQL isn't set to false, and
* - ext/mysql is loaded.
*/
$attempt_fallback = true;
if ( $this->has_connected
|| ( defined( 'WP_USE_EXT_MYSQL' ) && ! \WP_USE_EXT_MYSQL )
|| ( ! function_exists( 'mysql_connect' ) ) ) {
$attempt_fallback = false;
}
if ( $attempt_fallback ) {
$this->use_mysqli = false;
return $this->db_connect( $allow_bail );
}
}
}
if ( ! $this->dbh && $allow_bail ) {
return false;
} elseif ( $this->dbh ) {
if ( ! $this->has_connected ) {
$this->init_charset();
}
$this->has_connected = true;
$this->set_charset( $this->dbh );
$this->ready = true;
$this->set_sql_mode();
$this->select( $this->dbname, $this->dbh );
return true;
}
return false;
}
/**
* Selects a database using the current database connection.
*
* The database name will be changed based on the current database
* connection. On failure, the execution will bail and display an DB error.
*
* @since 0.71
*
* @param string $db MySQL database name.
* @param resource|null $dbh Optional link identifier.
*/
public function select( $db, $dbh = null ) {
if ( is_null( $dbh ) ) {
$dbh = $this->dbh;
}
if ( $this->use_mysqli ) {
$success = mysqli_select_db( $dbh, $db ); // phpcs:ignore
} else {
$success = mysql_select_db( $db, $dbh ); // phpcs:ignore
}
if ( ! $success ) {
$this->ready = false;
$this->db_select_error = true;
}
}
}
}