Config: Updated DB host to handle ipv6

Can be set via the square bracket format.
For #5464
This commit is contained in:
Dan Brown 2025-03-15 20:32:57 +00:00
parent 94b1cffa2d
commit 4f5ad171ac
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
4 changed files with 44 additions and 14 deletions

View File

@ -56,6 +56,7 @@ APP_PROXIES=null
# Database details # Database details
# Host can contain a port (localhost:3306) or a separate DB_PORT option can be used. # Host can contain a port (localhost:3306) or a separate DB_PORT option can be used.
# An ipv6 address can be used via the square bracket format ([::1]).
DB_HOST=localhost DB_HOST=localhost
DB_PORT=3306 DB_PORT=3306
DB_DATABASE=database_database DB_DATABASE=database_database

View File

@ -40,12 +40,16 @@ if (env('REDIS_SERVERS', false)) {
// MYSQL // MYSQL
// Split out port from host if set // Split out port from host if set
$mysql_host = env('DB_HOST', 'localhost'); $mysqlHost = env('DB_HOST', 'localhost');
$mysql_host_exploded = explode(':', $mysql_host); $mysqlHostExploded = explode(':', $mysqlHost);
$mysql_port = env('DB_PORT', 3306); $mysqlPort = env('DB_PORT', 3306);
if (count($mysql_host_exploded) > 1) { $mysqlHostIpv6 = str_starts_with($mysqlHost, '[');
$mysql_host = $mysql_host_exploded[0]; if ($mysqlHostIpv6 && str_contains($mysqlHost, ']:')) {
$mysql_port = intval($mysql_host_exploded[1]); $mysqlHost = implode(':', array_slice($mysqlHostExploded, 0, -1));
$mysqlPort = intval(end($mysqlHostExploded));
} else if (!$mysqlHostIpv6 && count($mysqlHostExploded) > 1) {
$mysqlHost = $mysqlHostExploded[0];
$mysqlPort = intval($mysqlHostExploded[1]);
} }
return [ return [
@ -61,12 +65,12 @@ return [
'mysql' => [ 'mysql' => [
'driver' => 'mysql', 'driver' => 'mysql',
'url' => env('DATABASE_URL'), 'url' => env('DATABASE_URL'),
'host' => $mysql_host, 'host' => $mysqlHost,
'database' => env('DB_DATABASE', 'forge'), 'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'), 'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''), 'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''), 'unix_socket' => env('DB_SOCKET', ''),
'port' => $mysql_port, 'port' => $mysqlPort,
'charset' => 'utf8mb4', 'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci', 'collation' => 'utf8mb4_unicode_ci',
// Prefixes are only semi-supported and may be unstable // Prefixes are only semi-supported and may be unstable
@ -88,7 +92,7 @@ return [
'database' => 'bookstack-test', 'database' => 'bookstack-test',
'username' => env('MYSQL_USER', 'bookstack-test'), 'username' => env('MYSQL_USER', 'bookstack-test'),
'password' => env('MYSQL_PASSWORD', 'bookstack-test'), 'password' => env('MYSQL_PASSWORD', 'bookstack-test'),
'port' => $mysql_port, 'port' => $mysqlPort,
'charset' => 'utf8mb4', 'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci', 'collation' => 'utf8mb4_unicode_ci',
'prefix' => '', 'prefix' => '',

View File

@ -118,7 +118,7 @@ abstract class TestCase extends BaseTestCase
* Database config is juggled so the value can be restored when * Database config is juggled so the value can be restored when
* parallel testing are used, where multiple databases exist. * parallel testing are used, where multiple databases exist.
*/ */
protected function runWithEnv(string $name, $value, callable $callback) protected function runWithEnv(string $name, $value, callable $callback, bool $handleDatabase = true)
{ {
Env::disablePutenv(); Env::disablePutenv();
$originalVal = $_SERVER[$name] ?? null; $originalVal = $_SERVER[$name] ?? null;
@ -132,13 +132,17 @@ abstract class TestCase extends BaseTestCase
$database = config('database.connections.mysql_testing.database'); $database = config('database.connections.mysql_testing.database');
$this->refreshApplication(); $this->refreshApplication();
DB::purge(); if ($handleDatabase) {
config()->set('database.connections.mysql_testing.database', $database); DB::purge();
DB::beginTransaction(); config()->set('database.connections.mysql_testing.database', $database);
DB::beginTransaction();
}
$callback(); $callback();
DB::rollBack(); if ($handleDatabase) {
DB::rollBack();
}
if (is_null($originalVal)) { if (is_null($originalVal)) {
unset($_SERVER[$name]); unset($_SERVER[$name]);

View File

@ -160,6 +160,27 @@ class ConfigTest extends TestCase
$this->assertTrue($isMailTlsRequired()); $this->assertTrue($isMailTlsRequired());
} }
public function test_mysql_host_parsed_as_expected()
{
$cases = [
'127.0.0.1' => ['127.0.0.1', 3306],
'127.0.0.1:3307' => ['127.0.0.1', 3307],
'a.example.com' => ['a.example.com', 3306],
'a.example.com:3307' => ['a.example.com', 3307],
'[::1]' => ['[::1]', 3306],
'[::1]:123' => ['[::1]', 123],
'[2001:db8:3c4d:0015:0000:0000:1a2f]' => ['[2001:db8:3c4d:0015:0000:0000:1a2f]', 3306],
'[2001:db8:3c4d:0015:0000:0000:1a2f]:4567' => ['[2001:db8:3c4d:0015:0000:0000:1a2f]', 4567],
];
foreach ($cases as $host => [$expectedHost, $expectedPort]) {
$this->runWithEnv("DB_HOST", $host, function () use ($expectedHost, $expectedPort) {
$this->assertEquals($expectedHost, config("database.connections.mysql.host"));
$this->assertEquals($expectedPort, config("database.connections.mysql.port"));
}, false);
}
}
/** /**
* Set an environment variable of the given name and value * Set an environment variable of the given name and value
* then check the given config key to see if it matches the given result. * then check the given config key to see if it matches the given result.