diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 21fe4042d..50981c3c6 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -6,10 +6,26 @@ final class ConfigProvider { + public 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' => [], + // ], ]; } @@ -17,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 new file mode 100644 index 000000000..454f8e570 --- /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 + * + * @phpstan-param ContainerInterface&ServiceManager $container + * @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 $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/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,