Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/database/src/Config/DatabaseConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,52 @@

interface DatabaseConfig extends HasTag
{
/**
* PDO data source name connection string.
*/
public string $dsn {
get;
}

/**
* The naming strategy for database tables and columns.
*/
public NamingStrategy $namingStrategy {
get;
}

/**
* The database dialect (MySQL, PostgreSQL, SQLite).
*/
public DatabaseDialect $dialect {
get;
}

/**
* The database username for authentication.
*/
public ?string $username {
get;
}

/**
* The database password for authentication.
*/
public ?string $password {
get;
}

/**
* Whether to use persistent database connections.
*/
public bool $usePersistentConnection {
get;
}

/**
* PDO connection options built from configuration properties.
*/
public array $options {
get;
}
}
53 changes: 53 additions & 0 deletions packages/database/src/Config/MysqlConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Tempest\Database\Config;

use PDO;
use Pdo\Mysql;
use SensitiveParameter;
use Tempest\Database\Tables\NamingStrategy;
use Tempest\Database\Tables\PluralizedSnakeCaseStrategy;
Expand All @@ -24,6 +26,52 @@ final class MysqlConfig implements DatabaseConfig
get => DatabaseDialect::MYSQL;
}

public bool $usePersistentConnection {
get => $this->persistent;
}

public array $options {
get {
$options = [];

if ($this->persistent) {
$options[PDO::ATTR_PERSISTENT] = true;
}

if ($this->certificateAuthority !== null) {
$options[Mysql::ATTR_SSL_CA] = $this->certificateAuthority;
}

if ($this->verifyServerCertificate !== null) {
$options[Mysql::ATTR_SSL_VERIFY_SERVER_CERT] = $this->verifyServerCertificate;
}

if ($this->clientCertificate !== null) {
$options[Mysql::ATTR_SSL_CERT] = $this->clientCertificate;
}

if ($this->clientKey !== null) {
$options[Mysql::ATTR_SSL_KEY] = $this->clientKey;
}

return $options;
}
}

/**
* @param string $host The MySQL server hostname or IP address.
* @param string $port The MySQL server port number.
* @param string $username The MySQL username for authentication.
* @param string $password The MySQL password for authentication.
* @param string $database The database name to connect to.
* @param bool $persistent Whether to use persistent connections. Persistent connections are not closed at the end of the script and are cached for reuse when another script requests a connection using the same credentials.
* @param bool|null $verifyServerCertificate Whether to verify the server's SSL certificate. Set to false for self-signed certificates (not recommended for production).
* @param string|null $certificateAuthority Path to the SSL Certificate Authority (CA) file. Required for SSL/TLS connections to verify the server's certificate.
* @param string|null $clientCertificate Path to the client's SSL certificate file. Used for mutual TLS authentication.
* @param string|null $clientKey Path to the client's SSL private key file. Used for mutual TLS authentication.
* @param NamingStrategy $namingStrategy The naming strategy for database tables and columns.
* @param string|UnitEnum|null $tag An optional tag to identify this database configuration.
*/
public function __construct(
#[SensitiveParameter]
public string $host = 'localhost',
Expand All @@ -35,6 +83,11 @@ public function __construct(
public string $password = '',
#[SensitiveParameter]
public string $database = 'app',
public bool $persistent = false,
public ?bool $verifyServerCertificate = null,
public ?string $certificateAuthority = null,
public ?string $clientCertificate = null,
public ?string $clientKey = null,
public NamingStrategy $namingStrategy = new PluralizedSnakeCaseStrategy(),
public null|string|UnitEnum $tag = null,
) {}
Expand Down
28 changes: 28 additions & 0 deletions packages/database/src/Config/PostgresConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Tempest\Database\Config;

use PDO;
use SensitiveParameter;
use Tempest\Database\Tables\NamingStrategy;
use Tempest\Database\Tables\PluralizedSnakeCaseStrategy;
Expand All @@ -26,6 +27,32 @@ final class PostgresConfig implements DatabaseConfig
get => DatabaseDialect::POSTGRESQL;
}

public bool $usePersistentConnection {
get => $this->persistent;
}

public array $options {
get {
$options = [];

if ($this->persistent) {
$options[PDO::ATTR_PERSISTENT] = true;
}

return $options;
}
}

/**
* @param string $host The PostgreSQL server hostname or IP address.
* @param string $port The PostgreSQL server port number.
* @param string $username The PostgreSQL username for authentication.
* @param string $password The PostgreSQL password for authentication.
* @param string $database The database name to connect to.
* @param bool $persistent Whether to use persistent connections. Persistent connections are not closed at the end of the script and are cached for reuse when another script requests a connection using the same credentials.
* @param NamingStrategy $namingStrategy The naming strategy for database tables and columns.
* @param string|UnitEnum|null $tag An optional tag to identify this database configuration.
*/
public function __construct(
#[SensitiveParameter]
public string $host = '127.0.0.1',
Expand All @@ -37,6 +64,7 @@ public function __construct(
public string $password = '',
#[SensitiveParameter]
public string $database = 'app',
public bool $persistent = false,
public NamingStrategy $namingStrategy = new PluralizedSnakeCaseStrategy(),
public null|string|UnitEnum $tag = null,
) {}
Expand Down
24 changes: 24 additions & 0 deletions packages/database/src/Config/SQLiteConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Tempest\Database\Config;

use PDO;
use SensitiveParameter;
use Tempest\Database\Tables\NamingStrategy;
use Tempest\Database\Tables\PluralizedSnakeCaseStrategy;
Expand All @@ -30,9 +31,32 @@ final class SQLiteConfig implements DatabaseConfig
get => DatabaseDialect::SQLITE;
}

public bool $usePersistentConnection {
get => $this->persistent;
}

public array $options {
get {
$options = [];

if ($this->persistent) {
$options[PDO::ATTR_PERSISTENT] = true;
}

return $options;
}
}

/**
* @param string $path Path to the SQLite database file. Use ':memory:' for an in-memory database.
* @param bool $persistent Whether to use persistent connections. Persistent connections are not closed at the end of the script and are cached for reuse when another script requests a connection using the same credentials.
* @param NamingStrategy $namingStrategy The naming strategy for database tables and columns.
* @param string|UnitEnum|null $tag An optional tag to identify this database configuration.
*/
public function __construct(
#[SensitiveParameter]
public string $path = 'localhost',
public bool $persistent = false,
public NamingStrategy $namingStrategy = new PluralizedSnakeCaseStrategy(),
public null|string|UnitEnum $tag = null,
) {}
Expand Down
1 change: 1 addition & 0 deletions packages/database/src/Connection/PDOConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public function connect(): void
dsn: $this->config->dsn,
username: $this->config->username,
password: $this->config->password,
options: $this->config->options,
);
}
}
56 changes: 56 additions & 0 deletions packages/database/tests/Config/DatabaseConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Tempest\Database\Tests\Config;

use Generator;
use PDO;
use Pdo\Mysql;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -62,4 +64,58 @@ public static function provide_database_drivers(): Generator
'secret',
];
}

#[DataProvider('provide_database_drivers_with_options')]
#[Test]
public function driver_supports_pdo_options(DatabaseConfig $driver, array $expectedOptions): void
{
$this->assertSame($expectedOptions, $driver->options);
}

public static function provide_database_drivers_with_options(): Generator
{
yield 'mysql with SSL' => [
new MysqlConfig(
certificateAuthority: '/etc/ssl/certs/ca-certificates.crt',
persistent: true,
),
[
PDO::ATTR_PERSISTENT => true,
Mysql::ATTR_SSL_CA => '/etc/ssl/certs/ca-certificates.crt',
],
];

yield 'mysql with all SSL options' => [
new MysqlConfig(
certificateAuthority: '/etc/ssl/certs/ca-certificates.crt',
verifyServerCertificate: false,
clientCertificate: '/path/to/cert.pem',
clientKey: '/path/to/key.pem',
),
[
Mysql::ATTR_SSL_CA => '/etc/ssl/certs/ca-certificates.crt',
Mysql::ATTR_SSL_VERIFY_SERVER_CERT => false,
Mysql::ATTR_SSL_CERT => '/path/to/cert.pem',
Mysql::ATTR_SSL_KEY => '/path/to/key.pem',
],
];

yield 'postgresql with persistent' => [
new PostgresConfig(
persistent: true,
),
[
PDO::ATTR_PERSISTENT => true,
],
];

yield 'sqlite with persistent' => [
new SQLiteConfig(
persistent: true,
),
[
PDO::ATTR_PERSISTENT => true,
],
];
}
}