From a5bd834be18d4b85d58c27839a3d7b3d5601a050 Mon Sep 17 00:00:00 2001 From: Mateusz Charytoniuk <mateusz.charytoniuk@protonmail.com> Date: Thu, 8 Feb 2024 16:43:51 +0100 Subject: [PATCH] feat: pdo pool hooks --- src/Attribute/BuildsPDOPoolConnection.php | 19 +++++++ src/PDOPool.php | 42 ++++++++++----- src/PDOPoolConnectionBuilder.php | 7 +++ src/PDOPoolConnectionBuilderCollection.php | 45 ++++++++++++++++ src/PDOPoolConnectionBuilderInterface.php | 12 +++++ src/SingletonCollection.php | 1 + ...tabaseConnectionPoolRepositoryProvider.php | 5 +- ...oolConnectionBuilderCollectionProvider.php | 51 +++++++++++++++++++ 8 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 src/Attribute/BuildsPDOPoolConnection.php create mode 100644 src/PDOPoolConnectionBuilder.php create mode 100644 src/PDOPoolConnectionBuilderCollection.php create mode 100644 src/PDOPoolConnectionBuilderInterface.php create mode 100644 src/SingletonProvider/PDOPoolConnectionBuilderCollectionProvider.php diff --git a/src/Attribute/BuildsPDOPoolConnection.php b/src/Attribute/BuildsPDOPoolConnection.php new file mode 100644 index 00000000..ad0686af --- /dev/null +++ b/src/Attribute/BuildsPDOPoolConnection.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\Attribute; + +use Attribute; +use Distantmagic\Resonance\Attribute as BaseAttribute; + +#[Attribute(Attribute::TARGET_CLASS)] +readonly class BuildsPDOPoolConnection extends BaseAttribute +{ + /** + * @param non-empty-string $name + */ + public function __construct( + public string $name, + ) {} +} diff --git a/src/PDOPool.php b/src/PDOPool.php index 0bfa457d..7217bb42 100644 --- a/src/PDOPool.php +++ b/src/PDOPool.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Distantmagic\Resonance; +use Ds\Set; use PDO; use Swoole\ConnectionPool; use Swoole\Database\PDOConfig; @@ -14,9 +15,13 @@ use Swoole\Database\PDOProxy; */ class PDOPool extends ConnectionPool { + /** + * @param Set<PDOPoolConnectionBuilderInterface> $connectionBuilders + */ public function __construct( - private PDOConfig $config, + private Set $connectionBuilders, DatabaseConnectionPoolConfiguration $databaseConnectionPoolConfiguration, + private PDOConfig $config, ) { parent::__construct( $this->createConnection(...), @@ -38,21 +43,13 @@ class PDOPool extends ConnectionPool private function createConnection(): PDO { - $driver = $this->config->getDriver(); + $pdo = $this->createPDO(); - if ('sqlite' !== $driver) { - return new PDO( - $this->createDSN($driver), - $this->config->getUsername(), - $this->config->getPassword(), - $this->config->getOptions() - ); + foreach ($this->connectionBuilders as $connectionBuilder) { + $pdo = $connectionBuilder->buildPDOConnection($pdo); } - return new PDO(sprintf( - 'sqlite:%s', - $this->config->getDbname() - )); + return $pdo; } private function createDSN(string $driver): string @@ -87,4 +84,23 @@ class PDOPool extends ConnectionPool ), }; } + + private function createPDO(): PDO + { + $driver = $this->config->getDriver(); + + if ('sqlite' !== $driver) { + return new PDO( + $this->createDSN($driver), + $this->config->getUsername(), + $this->config->getPassword(), + $this->config->getOptions() + ); + } + + return new PDO(sprintf( + 'sqlite:%s', + $this->config->getDbname() + )); + } } diff --git a/src/PDOPoolConnectionBuilder.php b/src/PDOPoolConnectionBuilder.php new file mode 100644 index 00000000..10d63ec6 --- /dev/null +++ b/src/PDOPoolConnectionBuilder.php @@ -0,0 +1,7 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +abstract readonly class PDOPoolConnectionBuilder implements PDOPoolConnectionBuilderInterface {} diff --git a/src/PDOPoolConnectionBuilderCollection.php b/src/PDOPoolConnectionBuilderCollection.php new file mode 100644 index 00000000..f2a29644 --- /dev/null +++ b/src/PDOPoolConnectionBuilderCollection.php @@ -0,0 +1,45 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use Ds\Map; +use Ds\Set; + +readonly class PDOPoolConnectionBuilderCollection +{ + /** + * @var Map<non-empty-string,Set<PDOPoolConnectionBuilderInterface>> + */ + private Map $connectionBuilders; + + public function __construct() + { + $this->connectionBuilders = new Map(); + } + + /** + * @param non-empty-string $name + */ + public function addBuilder( + string $name, + PDOPoolConnectionBuilderInterface $pdoPoolConnectionBuilder, + ): void { + $this->getBuildersForConnection($name)->add($pdoPoolConnectionBuilder); + } + + /** + * @param non-empty-string $name + * + * @return Set<PDOPoolConnectionBuilderInterface> + */ + public function getBuildersForConnection(string $name): Set + { + if (!$this->connectionBuilders->hasKey($name)) { + $this->connectionBuilders->put($name, new Set()); + } + + return $this->connectionBuilders->get($name); + } +} diff --git a/src/PDOPoolConnectionBuilderInterface.php b/src/PDOPoolConnectionBuilderInterface.php new file mode 100644 index 00000000..1e4bdc21 --- /dev/null +++ b/src/PDOPoolConnectionBuilderInterface.php @@ -0,0 +1,12 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance; + +use PDO; + +interface PDOPoolConnectionBuilderInterface +{ + public function buildPDOConnection(PDO $pdo): PDO; +} diff --git a/src/SingletonCollection.php b/src/SingletonCollection.php index 769b7b68..33d2aa22 100644 --- a/src/SingletonCollection.php +++ b/src/SingletonCollection.php @@ -23,6 +23,7 @@ enum SingletonCollection implements SingletonCollectionInterface case OpenAPIRouteParameterExtractor; case OpenAPIRouteRequestBodyContentExtractor; case OpenAPIRouteSecurityRequirementExtractor; + case PDOPoolConnectionBuilder; case ServerPipeMessageHandler; case ServerTaskHandler; case SiteActionGate; diff --git a/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php b/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php index 5751076c..b3a096c6 100644 --- a/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php +++ b/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php @@ -8,6 +8,7 @@ use Distantmagic\Resonance\Attribute\Singleton; use Distantmagic\Resonance\DatabaseConfiguration; use Distantmagic\Resonance\DatabaseConnectionPoolRepository; use Distantmagic\Resonance\PDOPool; +use Distantmagic\Resonance\PDOPoolConnectionBuilderCollection; use Distantmagic\Resonance\PHPProjectFiles; use Distantmagic\Resonance\SingletonContainer; use Distantmagic\Resonance\SingletonProvider; @@ -21,6 +22,7 @@ final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonP { public function __construct( private DatabaseConfiguration $databaseConfiguration, + private PDOPoolConnectionBuilderCollection $pdoPoolConnectionBuilderCollection, ) {} public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): DatabaseConnectionPoolRepository @@ -45,8 +47,9 @@ final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonP $pdoConfig->withPassword($connectionPoolConfiguration->password); $pdoPool = new PDOPool( - $pdoConfig, + $this->pdoPoolConnectionBuilderCollection->getBuildersForConnection($name), $connectionPoolConfiguration, + $pdoConfig, ); if ($connectionPoolConfiguration->poolPrefill) { diff --git a/src/SingletonProvider/PDOPoolConnectionBuilderCollectionProvider.php b/src/SingletonProvider/PDOPoolConnectionBuilderCollectionProvider.php new file mode 100644 index 00000000..74021897 --- /dev/null +++ b/src/SingletonProvider/PDOPoolConnectionBuilderCollectionProvider.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +namespace Distantmagic\Resonance\SingletonProvider; + +use Distantmagic\Resonance\Attribute\BuildsPDOPoolConnection; +use Distantmagic\Resonance\Attribute\RequiresSingletonCollection; +use Distantmagic\Resonance\Attribute\Singleton; +use Distantmagic\Resonance\HttpResponderCollection; +use Distantmagic\Resonance\PDOPoolConnectionBuilderCollection; +use Distantmagic\Resonance\PDOPoolConnectionBuilderInterface; +use Distantmagic\Resonance\PHPProjectFiles; +use Distantmagic\Resonance\SingletonAttribute; +use Distantmagic\Resonance\SingletonCollection; +use Distantmagic\Resonance\SingletonContainer; +use Distantmagic\Resonance\SingletonProvider; + +/** + * @template-extends SingletonProvider<HttpResponderCollection> + */ +#[RequiresSingletonCollection(SingletonCollection::PDOPoolConnectionBuilder)] +#[Singleton(provides: PDOPoolConnectionBuilderCollection::class)] +final readonly class PDOPoolConnectionBuilderCollectionProvider extends SingletonProvider +{ + public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): PDOPoolConnectionBuilderCollection + { + $pdoPoolConnectionBuilderCollection = new PDOPoolConnectionBuilderCollection(); + + foreach ($this->collectBuilders($singletons) as $builderAttribute) { + $pdoPoolConnectionBuilderCollection->addBuilder( + $builderAttribute->attribute->name, + $builderAttribute->singleton + ); + } + + return $pdoPoolConnectionBuilderCollection; + } + + /** + * @return iterable<SingletonAttribute<PDOPoolConnectionBuilderInterface,BuildsPDOPoolConnection>> + */ + private function collectBuilders(SingletonContainer $singletons): iterable + { + return $this->collectAttributes( + $singletons, + PDOPoolConnectionBuilderInterface::class, + BuildsPDOPoolConnection::class, + ); + } +} -- GitLab