Skip to content

Commit

Permalink
refactor(database): wrap PDO so it can be closed
Browse files Browse the repository at this point in the history
  • Loading branch information
innocenzi committed Dec 24, 2024
1 parent 44efff9 commit bd1c4f5
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 26 deletions.
1 change: 0 additions & 1 deletion src/Tempest/Database/src/DatabaseInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Tempest\Database;

use PDO;
use Tempest\Container\Container;
use Tempest\Container\Initializer;
use Tempest\Container\Singleton;
Expand Down
9 changes: 9 additions & 0 deletions src/Tempest/Database/src/Exceptions/ConnectionClosed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Tempest\Database\Exceptions;

final class ConnectionClosed extends DatabaseException
{
}
9 changes: 5 additions & 4 deletions src/Tempest/Database/src/GenericDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use BackedEnum;
use DateTimeInterface;
use PDO;
use PDO as GlobalPDO;
use PDOException;
use Tempest\Database\Exceptions\QueryException;
use Tempest\Database\Transactions\TransactionManager;
Expand All @@ -26,6 +26,7 @@ public function execute(Query $query): void

try {
$this->pdo
->getPdo()
->prepare($query->getSql())
->execute($bindings);
} catch (PDOException $pdoException) {
Expand All @@ -35,16 +36,16 @@ public function execute(Query $query): void

public function getLastInsertId(): Id
{
return new Id($this->pdo->lastInsertId());
return new Id($this->pdo->getPdo()->lastInsertId());
}

public function fetch(Query $query): array
{
$pdoQuery = $this->pdo->prepare($query->getSql());
$pdoQuery = $this->pdo->getPdo()->prepare($query->getSql());

$pdoQuery->execute($this->resolveBindings($query));

return $pdoQuery->fetchAll(PDO::FETCH_NAMED);
return $pdoQuery->fetchAll(GlobalPDO::FETCH_NAMED);
}

public function fetchFirst(Query $query): ?array
Expand Down
35 changes: 35 additions & 0 deletions src/Tempest/Database/src/PDO.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Tempest\Database;

use PDO as GlobalPDO;
use Tempest\Database\Exceptions\ConnectionClosed;

final class PDO
{
private function __construct(
private ?GlobalPDO $pdo,
) {
}

public static function create(string $dsn, ?string $username = null, ?string $password = null, ?array $options = []): self
{
return new self(new GlobalPDO($dsn, $username, $password, $options));
}

public function getPdo(): GlobalPDO
{
if ($this->pdo === null) {
throw new ConnectionClosed('The database connection is closed.');
}

return $this->pdo;
}

public function close(): void
{
$this->pdo = null;
}
}
23 changes: 7 additions & 16 deletions src/Tempest/Database/src/PDOInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,23 @@

namespace Tempest\Database;

use PDO;
use Tempest\Container\Container;
use Tempest\Container\Initializer;
use Tempest\Container\Singleton;

final class PDOInitializer implements Initializer
{
private static ?PDO $pdo = null;

#[Singleton]
public function initialize(Container $container): PDO
{
// Prevent multiple PDO connections to live on in memory while running tests
// TODO: need to improve
if (self::$pdo === null) {
$databaseConfig = $container->get(DatabaseConfig::class);

$connection = $databaseConfig->connection();
$databaseConfig = $container->get(DatabaseConfig::class);

self::$pdo = new PDO(
$connection->getDsn(),
$connection->getUsername(),
$connection->getPassword(),
);
}
$connection = $databaseConfig->connection();

return self::$pdo;
return PDO::create(
$connection->getDsn(),
$connection->getUsername(),
$connection->getPassword(),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace Tempest\Database\Transactions;

use PDO;
use Tempest\Database\Exceptions\CouldNotBeginTransaction;
use Tempest\Database\Exceptions\CouldNotCommitTransaction;
use Tempest\Database\Exceptions\CouldNotRollbackTransaction;
use Tempest\Database\PDO;

final class GenericTransactionManager implements TransactionManager
{
Expand All @@ -17,7 +17,7 @@ public function __construct(private PDO $pdo)

public function begin(): void
{
$transactionBegun = $this->pdo->beginTransaction();
$transactionBegun = $this->pdo->getPdo()->beginTransaction();

if (! $transactionBegun) {
throw new CouldNotBeginTransaction();
Expand All @@ -26,7 +26,7 @@ public function begin(): void

public function commit(): void
{
$transactionCommitted = $this->pdo->commit();
$transactionCommitted = $this->pdo->getPdo()->commit();

if (! $transactionCommitted) {
throw new CouldNotCommitTransaction();
Expand All @@ -35,7 +35,7 @@ public function commit(): void

public function rollback(): void
{
$transactionRolledBack = $this->pdo->rollBack();
$transactionRolledBack = $this->pdo->getPdo()->rollBack();

if (! $transactionRolledBack) {
throw new CouldNotRollbackTransaction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace Tempest\Database\Transactions;

use PDO;
use Tempest\Container\Container;
use Tempest\Container\Initializer;
use Tempest\Container\Singleton;
use Tempest\Database\PDO;

final readonly class TransactionManagerInitializer implements Initializer
{
Expand Down
3 changes: 3 additions & 0 deletions tests/Integration/Console/Components/TaskComponentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Tempest\Console\Components\Interactive\TaskComponent;
use Tempest\Console\Console;
use Tempest\Console\Terminal\Terminal;
use Tempest\Database\PDO;
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;

/**
Expand All @@ -20,6 +21,8 @@ protected function setUp(): void
{
parent::setUp();

$this->container->get(PDO::class)->close();

if (PHP_OS_FAMILY === 'Windows') {
$this->markTestSkipped('These tests require the pcntl extension, which is not available on Windows.');
}
Expand Down
15 changes: 15 additions & 0 deletions tests/Integration/Database/GenericDatabaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
namespace Tests\Tempest\Integration\Database;

use Exception;
use PDO;
use Tempest\Database\Database;
use Tempest\Database\Exceptions\ConnectionClosed;
use Tempest\Database\Migrations\CreateMigrationsTable;
use Tempest\Database\Migrations\Migration;
use Tempest\Database\PDO as DatabasePDO;
use Tests\Tempest\Fixtures\Migrations\CreateAuthorTable;
use Tests\Tempest\Fixtures\Modules\Books\Models\Author;
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
Expand Down Expand Up @@ -43,4 +46,16 @@ public function test_execute_with_fail_works_correctly(): void

$this->assertCount(0, Author::all());
}

public function test_pdo_close(): void
{
$this->expectException(ConnectionClosed::class);

$pdo = $this->container->get(DatabasePDO::class);

$this->assertInstanceOf(PDO::class, $pdo->getPdo());

$pdo->close();
$pdo->getPdo(); // this throws
}
}

0 comments on commit bd1c4f5

Please sign in to comment.