From 02834366541b6d48728fa836dd1d611f177d310c Mon Sep 17 00:00:00 2001 From: Mathieu TUDISCO Date: Tue, 23 Apr 2019 10:38:26 +0200 Subject: [PATCH 1/6] Add Database Url Parser with tests. --- src/Illuminate/Database/UrlParser.php | 152 ++++++++++ tests/Database/DatabaseUrlParserTest.php | 339 +++++++++++++++++++++++ 2 files changed, 491 insertions(+) create mode 100644 src/Illuminate/Database/UrlParser.php create mode 100644 tests/Database/DatabaseUrlParserTest.php diff --git a/src/Illuminate/Database/UrlParser.php b/src/Illuminate/Database/UrlParser.php new file mode 100644 index 000000000000..8818d6a5bb9e --- /dev/null +++ b/src/Illuminate/Database/UrlParser.php @@ -0,0 +1,152 @@ + 'sqlsrv', + 'mysql2' => 'mysql', // Amazon RDS, for some weird reason + 'postgres' => 'pgsql', + 'postgresql' => 'pgsql', + 'sqlite3' => 'sqlite', + ]; + + /** + * @var array + */ + private $parsedUrl; + + public static function getDriverAliases(): array + { + return self::$driverAliases; + } + + public static function addDriverAlias(string $alias, string $driver) + { + self::$driverAliases[$alias] = $driver; + } + + /** + * @param array|string $config + * + * @return array + */ + public function parseDatabaseConfigWithUrl($config): array + { + if (is_string($config)) { + $config = ['url' => $config]; + } + + $url = $config['url'] ?? null; + $config = Arr::except($config, 'url'); + + if (!$url) { + return $config; + } + + $this->parsedUrl = $this->parseUrl($url); + + return array_merge( + $config, + $this->getMainAttributes(), + $this->getOtherOptions() + ); + } + + private function parseUrl(string $url): array + { + // sqlite3?:///... => sqlite3?://null/... or else the URL will be invalid + $url = preg_replace('#^(sqlite3?):///#', '$1://null/', $url); + + $parsedUrl = parse_url($url); + + if ($parsedUrl === false) { + throw new \InvalidArgumentException('Malformed parameter "url".'); + } + + return $this->parseStringsToNativeTypes(array_map('rawurldecode', $parsedUrl)); + } + + private function parseStringsToNativeTypes($value) + { + if (is_array($value)) { + return array_map([$this, 'parseStringsToNativeTypes'], $value); + } + + if (!is_string($value)) { + return $value; + } + + $parsedValue = json_decode($value, true); + + if (json_last_error() === JSON_ERROR_NONE) { + return $parsedValue; + } + + return $value; + } + + private function getMainAttributes(): array + { + return array_filter([ + 'driver' => $this->getDriver(), + 'database' => $this->getNormalizedPath(), + 'host' => $this->getInUrl('host'), + 'port' => $this->getInUrl('port'), + 'username' => $this->getInUrl('user'), + 'password' => $this->getInUrl('pass'), + ], function ($value) { + return $value !== null; + }); + } + + private function getDriver(): ?string + { + $alias = $this->getInUrl('scheme'); + + if (!$alias) { + return null; + } + + return self::$driverAliases[$alias] ?? $alias; + } + + private function getInUrl(string $key): ?string + { + return $this->parsedUrl[$key] ?? null; + } + + private function getNormalizedPath(): ?string + { + $path = $this->getInUrl('path'); + + if (!$path) { + return null; + } + + return substr($path, 1); + } + + private function getOtherOptions(): array + { + $queryString = $this->getInUrl('query'); + + if (!$queryString) { + return []; + } + + $query = []; + + parse_str($queryString, $query); + + return $this->parseStringsToNativeTypes($query); + } +} diff --git a/tests/Database/DatabaseUrlParserTest.php b/tests/Database/DatabaseUrlParserTest.php new file mode 100644 index 000000000000..1396e4e0eeda --- /dev/null +++ b/tests/Database/DatabaseUrlParserTest.php @@ -0,0 +1,339 @@ +assertEquals($expectedOutput, (new UrlParser)->parseDatabaseConfigWithUrl($config)); + } + + public function testDriversAliases() + { + $this->assertEquals([ + 'mssql' => 'sqlsrv', + 'mysql2' => 'mysql', + 'postgres' => 'pgsql', + 'postgresql' => 'pgsql', + 'sqlite3' => 'sqlite', + ], UrlParser::getDriverAliases()); + + UrlParser::addDriverAlias('some-particular-alias', 'mysql'); + + $this->assertEquals([ + 'mssql' => 'sqlsrv', + 'mysql2' => 'mysql', + 'postgres' => 'pgsql', + 'postgresql' => 'pgsql', + 'sqlite3' => 'sqlite', + 'some-particular-alias' => 'mysql', + ], UrlParser::getDriverAliases()); + + $this->assertEquals([ + 'driver' => 'mysql', + ], (new UrlParser)->parseDatabaseConfigWithUrl('some-particular-alias://null')); + } + + public function databaseUrls() + { + return [ + 'simple URL' => [ + 'mysql://foo:bar@localhost/baz', + [ + 'driver' => 'mysql', + 'username' => 'foo', + 'password' => 'bar', + 'host' => 'localhost', + 'database' => 'baz', + ], + ], + 'simple URL with port' => [ + 'mysql://foo:bar@localhost:134/baz', + [ + 'driver' => 'mysql', + 'username' => 'foo', + 'password' => 'bar', + 'host' => 'localhost', + 'port' => 134, + 'database' => 'baz', + ], + ], + 'sqlite relative URL with host' => [ + 'sqlite://localhost/foo/database.sqlite', + [ + 'database' => 'foo/database.sqlite', + 'driver' => 'sqlite', + 'host' => 'localhost', + ], + ], + 'sqlite absolute URL with host' => [ + 'sqlite://localhost//tmp/database.sqlite', + [ + 'database' => '/tmp/database.sqlite', + 'driver' => 'sqlite', + 'host' => 'localhost', + ], + ], + 'sqlite relative URL without host' => [ + 'sqlite:///foo/database.sqlite', + [ + 'database' => 'foo/database.sqlite', + 'driver' => 'sqlite', + ], + ], + 'sqlite absolute URL without host' => [ + 'sqlite:////tmp/database.sqlite', + [ + 'database' => '/tmp/database.sqlite', + 'driver' => 'sqlite', + ], + ], + 'sqlite memory' => [ + 'sqlite:///:memory:', + [ + 'database' => ':memory:', + 'driver' => 'sqlite', + ], + ], + 'params parsed from URL override individual params' => [ + [ + 'url' => 'mysql://foo:bar@localhost/baz', + 'password' => 'lulz', + 'driver' => 'sqlite', + ], + [ + 'username' => 'foo', + 'password' => 'bar', + 'host' => 'localhost', + 'database' => 'baz', + 'driver' => 'mysql', + ], + ], + 'params not parsed from URL but individual params are preserved' => [ + [ + 'url' => 'mysql://foo:bar@localhost/baz', + 'port' => 134, + ], + [ + 'username' => 'foo', + 'password' => 'bar', + 'host' => 'localhost', + 'port' => 134, + 'database' => 'baz', + 'driver' => 'mysql', + ], + ], + 'query params from URL are used as extra params' => [ + 'url' => 'mysql://foo:bar@localhost/database?charset=UTF-8', + [ + 'driver' => 'mysql', + 'database' => 'database', + 'host' => 'localhost', + 'username' => 'foo', + 'password' => 'bar', + 'charset' => 'UTF-8', + ], + ], + 'simple URL with driver set apart' => [ + [ + 'url' => '//foo:bar@localhost/baz', + 'driver' => 'sqlsrv', + ], + [ + 'username' => 'foo', + 'password' => 'bar', + 'host' => 'localhost', + 'database' => 'baz', + 'driver' => 'sqlsrv', + ], + ], + 'simple URL with percent encoding' => [ + 'mysql://foo%3A:bar%2F@localhost/baz+baz%40', + [ + 'username' => 'foo:', + 'password' => 'bar/', + 'host' => 'localhost', + 'database' => 'baz+baz@', + 'driver' => 'mysql', + ], + ], + 'simple URL with percent sign in password' => [ + 'mysql://foo:bar%25bar@localhost/baz', + [ + 'username' => 'foo', + 'password' => 'bar%bar', + 'host' => 'localhost', + 'database' => 'baz', + 'driver' => 'mysql', + ], + ], + 'URL with mssql alias driver' => [ + 'mssql://null', + [ + 'driver' => 'sqlsrv', + ], + ], + 'URL with sqlsrv alias driver' => [ + 'sqlsrv://null', + [ + 'driver' => 'sqlsrv', + ], + ], + 'URL with mysql alias driver' => [ + 'mysql://null', + [ + 'driver' => 'mysql', + ], + ], + 'URL with mysql2 alias driver' => [ + 'mysql2://null', + [ + 'driver' => 'mysql', + ], + ], + 'URL with postgres alias driver' => [ + 'postgres://null', + [ + 'driver' => 'pgsql', + ], + ], + 'URL with postgresql alias driver' => [ + 'postgresql://null', + [ + 'driver' => 'pgsql', + ], + ], + 'URL with pgsql alias driver' => [ + 'pgsql://null', + [ + 'driver' => 'pgsql', + ], + ], + 'URL with sqlite alias driver' => [ + 'sqlite://null', + [ + 'driver' => 'sqlite', + ], + ], + 'URL with sqlite3 alias driver' => [ + 'sqlite3://null', + [ + 'driver' => 'sqlite', + ], + ], + + 'URL with unknown driver' => [ + 'foo://null', + [ + 'driver' => 'foo', + ], + ], + 'Sqlite with foreign_key_constraints' => [ + 'sqlite:////absolute/path/to/database.sqlite?foreign_key_constraints=true', + [ + 'driver' => 'sqlite', + 'database' => '/absolute/path/to/database.sqlite', + 'foreign_key_constraints' => true, + ], + ], + + 'Most complex example with read and write subarrays all in string' => [ + 'mysql://root:@null/database?read[host][]=192.168.1.1&write[host][]=196.168.1.2&sticky=true&charset=utf8mb4&collation=utf8mb4_unicode_ci&prefix=', + [ + 'read' => [ + 'host' => ['192.168.1.1'], + ], + 'write' => [ + 'host' => ['196.168.1.2'], + ], + 'sticky' => true, + 'driver' => 'mysql', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + ], + ], + + 'Full example from doc that prove that there isn\'t any Breaking Change' => [ + [ + 'driver' => 'mysql', + 'host' => '127.0.0.1', + 'port' => '3306', + 'database' => 'forge', + 'username' => 'forge', + 'password' => '', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => ['foo' => 'bar'], + ], + [ + 'driver' => 'mysql', + 'host' => '127.0.0.1', + 'port' => '3306', + 'database' => 'forge', + 'username' => 'forge', + 'password' => '', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => ['foo' => 'bar'], + ], + ], + + 'Full example from doc with url overwriting parameters' => [ + [ + 'url' => 'pgsql://root:pass@db/local', + 'driver' => 'mysql', + 'host' => '127.0.0.1', + 'port' => '3306', + 'database' => 'forge', + 'username' => 'forge', + 'password' => '', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => ['foo' => 'bar'], + ], + [ + 'driver' => 'pgsql', + 'host' => 'db', + 'port' => '3306', + 'database' => 'local', + 'username' => 'root', + 'password' => 'pass', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => ['foo' => 'bar'], + ], + ], + ]; + } +} From c91d6a515f68d7860d313627416a7158078830e3 Mon Sep 17 00:00:00 2001 From: Mathieu TUDISCO Date: Tue, 23 Apr 2019 13:10:10 +0200 Subject: [PATCH 2/6] Parse Database Url when creating connection. --- src/Illuminate/Database/DatabaseManager.php | 2 +- tests/Database/DatabaseConnectionFactoryTest.php | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Database/DatabaseManager.php b/src/Illuminate/Database/DatabaseManager.php index 41aef9c6c66f..5095b2ca7980 100755 --- a/src/Illuminate/Database/DatabaseManager.php +++ b/src/Illuminate/Database/DatabaseManager.php @@ -151,7 +151,7 @@ protected function configuration($name) throw new InvalidArgumentException("Database [{$name}] not configured."); } - return $config; + return $this->app->make(UrlParser::class)->parseDatabaseConfigWithUrl($config); } /** diff --git a/tests/Database/DatabaseConnectionFactoryTest.php b/tests/Database/DatabaseConnectionFactoryTest.php index 72c92dfa0006..966471f509c9 100755 --- a/tests/Database/DatabaseConnectionFactoryTest.php +++ b/tests/Database/DatabaseConnectionFactoryTest.php @@ -24,6 +24,10 @@ protected function setUp(): void 'database' => ':memory:', ]); + $this->db->addConnection([ + 'url' => 'sqlite:///:memory:', + ], 'url'); + $this->db->addConnection([ 'driver' => 'sqlite', 'read' => [ @@ -48,6 +52,8 @@ public function testConnectionCanBeCreated() $this->assertInstanceOf(PDO::class, $this->db->connection()->getReadPdo()); $this->assertInstanceOf(PDO::class, $this->db->connection('read_write')->getPdo()); $this->assertInstanceOf(PDO::class, $this->db->connection('read_write')->getReadPdo()); + $this->assertInstanceOf(PDO::class, $this->db->connection('url')->getPdo()); + $this->assertInstanceOf(PDO::class, $this->db->connection('url')->getReadPdo()); } public function testSingleConnectionNotCreatedUntilNeeded() @@ -105,9 +111,7 @@ public function testCustomConnectorsCanBeResolvedViaContainer() public function testSqliteForeignKeyConstraints() { $this->db->addConnection([ - 'driver' => 'sqlite', - 'database' => ':memory:', - 'foreign_key_constraints' => true, + 'url' => 'sqlite:///:memory:?foreign_key_constraints=true', ], 'constraints_set'); $this->assertEquals(0, $this->db->connection()->select('PRAGMA foreign_keys')[0]->foreign_keys); From ba8d1f47155d50777807f0836b68b40e401035a0 Mon Sep 17 00:00:00 2001 From: Mathieu TUDISCO Date: Tue, 23 Apr 2019 14:16:56 +0200 Subject: [PATCH 3/6] Apply styleCI. --- src/Illuminate/Database/UrlParser.php | 14 +++++++------- tests/Database/DatabaseUrlParserTest.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Database/UrlParser.php b/src/Illuminate/Database/UrlParser.php index 8818d6a5bb9e..91f2689a7510 100644 --- a/src/Illuminate/Database/UrlParser.php +++ b/src/Illuminate/Database/UrlParser.php @@ -2,12 +2,12 @@ namespace Illuminate\Database; -use Illuminate\Support\Arr; use function array_map; -use function array_merge; use function parse_str; use function parse_url; +use function array_merge; use function preg_replace; +use Illuminate\Support\Arr; class UrlParser { @@ -48,7 +48,7 @@ public function parseDatabaseConfigWithUrl($config): array $url = $config['url'] ?? null; $config = Arr::except($config, 'url'); - if (!$url) { + if (! $url) { return $config; } @@ -81,7 +81,7 @@ private function parseStringsToNativeTypes($value) return array_map([$this, 'parseStringsToNativeTypes'], $value); } - if (!is_string($value)) { + if (! is_string($value)) { return $value; } @@ -112,7 +112,7 @@ private function getDriver(): ?string { $alias = $this->getInUrl('scheme'); - if (!$alias) { + if (! $alias) { return null; } @@ -128,7 +128,7 @@ private function getNormalizedPath(): ?string { $path = $this->getInUrl('path'); - if (!$path) { + if (! $path) { return null; } @@ -139,7 +139,7 @@ private function getOtherOptions(): array { $queryString = $this->getInUrl('query'); - if (!$queryString) { + if (! $queryString) { return []; } diff --git a/tests/Database/DatabaseUrlParserTest.php b/tests/Database/DatabaseUrlParserTest.php index 1396e4e0eeda..c52b73114990 100644 --- a/tests/Database/DatabaseUrlParserTest.php +++ b/tests/Database/DatabaseUrlParserTest.php @@ -2,8 +2,8 @@ namespace Illuminate\Tests\Database; -use Illuminate\Database\UrlParser; use PHPUnit\Framework\TestCase; +use Illuminate\Database\UrlParser; class DatabaseUrlParserTest extends TestCase { From f7dc3f4e6a1fcb6fede971ab22427ff6d7cfa0ce Mon Sep 17 00:00:00 2001 From: Mathieu TUDISCO Date: Thu, 25 Apr 2019 08:56:09 +0200 Subject: [PATCH 4/6] Add some more tests. --- .../DatabaseConnectionFactoryTest.php | 51 +++++++++++++++---- tests/Database/DatabaseUrlParserTest.php | 4 +- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/tests/Database/DatabaseConnectionFactoryTest.php b/tests/Database/DatabaseConnectionFactoryTest.php index 966471f509c9..4204d767480b 100755 --- a/tests/Database/DatabaseConnectionFactoryTest.php +++ b/tests/Database/DatabaseConnectionFactoryTest.php @@ -13,6 +13,7 @@ class DatabaseConnectionFactoryTest extends TestCase { + /** @var DB */ protected $db; protected function setUp(): void @@ -48,17 +49,47 @@ protected function tearDown(): void public function testConnectionCanBeCreated() { - $this->assertInstanceOf(PDO::class, $this->db->connection()->getPdo()); - $this->assertInstanceOf(PDO::class, $this->db->connection()->getReadPdo()); - $this->assertInstanceOf(PDO::class, $this->db->connection('read_write')->getPdo()); - $this->assertInstanceOf(PDO::class, $this->db->connection('read_write')->getReadPdo()); - $this->assertInstanceOf(PDO::class, $this->db->connection('url')->getPdo()); - $this->assertInstanceOf(PDO::class, $this->db->connection('url')->getReadPdo()); + $this->assertInstanceOf(PDO::class, $this->db->getConnection()->getPdo()); + $this->assertInstanceOf(PDO::class, $this->db->getConnection()->getReadPdo()); + $this->assertInstanceOf(PDO::class, $this->db->getConnection('read_write')->getPdo()); + $this->assertInstanceOf(PDO::class, $this->db->getConnection('read_write')->getReadPdo()); + $this->assertInstanceOf(PDO::class, $this->db->getConnection('url')->getPdo()); + $this->assertInstanceOf(PDO::class, $this->db->getConnection('url')->getReadPdo()); + } + + public function testConnectionFromUrlHasProperConfig() + { + $this->db->addConnection([ + 'url' => 'mysql://root:pass@db/local?strict=true', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => false, + 'engine' => null, + ], 'url-config'); + + $this->assertEquals([ + 'name' => 'url-config', + 'driver' => 'mysql', + 'database' => 'local', + 'host' => 'db', + 'username' => 'root', + 'password' => 'pass', + 'unix_socket' => '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + ], $this->db->getConnection('url-config')->getConfig()); } public function testSingleConnectionNotCreatedUntilNeeded() { - $connection = $this->db->connection(); + $connection = $this->db->getConnection(); $pdo = new ReflectionProperty(get_class($connection), 'pdo'); $pdo->setAccessible(true); $readPdo = new ReflectionProperty(get_class($connection), 'readPdo'); @@ -70,7 +101,7 @@ public function testSingleConnectionNotCreatedUntilNeeded() public function testReadWriteConnectionsNotCreatedUntilNeeded() { - $connection = $this->db->connection('read_write'); + $connection = $this->db->getConnection('read_write'); $pdo = new ReflectionProperty(get_class($connection), 'pdo'); $pdo->setAccessible(true); $readPdo = new ReflectionProperty(get_class($connection), 'readPdo'); @@ -114,8 +145,8 @@ public function testSqliteForeignKeyConstraints() 'url' => 'sqlite:///:memory:?foreign_key_constraints=true', ], 'constraints_set'); - $this->assertEquals(0, $this->db->connection()->select('PRAGMA foreign_keys')[0]->foreign_keys); + $this->assertEquals(0, $this->db->getConnection()->select('PRAGMA foreign_keys')[0]->foreign_keys); - $this->assertEquals(1, $this->db->connection('constraints_set')->select('PRAGMA foreign_keys')[0]->foreign_keys); + $this->assertEquals(1, $this->db->getConnection('constraints_set')->select('PRAGMA foreign_keys')[0]->foreign_keys); } } diff --git a/tests/Database/DatabaseUrlParserTest.php b/tests/Database/DatabaseUrlParserTest.php index c52b73114990..27245af801b3 100644 --- a/tests/Database/DatabaseUrlParserTest.php +++ b/tests/Database/DatabaseUrlParserTest.php @@ -301,7 +301,7 @@ public function databaseUrls() 'Full example from doc with url overwriting parameters' => [ [ - 'url' => 'pgsql://root:pass@db/local', + 'url' => 'mysql://root:pass@db/local', 'driver' => 'mysql', 'host' => '127.0.0.1', 'port' => '3306', @@ -318,7 +318,7 @@ public function databaseUrls() 'options' => ['foo' => 'bar'], ], [ - 'driver' => 'pgsql', + 'driver' => 'mysql', 'host' => 'db', 'port' => '3306', 'database' => 'local', From 95d7e8250a728674962abeb2f09f0eebe31ac292 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Thu, 25 Apr 2019 16:12:16 +0200 Subject: [PATCH 5/6] Apply @driesvints suggestions from code review. Co-Authored-By: mathieutu --- src/Illuminate/Database/DatabaseManager.php | 4 +++- src/Illuminate/Database/UrlParser.php | 18 +++++++++--------- .../Database/DatabaseConnectionFactoryTest.php | 1 - 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Illuminate/Database/DatabaseManager.php b/src/Illuminate/Database/DatabaseManager.php index 5095b2ca7980..47e17a86e881 100755 --- a/src/Illuminate/Database/DatabaseManager.php +++ b/src/Illuminate/Database/DatabaseManager.php @@ -151,7 +151,9 @@ protected function configuration($name) throw new InvalidArgumentException("Database [{$name}] not configured."); } - return $this->app->make(UrlParser::class)->parseDatabaseConfigWithUrl($config); + $urlParser = new UrlParser; + + return $urlParser->parseDatabaseConfigWithUrl($config); } /** diff --git a/src/Illuminate/Database/UrlParser.php b/src/Illuminate/Database/UrlParser.php index 91f2689a7510..06b69be81c0d 100644 --- a/src/Illuminate/Database/UrlParser.php +++ b/src/Illuminate/Database/UrlParser.php @@ -22,14 +22,14 @@ class UrlParser /** * @var array */ - private $parsedUrl; + protected $parsedUrl; public static function getDriverAliases(): array { return self::$driverAliases; } - public static function addDriverAlias(string $alias, string $driver) + public static function addDriverAlias($alias, $driver) { self::$driverAliases[$alias] = $driver; } @@ -61,7 +61,7 @@ public function parseDatabaseConfigWithUrl($config): array ); } - private function parseUrl(string $url): array + protected function parseUrl($url): array { // sqlite3?:///... => sqlite3?://null/... or else the URL will be invalid $url = preg_replace('#^(sqlite3?):///#', '$1://null/', $url); @@ -75,7 +75,7 @@ private function parseUrl(string $url): array return $this->parseStringsToNativeTypes(array_map('rawurldecode', $parsedUrl)); } - private function parseStringsToNativeTypes($value) + protected function parseStringsToNativeTypes($value) { if (is_array($value)) { return array_map([$this, 'parseStringsToNativeTypes'], $value); @@ -94,7 +94,7 @@ private function parseStringsToNativeTypes($value) return $value; } - private function getMainAttributes(): array + protected function getMainAttributes(): array { return array_filter([ 'driver' => $this->getDriver(), @@ -108,7 +108,7 @@ private function getMainAttributes(): array }); } - private function getDriver(): ?string + protected function getDriver() { $alias = $this->getInUrl('scheme'); @@ -119,12 +119,12 @@ private function getDriver(): ?string return self::$driverAliases[$alias] ?? $alias; } - private function getInUrl(string $key): ?string + protected function getInUrl($key) { return $this->parsedUrl[$key] ?? null; } - private function getNormalizedPath(): ?string + protected function getNormalizedPath() { $path = $this->getInUrl('path'); @@ -135,7 +135,7 @@ private function getNormalizedPath(): ?string return substr($path, 1); } - private function getOtherOptions(): array + protected function getOtherOptions(): array { $queryString = $this->getInUrl('query'); diff --git a/tests/Database/DatabaseConnectionFactoryTest.php b/tests/Database/DatabaseConnectionFactoryTest.php index 4204d767480b..e03cf4ddb5ae 100755 --- a/tests/Database/DatabaseConnectionFactoryTest.php +++ b/tests/Database/DatabaseConnectionFactoryTest.php @@ -13,7 +13,6 @@ class DatabaseConnectionFactoryTest extends TestCase { - /** @var DB */ protected $db; protected function setUp(): void From 1b568d02e6e17e324e462a5259c67528767cbcbb Mon Sep 17 00:00:00 2001 From: Mathieu TUDISCO Date: Thu, 25 Apr 2019 18:17:50 +0200 Subject: [PATCH 6/6] Add docblocs and change self to static. --- src/Illuminate/Database/UrlParser.php | 76 ++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Database/UrlParser.php b/src/Illuminate/Database/UrlParser.php index 06b69be81c0d..9a1f15a9f391 100644 --- a/src/Illuminate/Database/UrlParser.php +++ b/src/Illuminate/Database/UrlParser.php @@ -8,10 +8,16 @@ use function array_merge; use function preg_replace; use Illuminate\Support\Arr; +use InvalidArgumentException; class UrlParser { - private static $driverAliases = [ + /** + * The drivers aliases map. + * + * @var array + */ + protected static $driverAliases = [ 'mssql' => 'sqlsrv', 'mysql2' => 'mysql', // Amazon RDS, for some weird reason 'postgres' => 'pgsql', @@ -20,23 +26,38 @@ class UrlParser ]; /** + * The different components of parsed url. + * * @var array */ protected $parsedUrl; + /** + * Get all of the current drivers aliases. + * + * @return array + */ public static function getDriverAliases(): array { - return self::$driverAliases; + return static::$driverAliases; } + /** + * Add the driver alias to the driver aliases array. + * + * @param string $alias + * @param string $driver + * @return void + */ public static function addDriverAlias($alias, $driver) { - self::$driverAliases[$alias] = $driver; + static::$driverAliases[$alias] = $driver; } /** - * @param array|string $config + * Transform the url string or config array with url key to a parsed classic config array. * + * @param array|string $config * @return array */ public function parseDatabaseConfigWithUrl($config): array @@ -61,6 +82,12 @@ public function parseDatabaseConfigWithUrl($config): array ); } + /** + * Decode the string url, to an array of all of its components. + * + * @param string $url + * @return array + */ protected function parseUrl($url): array { // sqlite3?:///... => sqlite3?://null/... or else the URL will be invalid @@ -69,12 +96,19 @@ protected function parseUrl($url): array $parsedUrl = parse_url($url); if ($parsedUrl === false) { - throw new \InvalidArgumentException('Malformed parameter "url".'); + throw new InvalidArgumentException('Malformed parameter "url".'); } return $this->parseStringsToNativeTypes(array_map('rawurldecode', $parsedUrl)); } + /** + * Convert string casted values to there native types. + * Ex: 'false' => false, '42' => 42, 'foo' => 'foo' + * + * @param string $url + * @return mixed + */ protected function parseStringsToNativeTypes($value) { if (is_array($value)) { @@ -94,11 +128,16 @@ protected function parseStringsToNativeTypes($value) return $value; } + /** + * Return the main attributes of the database connection config from url. + * + * @return array + */ protected function getMainAttributes(): array { return array_filter([ 'driver' => $this->getDriver(), - 'database' => $this->getNormalizedPath(), + 'database' => $this->getDatabase(), 'host' => $this->getInUrl('host'), 'port' => $this->getInUrl('port'), 'username' => $this->getInUrl('user'), @@ -108,6 +147,11 @@ protected function getMainAttributes(): array }); } + /** + * Find connection driver from url. + * + * @return string|null + */ protected function getDriver() { $alias = $this->getInUrl('scheme'); @@ -116,15 +160,26 @@ protected function getDriver() return null; } - return self::$driverAliases[$alias] ?? $alias; + return static::$driverAliases[$alias] ?? $alias; } + /** + * Get a component of the parsed url. + * + * @param string $key + * @return string|null + */ protected function getInUrl($key) { return $this->parsedUrl[$key] ?? null; } - protected function getNormalizedPath() + /** + * Find connection database from url. + * + * @return string|null + */ + protected function getDatabase() { $path = $this->getInUrl('path'); @@ -135,6 +190,11 @@ protected function getNormalizedPath() return substr($path, 1); } + /** + * Return all the options added to the url with query params. + * + * @return array + */ protected function getOtherOptions(): array { $queryString = $this->getInUrl('query');