From 7ad36e1e4fbc97888e1a6cc681b10787ab8bdcc5 Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Fri, 26 Dec 2025 03:13:32 -0600 Subject: [PATCH 1/2] Adds final public const ConfigProvider::NAMED_ADAPTER_KEY for targeting the configuration key used by the AbstractAdapterInterfaceFactory Signed-off-by: Joey Smith Signed-off-by: Joey Smith --- src/ConfigProvider.php | 18 ++- .../AbstractAdapterInterfaceFactory.php | 129 ++++++++++++++++++ .../AdapterAbstractServiceFactory.php | 127 ----------------- src/Exception/ContainerException.php | 27 ++++ 4 files changed, 173 insertions(+), 128 deletions(-) create mode 100644 src/Container/AbstractAdapterInterfaceFactory.php delete mode 100644 src/Container/AdapterAbstractServiceFactory.php create mode 100644 src/Exception/ContainerException.php diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 21fe4042d..7468bf36c 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -6,10 +6,26 @@ final class ConfigProvider { + public final const NAMED_ADAPTER_KEY = 'adapters'; public function __invoke(): array { return [ - 'dependencies' => $this->getDependencies(), + 'dependencies' => $this->getDependencies(), + Adapter\AdapterInterface::class => $this->getConfig(), + ]; + } + + public function getConfig(): array + { + // supported configuration structure + return [ + // Adapter\Adapter::class => [], + // Adapter\AdapterInterface::class => [], + // self::NAMED_ADAPTER_KEY => [ + // Adapter\Adapter::class => [], + // Adapter\AdapterInterface::class => [], + // 'Custom\Name' => [], + // ], ]; } diff --git a/src/Container/AbstractAdapterInterfaceFactory.php b/src/Container/AbstractAdapterInterfaceFactory.php new file mode 100644 index 000000000..52e1b8521 --- /dev/null +++ b/src/Container/AbstractAdapterInterfaceFactory.php @@ -0,0 +1,129 @@ +getConfig($container); + + if ($config === []) { + return false; + } + + return isset($config[$requestedName]) + && is_array($config[$requestedName]) + && ! empty($config[$requestedName]); + } + + /** + * Create a DB adapter + * + * @param string $requestedName + */ + public function __invoke( + ContainerInterface&ServiceManager $container, + $requestedName, + ?array $options = null + ): AdapterInterface&Adapter { + /** @var string|null $driverClass */ + $driverClass = $this->config[$requestedName]['driver'] ?? null; + + if ($driverClass === null) { + throw ContainerException::forService( + $requestedName, + self::class, + 'no driver configured' + ); + } + + /** @var DriverInterface|PdoDriverInterface $driver */ + $driver = $container->build($driverClass, $this->config[$requestedName]); + /** @var PlatformInterface&Pgsql\AdapterPlatform $platform */ + $platform = $container->build(PlatformInterface::class, ['driver' => $driver]); + /** @var ResultSetInterface|null $resultSet */ + $resultSet = $container->has(ResultSetInterface::class) + ? $container->build(ResultSetInterface::class) + : null; + /** @var ProfilerInterface|null $profiler */ + $profiler = $container->has(ProfilerInterface::class) + ? $container->build(ProfilerInterface::class) + : null; + + return match (true) { + $resultSet !== null && $profiler !== null => new Adapter( + driver: $driver, + platform: $platform, + queryResultSetPrototype: $resultSet, + profiler: $profiler, + ), + $resultSet !== null => new Adapter( + driver: $driver, + platform: $platform, + queryResultSetPrototype: $resultSet, + ), + $profiler !== null => new Adapter( + driver: $driver, + platform: $platform, + profiler: $profiler, + ), + default => new Adapter( + driver: $driver, + platform: $platform, + ), + }; + } + + /** + * Get db configuration, if any + * todo: refactor to use PhpDb\ConfigProvider::NAMED_ADAPTER_KEY instead of hardcoding 'adapters' + */ + protected function getConfig(ContainerInterface $container): array + { + if ($this->config !== null) { + return $this->config; + } + + if (! $container->has('config')) { + $this->config = []; + return $this->config; + } + + $config = $container->get('config'); + $this->config = $config[AdapterInterface::class][ConfigProvider::NAMED_ADAPTER_KEY] ?? []; + return $this->config; + } +} diff --git a/src/Container/AdapterAbstractServiceFactory.php b/src/Container/AdapterAbstractServiceFactory.php deleted file mode 100644 index 6e1a9098a..000000000 --- a/src/Container/AdapterAbstractServiceFactory.php +++ /dev/null @@ -1,127 +0,0 @@ -getConfig($container); - if (empty($config)) { - return false; - } - - return isset($config[$requestedName]) - && is_array($config[$requestedName]) - && ! empty($config[$requestedName]); - } - - /** - * Create a DB adapter - * - * @param string $requestedName - */ - public function __invoke( - ContainerInterface $container, - $requestedName, - ?array $options = null - ): AdapterInterface { - $driverFactory = ($container->get(DriverInterfaceFactoryFactoryInterface::class))($container, $requestedName); - $driverInstance = $driverFactory::createFromConfig($container, $requestedName); - $platformFactory = ($container->get(PlatformInterfaceFactoryFactoryInterface::class))(); - - $hasResultSet = $container->has(ResultSetInterface::class); - $hasProfiler = $container->has(ProfilerInterface::class); - $hasBoth = $hasResultSet && $hasProfiler; - $hasNeither = ! $hasResultSet && ! $hasProfiler; - - return match (true) { - $hasNeither => new Adapter( - driver: $driverInstance, - platform: $platformFactory::fromDriver($driverInstance), - ), - $hasResultSet => new Adapter( - driver: $driverInstance, - platform: $platformFactory::fromDriver($driverInstance), - queryResultSetPrototype: $container->get(ResultSetInterface::class), - ), - $hasProfiler => new Adapter( - driver: $driverInstance, - platform: $platformFactory::fromDriver($driverInstance), - profiler: $container->get(ProfilerInterface::class), - ), - $hasBoth => new Adapter( - driver: $driverInstance, - platform: $platformFactory::fromDriver($driverInstance), - queryResultSetPrototype: $container->get(ResultSetInterface::class), - profiler: $container->get(ProfilerInterface::class), - ), - default => throw new ServiceNotCreatedException( - 'Cannot create Named Adapter: ' . $requestedName . ' due to invalid configuration.' - ), - }; - } - - /** - * Get db configuration, if any - */ - protected function getConfig(ContainerInterface $container): array - { - if ($this->config !== null) { - return $this->config; - } - - if (! $container->has('config')) { - $this->config = []; - return $this->config; - } - - $config = $container->get('config'); - if ( - ! isset($config['db']) - || ! is_array($config['db']) - ) { - $this->config = []; - return $this->config; - } - - $config = $config['db']; - if ( - ! isset($config['adapters']) - || ! is_array($config['adapters']) - ) { - $this->config = []; - return $this->config; - } - - $this->config = $config['adapters']; - return $this->config; - } -} diff --git a/src/Exception/ContainerException.php b/src/Exception/ContainerException.php new file mode 100644 index 000000000..0ae5d5b7f --- /dev/null +++ b/src/Exception/ContainerException.php @@ -0,0 +1,27 @@ + Date: Fri, 26 Dec 2025 04:07:49 -0600 Subject: [PATCH 2/2] QA fixes Signed-off-by: Joey Smith Signed-off-by: Joey Smith --- src/ConfigProvider.php | 4 +- .../AbstractAdapterInterfaceFactory.php | 6 +- ...ectionInterfaceFactoryFactoryInterface.php | 9 - ...DriverInterfaceFactoryFactoryInterface.php | 9 - src/Container/FactoryFactoryInterface.php | 10 - ...atformInterfaceFactoryFactoryInterface.php | 9 - src/Exception/ContainerException.php | 8 +- .../AbstractAdapterInterfaceFactoryTest.php | 102 +++++++++++ .../AdapterAbstractServiceFactoryTest.php | 172 ------------------ test/unit/ConfigProviderTest.php | 7 +- 10 files changed, 116 insertions(+), 220 deletions(-) delete mode 100644 src/Container/ConnectionInterfaceFactoryFactoryInterface.php delete mode 100644 src/Container/DriverInterfaceFactoryFactoryInterface.php delete mode 100644 src/Container/FactoryFactoryInterface.php delete mode 100644 src/Container/PlatformInterfaceFactoryFactoryInterface.php create mode 100644 test/unit/Adapter/Container/AbstractAdapterInterfaceFactoryTest.php delete mode 100644 test/unit/Adapter/Container/AdapterAbstractServiceFactoryTest.php diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 7468bf36c..50981c3c6 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -6,7 +6,7 @@ final class ConfigProvider { - public final const NAMED_ADAPTER_KEY = 'adapters'; + public const NAMED_ADAPTER_KEY = 'adapters'; public function __invoke(): array { return [ @@ -33,7 +33,7 @@ public function getDependencies(): array { return [ 'abstract_factories' => [ - Container\AdapterAbstractServiceFactory::class, + Container\AbstractAdapterInterfaceFactory::class, ], 'aliases' => [ Adapter\AdapterInterface::class => Adapter\Adapter::class, diff --git a/src/Container/AbstractAdapterInterfaceFactory.php b/src/Container/AbstractAdapterInterfaceFactory.php index 52e1b8521..454f8e570 100644 --- a/src/Container/AbstractAdapterInterfaceFactory.php +++ b/src/Container/AbstractAdapterInterfaceFactory.php @@ -15,7 +15,6 @@ use PhpDb\ConfigProvider; use PhpDb\Exception\ContainerException; use PhpDb\ResultSet\ResultSetInterface; -use PSpell\Config; use Psr\Container\ContainerInterface; use function is_array; @@ -52,10 +51,11 @@ public function canCreate(ContainerInterface $container, $requestedName): bool /** * Create a DB adapter * + * @phpstan-param ContainerInterface&ServiceManager $container * @param string $requestedName */ public function __invoke( - ContainerInterface&ServiceManager $container, + ContainerInterface|ServiceManager $container, $requestedName, ?array $options = null ): AdapterInterface&Adapter { @@ -72,7 +72,7 @@ public function __invoke( /** @var DriverInterface|PdoDriverInterface $driver */ $driver = $container->build($driverClass, $this->config[$requestedName]); - /** @var PlatformInterface&Pgsql\AdapterPlatform $platform */ + /** @var PlatformInterface $platform */ $platform = $container->build(PlatformInterface::class, ['driver' => $driver]); /** @var ResultSetInterface|null $resultSet */ $resultSet = $container->has(ResultSetInterface::class) diff --git a/src/Container/ConnectionInterfaceFactoryFactoryInterface.php b/src/Container/ConnectionInterfaceFactoryFactoryInterface.php deleted file mode 100644 index aeedefa9f..000000000 --- a/src/Container/ConnectionInterfaceFactoryFactoryInterface.php +++ /dev/null @@ -1,9 +0,0 @@ -getMockBuilder(PdoDriverInterface::class)->getMock(); + /** @var PlatformInterface&MockObject $platformMock */ + $platformMock = $this->getMockBuilder(PlatformInterface::class)->getMock(); + + $config = [ + 'abstract_factories' => [AbstractAdapterInterfaceFactory::class], + 'factories' => [ + PdoStubDriver::class => static function ( + ContainerInterface $container + ) use ($pdoDriverInterfaceMock): PdoDriverInterface { + return $pdoDriverInterfaceMock; + }, + PlatformInterface::class => static function ( + ContainerInterface $container + ) use ($platformMock): PlatformInterface { + return $platformMock; + }, + ], + ]; + + $this->serviceManager = new ServiceManager($config); + + $this->serviceManager->setService('config', [ + AdapterInterface::class => [ + 'adapters' => [ + 'PhpDb\Adapter\Writer' => [ + 'driver' => PdoStubDriver::class, + ], + 'PhpDb\Adapter\Reader' => [ + 'driver' => PdoStubDriver::class, + ], + ], + ], + ]); + } + + public static function providerValidService(): array + { + return [ + ['PhpDb\Adapter\Writer'], + ['PhpDb\Adapter\Reader'], + ]; + } + + public static function providerInvalidService(): array + { + return [ + ['PhpDb\Adapter\Unknown'], + ]; + } + + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[DataProvider('providerValidService')] + public function testValidService(string $service): void + { + $actual = $this->serviceManager->get($service); + self::assertInstanceOf(AdapterInterface::class, $actual); + } + + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[DataProvider('providerInvalidService')] + public function testInvalidService(string $service): void + { + $this->expectException(ServiceNotFoundException::class); + $this->serviceManager->get($service); + } +} diff --git a/test/unit/Adapter/Container/AdapterAbstractServiceFactoryTest.php b/test/unit/Adapter/Container/AdapterAbstractServiceFactoryTest.php deleted file mode 100644 index dd02af7af..000000000 --- a/test/unit/Adapter/Container/AdapterAbstractServiceFactoryTest.php +++ /dev/null @@ -1,172 +0,0 @@ -getMockBuilder(PdoDriverInterface::class)->getMock(); - - $config = [ - 'abstract_factories' => [AdapterAbstractServiceFactory::class], - 'aliases' => [ - ConnectionInterfaceFactoryFactoryInterface::class => 'ConnectionInterfaceFactoryFactory', - DriverInterfaceFactoryFactoryInterface::class => 'DriverInterfaceFactoryFactory', - PlatformInterfaceFactoryFactoryInterface::class => 'PlatformInterfaceFactoryFactory', - ], - 'factories' => [ - 'ConnectionInterfaceFactoryFactory' - => new class implements ConnectionInterfaceFactoryFactoryInterface - { - public function __invoke(): callable - { - return new class implements FactoryFactoryInterface { - public function __invoke(): callable - { - return new self(); - } - - public static function createFromConfig(): ConnectionWrapper - { - return new ConnectionWrapper(); - } - }; - } - }, - 'DriverInterfaceFactoryFactory' - => new class ($pdoDriverInterfaceMock) implements DriverInterfaceFactoryFactoryInterface - { - private static PdoDriverInterface $pdoDriverInterface; - - public function __construct(PdoDriverInterface $pdoDriverInterfaceMock) - { - static::$pdoDriverInterface = $pdoDriverInterfaceMock; - } - - public function __invoke(): callable - { - return new class (static::$pdoDriverInterface) implements FactoryFactoryInterface - { - private static PdoDriverInterface $pdoDriverInterface; - - public function __construct(PdoDriverInterface $pdoDriverInterface) - { - static::$pdoDriverInterface = $pdoDriverInterface; - } - - public function __invoke(): callable - { - return new self(static::$pdoDriverInterface); - } - - public static function createFromConfig(): PdoDriverInterface - { - return self::$pdoDriverInterface; - } - }; - } - }, - 'PlatformInterfaceFactoryFactory' - => function () { - return new class () implements PlatformInterfaceFactoryFactoryInterface - { - public function __invoke(): callable - { - return new class () implements FactoryFactoryInterface - { - public function __invoke(): callable - { - return new self(); - } - - public static function fromDriver(): Sql92 - { - return new Sql92(); - } - }; - } - }; - }, - ], - ]; - - $this->serviceManager = new ServiceManager($config); - - $this->serviceManager->setService('config', [ - 'db' => [ - 'adapters' => [ - 'PhpDb\Adapter\Writer' => [ - 'driver' => PdoStubDriver::class, - ], - 'PhpDb\Adapter\Reader' => [ - 'driver' => PdoStubDriver::class, - ], - ], - ], - ]); - } - - public static function providerValidService(): array - { - return [ - ['PhpDb\Adapter\Writer'], - ['PhpDb\Adapter\Reader'], - ]; - } - - public static function providerInvalidService(): array - { - return [ - ['PhpDb\Adapter\Unknown'], - ]; - } - - /** - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - #[DataProvider('providerValidService')] - public function testValidService(string $service): void - { - $actual = $this->serviceManager->get($service); - self::assertInstanceOf(AdapterInterface::class, $actual); - } - - /** - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - #[DataProvider('providerInvalidService')] - public function testInvalidService(string $service): void - { - $this->expectException(ServiceNotFoundException::class); - $this->serviceManager->get($service); - } -} diff --git a/test/unit/ConfigProviderTest.php b/test/unit/ConfigProviderTest.php index 7ea4e9d0a..fa0fa1798 100644 --- a/test/unit/ConfigProviderTest.php +++ b/test/unit/ConfigProviderTest.php @@ -6,16 +6,17 @@ use PhpDb\Adapter; use PhpDb\ConfigProvider; -use PhpDb\Container\AdapterAbstractServiceFactory; +use PhpDb\Container\AbstractAdapterInterfaceFactory; use PHPUnit\Framework\TestCase; class ConfigProviderTest extends TestCase { /** @phpstan-var array{'dependencies': array{abstract_factories: list, aliases: array}} */ private array $config = [ - 'dependencies' => [ + Adapter\AdapterInterface::class => [], + 'dependencies' => [ 'abstract_factories' => [ - AdapterAbstractServiceFactory::class, + AbstractAdapterInterfaceFactory::class, ], 'aliases' => [ Adapter\AdapterInterface::class => Adapter\Adapter::class,