diff --git a/src/Attribute/BuildsPDOPoolConnection.php b/src/Attribute/BuildsPDOPoolConnection.php new file mode 100644 index 0000000000000000000000000000000000000000..ad0686af34b6f9492f0e54badd2836dbfbc93b59 --- /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 0bfa457dc1e3ff67bcc681fc37861d79012b6d42..7217bb42cc7247b8189fb94c1ca9cf9024bd5cbf 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 0000000000000000000000000000000000000000..10d63ec6e71af8f0468e848d0f003d6e128e7426 --- /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 0000000000000000000000000000000000000000..f2a29644e7dd914c75bda27a9818dfa0bd982596 --- /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 0000000000000000000000000000000000000000..1e4bdc213ba624202aedf174abeea2be746db823 --- /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 769b7b680d8d9e68161b981a4ce9a0a5561dca95..33d2aa227dfa7a52f23b8df1113eb808dcc324ff 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 5751076c9f82f2c1603c4ca300132256d8cfca7e..b3a096c6f52bf7fbfd2763f19f5ba629d3fe6dd1 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 0000000000000000000000000000000000000000..740218972742e8eeb45291c64d0f40f7158b4d40 --- /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, + ); + } +}