Database: Add support for connecting to IPv6 hosts
IPv4 addresses are scarce, overworked, and underpaid. They're ready to retire, but we just won't let them go. If you care about their wellbeing, switch to IPv6 today. Props schlessera, birgire. Fixes #41722. git-svn-id: https://develop.svn.wordpress.org/trunk@41629 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
f5c4c69bf1
commit
199aa17cda
|
@ -1460,24 +1460,23 @@ class wpdb {
|
|||
if ( $this->use_mysqli ) {
|
||||
$this->dbh = mysqli_init();
|
||||
|
||||
// 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.
|
||||
$host = $this->dbhost;
|
||||
$port = null;
|
||||
$socket = null;
|
||||
$host = $this->dbhost;
|
||||
$port_or_socket = strstr( $host, ':' );
|
||||
if ( ! empty( $port_or_socket ) ) {
|
||||
$host = substr( $host, 0, strpos( $host, ':' ) );
|
||||
$port_or_socket = substr( $port_or_socket, 1 );
|
||||
if ( 0 !== strpos( $port_or_socket, '/' ) ) {
|
||||
$port = intval( $port_or_socket );
|
||||
$maybe_socket = strstr( $port_or_socket, ':' );
|
||||
if ( ! empty( $maybe_socket ) ) {
|
||||
$socket = substr( $maybe_socket, 1 );
|
||||
}
|
||||
} else {
|
||||
$socket = $port_or_socket;
|
||||
$is_ipv6 = false;
|
||||
|
||||
if ( $host_data = $this->parse_db_host( $this->dbhost ) ) {
|
||||
list( $host, $port, $socket, $is_ipv6 ) = $host_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* If using the `mysqlnd` library, the IPv6 address needs to be
|
||||
* enclosed in square brackets, whereas it doesn't while using the
|
||||
* `libmysqlclient` library.
|
||||
* @see https://bugs.php.net/bug.php?id=67563
|
||||
*/
|
||||
if ( $is_ipv6 && extension_loaded( 'mysqlnd' ) ) {
|
||||
$host = "[$host]";
|
||||
}
|
||||
|
||||
if ( WP_DEBUG ) {
|
||||
|
@ -1489,7 +1488,8 @@ class wpdb {
|
|||
if ( $this->dbh->connect_errno ) {
|
||||
$this->dbh = null;
|
||||
|
||||
/* It's possible ext/mysqli is misconfigured. Fall back to ext/mysql if:
|
||||
/*
|
||||
* 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.
|
||||
|
@ -1569,6 +1569,52 @@ class wpdb {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the DB_HOST setting to interpret it for mysqli_real_connect.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @since 4.9.0
|
||||
*
|
||||
* @param string $host The DB_HOST setting to parse.
|
||||
* @return array|bool Array containing the host, the port, the socket and whether
|
||||
* it is an IPv6 address, in that order. If $host couldn't be parsed,
|
||||
* returns false.
|
||||
*/
|
||||
public function parse_db_host( $host ) {
|
||||
$port = null;
|
||||
$socket = null;
|
||||
$is_ipv6 = false;
|
||||
|
||||
// We need to check for an IPv6 address first.
|
||||
// An IPv6 address will always contain at least two colons.
|
||||
if ( substr_count( $host, ':' ) > 1 ) {
|
||||
$pattern = '#^(?:\[)?(?<host>[0-9a-fA-F:]+)(?:\]:(?<port>[\d]+))?(?:/(?<socket>.+))?#';
|
||||
$is_ipv6 = true;
|
||||
} else {
|
||||
// We seem to be dealing with an IPv4 address.
|
||||
$pattern = '#^(?<host>[^:/]*)(?::(?<port>[\d]+))?(?::(?<socket>.+))?#';
|
||||
}
|
||||
|
||||
$matches = array();
|
||||
$result = preg_match( $pattern, $host, $matches );
|
||||
|
||||
if ( 1 !== $result ) {
|
||||
// Couldn't parse the address, bail.
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( array( 'host', 'port', 'socket' ) as $component ) {
|
||||
if ( array_key_exists( $component, $matches ) ) {
|
||||
$$component = $matches[$component];
|
||||
}
|
||||
}
|
||||
|
||||
return array( $host, $port, $socket, $is_ipv6 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the connection to the database is still up. If not, try to reconnect.
|
||||
*
|
||||
|
|
|
@ -1126,4 +1126,166 @@ class Tests_DB extends WP_UnitTestCase {
|
|||
$sql = $wpdb->prepare( '%d %1$d %%% %', 1 );
|
||||
$this->assertEquals( '1 %1$d %% %', $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider parse_db_host_data_provider
|
||||
* @ticket 41722
|
||||
*/
|
||||
public function test_parse_db_host( $host_string, $expect_bail, $host, $port, $socket, $is_ipv6 ) {
|
||||
global $wpdb;
|
||||
$data = $wpdb->parse_db_host( $host_string );
|
||||
if ( $expect_bail ) {
|
||||
$this->assertFalse( $data );
|
||||
} else {
|
||||
$this->assertInternalType( 'array', $data );
|
||||
|
||||
list( $parsed_host, $parsed_port, $parsed_socket, $parsed_is_ipv6 ) = $data;
|
||||
|
||||
$this->assertEquals( $host, $parsed_host );
|
||||
$this->assertEquals( $port, $parsed_port );
|
||||
$this->assertEquals( $socket, $parsed_socket );
|
||||
$this->assertEquals( $is_ipv6, $parsed_is_ipv6 );
|
||||
}
|
||||
}
|
||||
|
||||
public function parse_db_host_data_provider() {
|
||||
return array(
|
||||
array(
|
||||
'', // DB_HOST
|
||||
false, // Expect parse_db_host to bail for this hostname
|
||||
null, // Parsed host
|
||||
null, // Parsed port
|
||||
null, // Parsed socket
|
||||
false, // is_ipv6
|
||||
),
|
||||
array(
|
||||
':3306',
|
||||
false,
|
||||
null,
|
||||
'3306',
|
||||
null,
|
||||
false,
|
||||
),
|
||||
array(
|
||||
':/tmp/mysql.sock',
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
'/tmp/mysql.sock',
|
||||
false,
|
||||
),
|
||||
array(
|
||||
'127.0.0.1',
|
||||
false,
|
||||
'127.0.0.1',
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
),
|
||||
array(
|
||||
'127.0.0.1:3306',
|
||||
false,
|
||||
'127.0.0.1',
|
||||
'3306',
|
||||
null,
|
||||
false,
|
||||
),
|
||||
array(
|
||||
'example.com',
|
||||
false,
|
||||
'example.com',
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
),
|
||||
array(
|
||||
'example.com:3306',
|
||||
false,
|
||||
'example.com',
|
||||
'3306',
|
||||
null,
|
||||
false,
|
||||
),
|
||||
array(
|
||||
'localhost',
|
||||
false,
|
||||
'localhost',
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
),
|
||||
array(
|
||||
'localhost:/tmp/mysql.sock',
|
||||
false,
|
||||
'localhost',
|
||||
null,
|
||||
'/tmp/mysql.sock',
|
||||
false,
|
||||
),
|
||||
array(
|
||||
'0000:0000:0000:0000:0000:0000:0000:0001',
|
||||
false,
|
||||
'0000:0000:0000:0000:0000:0000:0000:0001',
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'::1',
|
||||
false,
|
||||
'::1',
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'[::1]',
|
||||
false,
|
||||
'::1',
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'[::1]:3306',
|
||||
false,
|
||||
'::1',
|
||||
'3306',
|
||||
null,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'2001:0db8:0000:0000:0000:ff00:0042:8329',
|
||||
false,
|
||||
'2001:0db8:0000:0000:0000:ff00:0042:8329',
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'2001:db8:0:0:0:ff00:42:8329',
|
||||
false,
|
||||
'2001:db8:0:0:0:ff00:42:8329',
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'2001:db8::ff00:42:8329',
|
||||
false,
|
||||
'2001:db8::ff00:42:8329',
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'?::',
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue