]> BookStack Code Mirror - bookstack/commitdiff
Config: Updated DB host to handle ipv6
authorDan Brown <redacted>
Sat, 15 Mar 2025 20:32:57 +0000 (20:32 +0000)
committerDan Brown <redacted>
Sat, 15 Mar 2025 20:32:57 +0000 (20:32 +0000)
Can be set via the square bracket format.
For #5464

.env.example.complete
app/Config/database.php
tests/TestCase.php
tests/Unit/ConfigTest.php

index 6b4936648f0484b6a2282b6d3309478672c9ba0c..25687aaac383ad75d3d821d1b47f8c2032924e20 100644 (file)
@@ -56,6 +56,7 @@ APP_PROXIES=null
 
 # Database details
 # 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_PORT=3306
 DB_DATABASE=database_database
index 59ac0f31bebc97c492a3ca323f98f3134bb49030..8d38a86dfed0b5444afc1d642a79d48bed6e7ebb 100644 (file)
@@ -40,12 +40,16 @@ if (env('REDIS_SERVERS', false)) {
 
 // MYSQL
 // Split out port from host if set
-$mysql_host = env('DB_HOST', 'localhost');
-$mysql_host_exploded = explode(':', $mysql_host);
-$mysql_port = env('DB_PORT', 3306);
-if (count($mysql_host_exploded) > 1) {
-    $mysql_host = $mysql_host_exploded[0];
-    $mysql_port = intval($mysql_host_exploded[1]);
+$mysqlHost = env('DB_HOST', 'localhost');
+$mysqlHostExploded = explode(':', $mysqlHost);
+$mysqlPort = env('DB_PORT', 3306);
+$mysqlHostIpv6 = str_starts_with($mysqlHost, '[');
+if ($mysqlHostIpv6 && str_contains($mysqlHost, ']:')) {
+    $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 [
@@ -61,12 +65,12 @@ return [
         'mysql' => [
             'driver'         => 'mysql',
             'url'            => env('DATABASE_URL'),
-            'host'           => $mysql_host,
+            'host'           => $mysqlHost,
             'database'       => env('DB_DATABASE', 'forge'),
             'username'       => env('DB_USERNAME', 'forge'),
             'password'       => env('DB_PASSWORD', ''),
             'unix_socket'    => env('DB_SOCKET', ''),
-            'port'           => $mysql_port,
+            'port'           => $mysqlPort,
             'charset'        => 'utf8mb4',
             'collation'      => 'utf8mb4_unicode_ci',
             // Prefixes are only semi-supported and may be unstable
@@ -88,7 +92,7 @@ return [
             'database'       => 'bookstack-test',
             'username'       => env('MYSQL_USER', 'bookstack-test'),
             'password'       => env('MYSQL_PASSWORD', 'bookstack-test'),
-            'port'           => $mysql_port,
+            'port'           => $mysqlPort,
             'charset'        => 'utf8mb4',
             'collation'      => 'utf8mb4_unicode_ci',
             'prefix'         => '',
index b63de307624201899ee8734b0ceabf4b770f9799..0fb899ea99eebf419fa93f92732c24e0594606c6 100644 (file)
@@ -118,7 +118,7 @@ abstract class TestCase extends BaseTestCase
      * Database config is juggled so the value can be restored when
      * 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();
         $originalVal = $_SERVER[$name] ?? null;
@@ -132,13 +132,17 @@ abstract class TestCase extends BaseTestCase
         $database = config('database.connections.mysql_testing.database');
         $this->refreshApplication();
 
-        DB::purge();
-        config()->set('database.connections.mysql_testing.database', $database);
-        DB::beginTransaction();
+        if ($handleDatabase) {
+            DB::purge();
+            config()->set('database.connections.mysql_testing.database', $database);
+            DB::beginTransaction();
+        }
 
         $callback();
 
-        DB::rollBack();
+        if ($handleDatabase) {
+            DB::rollBack();
+        }
 
         if (is_null($originalVal)) {
             unset($_SERVER[$name]);
index d5c74392ffcdd42eae0b95520facf521590c61f3..2883fddb8fc9a33f3a93cb2d944f408c761caccb 100644 (file)
@@ -160,6 +160,27 @@ class ConfigTest extends TestCase
         $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
      * then check the given config key to see if it matches the given result.