From cf673b48a24b3f6b4e1f5478b81c1c9521c2e6f1 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 10:44:35 +0900 Subject: [PATCH 01/24] Extract ConnectionLocatorFactory --- src/AuraSqlEnvModule.php | 2 ++ src/AuraSqlModule.php | 28 +------------------- src/ConnectionLocatorFactory.php | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 src/ConnectionLocatorFactory.php diff --git a/src/AuraSqlEnvModule.php b/src/AuraSqlEnvModule.php index 448b0cf..8e84a55 100644 --- a/src/AuraSqlEnvModule.php +++ b/src/AuraSqlEnvModule.php @@ -4,6 +4,7 @@ namespace Ray\AuraSqlModule; +use Aura\Sql\ConnectionLocatorInterface; use Ray\AuraSqlModule\Annotation\EnvAuth; use Ray\Di\AbstractModule; @@ -49,6 +50,7 @@ protected function configure(): void $this->bind()->annotatedWith('pdo_dsn')->toProvider(EnvAuthProvider::class, 'dsn'); $this->bind()->annotatedWith('pdo_user')->toProvider(EnvAuthProvider::class, 'user'); $this->bind()->annotatedWith('pdo_pass')->toProvider(EnvAuthProvider::class, 'password'); + $this->bind(ConnectionLocatorInterface::class)->toProvider(ConnectionLocatorProvider::class); $this->install(new AuraSqlModule('', '', '', $this->slave, $this->options)); } } diff --git a/src/AuraSqlModule.php b/src/AuraSqlModule.php index 035eae9..4ecee40 100644 --- a/src/AuraSqlModule.php +++ b/src/AuraSqlModule.php @@ -4,16 +4,13 @@ namespace Ray\AuraSqlModule; -use Aura\Sql\ConnectionLocator; use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerModule; use Ray\Di\AbstractModule; use Ray\Di\Scope; -use function explode; use function preg_match; -use function sprintf; class AuraSqlModule extends AbstractModule { @@ -70,30 +67,7 @@ private function configureSingleDsn(): void private function configureMasterSlaveDsn(): void { - $locator = new ConnectionLocator(); - $locator->setWrite('master', new Connection($this->dsn, $this->user, $this->password)); - $i = 1; - $slaves = explode(',', $this->slave); - foreach ($slaves as $slave) { - $slaveDsn = $this->changeHost($this->dsn, $slave); - $name = 'slave' . (string) $i++; - $locator->setRead($name, new Connection($slaveDsn, $this->user, $this->password)); - } - + $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave); $this->install(new AuraSqlReplicationModule($locator)); } - - private function changeHost(string $dsn, string $host): string - { - preg_match(self::PARSE_PDO_DSN_REGEX, $dsn, $parts); - if (! $parts) { - // @codeCoverageIgnoreStart - return $dsn; - // @codeCoverageIgnoreEnd - } - - $dsn = sprintf('%s:%s=%s;%s', $parts[1], $parts[2], $host, $parts[3]); - - return $dsn; - } } diff --git a/src/ConnectionLocatorFactory.php b/src/ConnectionLocatorFactory.php new file mode 100644 index 0000000..36008ad --- /dev/null +++ b/src/ConnectionLocatorFactory.php @@ -0,0 +1,45 @@ + new Connection($dsn, $user, $password)]; + $i = 1; + $slaves = explode(',', $slave); + $reads = []; + foreach ($slaves as $host) { + $slaveDsn = self::changeHost($dsn, $host); + $name = 'slave' . (string) $i++; + $reads[$name] = new Connection($slaveDsn, $user, $password); + } + + return new ConnectionLocator(null, $reads, $writes); + } + + private static function changeHost(string $dsn, string $host): string + { + preg_match(AuraSqlModule::PARSE_PDO_DSN_REGEX, $dsn, $parts); + if (! $parts) { + // @codeCoverageIgnoreStart + return $dsn; + // @codeCoverageIgnoreEnd + } + + return sprintf('%s:%s=%s;%s', $parts[1], $parts[2], $host, $parts[3]); + } +} From 10075da812524687a3965bf0ab9581fd34beed0a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 10:45:17 +0900 Subject: [PATCH 02/24] Remove redundant escape --- src/AuraSqlModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AuraSqlModule.php b/src/AuraSqlModule.php index 4ecee40..8cc8258 100644 --- a/src/AuraSqlModule.php +++ b/src/AuraSqlModule.php @@ -14,7 +14,7 @@ class AuraSqlModule extends AbstractModule { - public const PARSE_PDO_DSN_REGEX = '/(.*?)\:(?:(host|server)=.*?;)?(.*)/i'; + public const PARSE_PDO_DSN_REGEX = '/(.*?):(?:(host|server)=.*?;)?(.*)/i'; private string $dsn; private string $user; From 387adb1df8e60e66e329411fba33c56d85a2dc6d Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 10:58:30 +0900 Subject: [PATCH 03/24] Remove redundant module install TransactionalModule is insalled in AuraSqlModule! --- src/AuraSqlReplicationModule.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AuraSqlReplicationModule.php b/src/AuraSqlReplicationModule.php index dce34c7..682e967 100644 --- a/src/AuraSqlReplicationModule.php +++ b/src/AuraSqlReplicationModule.php @@ -36,8 +36,6 @@ protected function configure(): void $this->bind(ExtendedPdoInterface::class)->annotatedWith($this->qualifer)->toProvider(AuraSqlReplicationDbProvider::class, $this->qualifer)->in(Scope::SINGLETON); // @ReadOnlyConnection @WriteConnection $this->installReadWriteConnection(); - // @Transactional - $this->install(new TransactionalModule()); } protected function installReadWriteConnection(): void From 209401f0141965e1834df966b427ec9267a155e0 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 11:51:38 +0900 Subject: [PATCH 04/24] Change ConnectionLocator bound from toInstance to toProvider --- src/AuraSqlModule.php | 14 +++++-- src/ConnectionLocatorProvider.php | 64 +++++++++++++++++++++++++++++++ tests/AuraSqlModuleTest.php | 8 ++-- 3 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 src/ConnectionLocatorProvider.php diff --git a/src/AuraSqlModule.php b/src/AuraSqlModule.php index 8cc8258..9a4e520 100644 --- a/src/AuraSqlModule.php +++ b/src/AuraSqlModule.php @@ -4,6 +4,7 @@ namespace Ray\AuraSqlModule; +use Aura\Sql\ConnectionLocatorInterface; use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerModule; @@ -57,17 +58,24 @@ protected function configure(): void private function configureSingleDsn(): void { - $this->bind(ExtendedPdoInterface::class)->toConstructor(ExtendedPdo::class, 'dsn=pdo_dsn,username=pdo_user,password=pdo_pass,options=pdo_options,attributes=pdo_attributes')->in(Scope::SINGLETON); $this->bind()->annotatedWith('pdo_dsn')->toInstance($this->dsn); $this->bind()->annotatedWith('pdo_user')->toInstance($this->user); $this->bind()->annotatedWith('pdo_pass')->toInstance($this->password); + $this->bind()->annotatedWith('pdo_slave')->toInstance($this->slave); $this->bind()->annotatedWith('pdo_options')->toInstance($this->options); $this->bind()->annotatedWith('pdo_attributes')->toInstance($this->options); + $this->bind(ExtendedPdoInterface::class)->toConstructor(ExtendedPdo::class, 'dsn=pdo_dsn,username=pdo_user,password=pdo_pass,options=pdo_options,attributes=pdo_attributes')->in(Scope::SINGLETON); } private function configureMasterSlaveDsn(): void { - $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave); - $this->install(new AuraSqlReplicationModule($locator)); + $context = ''; + $this->bind()->annotatedWith('pdo_locator_dsn')->toInstance([$context => $this->dsn]); + $this->bind()->annotatedWith('pdo_locator_user')->toInstance([$context => $this->user]); + $this->bind()->annotatedWith('pdo_locator_pass')->toInstance([$context => $this->password]); + $this->bind()->annotatedWith('pdo_locator_slave')->toInstance([$context => $this->slave]); + $this->bind(ConnectionLocatorInterface::class)->annotatedWith($context)->toProvider(ConnectionLocatorProvider::class, $context); + // ReadOnlyConnection when GET, otherwise WriteConnection + $this->bind(ExtendedPdoInterface::class)->toProvider(AuraSqlReplicationDbProvider::class)->in(Scope::SINGLETON); } } diff --git a/src/ConnectionLocatorProvider.php b/src/ConnectionLocatorProvider.php new file mode 100644 index 0000000..f86786e --- /dev/null +++ b/src/ConnectionLocatorProvider.php @@ -0,0 +1,64 @@ + + */ +final class ConnectionLocatorProvider implements ProviderInterface, SetContextInterface +{ + /** @var array */ + private array $dsn; + + /** @var array */ + private array $user; + + /** @var array */ + private array $password; + + /** @var array */ + private array $slave; + private string $context; + + /** + * {@inheritDoc} + */ + public function setContext($context) + { + $this->context = $context; + } + + /** + * @param array $dsn + * @param array $user + * @param array $password + * @param array $slave + * + * @Named("dsn=pdo_locator_dsn,user=pdo_locator_user,password=pdo_locator_pass,slave=pdo_locator_slave") + */ + #[Named('dsn=pdo_locator_dsn,user=pdo_locator_user,password=pdo_locator_pass,slave=pdo_locator_slave')] + public function __construct(array $dsn, array $user, array $password, array $slave) + { + $this->dsn = $dsn; + $this->user = $user; + $this->password = $password; + $this->slave = $slave; + } + + public function get(): ConnectionLocatorInterface + { + return ConnectionLocatorFactory::newInstance( + $this->dsn[$this->context], + $this->user[$this->context], + $this->password[$this->context], + $this->slave[$this->context] + ); + } +} diff --git a/tests/AuraSqlModuleTest.php b/tests/AuraSqlModuleTest.php index 197adb6..638580e 100644 --- a/tests/AuraSqlModuleTest.php +++ b/tests/AuraSqlModuleTest.php @@ -10,8 +10,8 @@ use PHPUnit\Framework\TestCase; use Ray\Compiler\DiCompiler; use Ray\Compiler\ScriptInjector; +use Ray\Di\DependencyProvider; use Ray\Di\Injector; -use Ray\Di\Instance; use ReflectionProperty; use function assert; @@ -56,9 +56,9 @@ public function testSqlite() public function testSlaveModule() { $module = new AuraSqlModule('mysql:host=localhost;dbname=testdb', 'root', '', 'slave1,slave2'); - $instance = $module->getContainer()->getContainer()['Aura\Sql\ConnectionLocatorInterface-']; - assert($instance instanceof Instance); - $locator = $instance->value; + $provider = $module->getContainer()->getContainer()['Aura\Sql\ConnectionLocatorInterface-']; + assert($provider instanceof DependencyProvider); + $locator = $provider->inject($module->getContainer()); assert($locator instanceof ConnectionLocatorInterface); $this->assertInstanceOf(ConnectionLocatorInterface::class, $locator); $dsn = $this->getDsn($locator->getRead()); From fca4bbc4af54fa50fd994e3f1eb66e803959bbe4 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 13:30:40 +0900 Subject: [PATCH 05/24] Change locator and pdo scope --- src/AuraSqlModule.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AuraSqlModule.php b/src/AuraSqlModule.php index 9a4e520..eac25ac 100644 --- a/src/AuraSqlModule.php +++ b/src/AuraSqlModule.php @@ -74,8 +74,8 @@ private function configureMasterSlaveDsn(): void $this->bind()->annotatedWith('pdo_locator_user')->toInstance([$context => $this->user]); $this->bind()->annotatedWith('pdo_locator_pass')->toInstance([$context => $this->password]); $this->bind()->annotatedWith('pdo_locator_slave')->toInstance([$context => $this->slave]); - $this->bind(ConnectionLocatorInterface::class)->annotatedWith($context)->toProvider(ConnectionLocatorProvider::class, $context); + $this->bind(ConnectionLocatorInterface::class)->annotatedWith($context)->toProvider(ConnectionLocatorProvider::class, $context)->in(Scope::SINGLETON); // ReadOnlyConnection when GET, otherwise WriteConnection - $this->bind(ExtendedPdoInterface::class)->toProvider(AuraSqlReplicationDbProvider::class)->in(Scope::SINGLETON); + $this->bind(ExtendedPdoInterface::class)->toProvider(AuraSqlReplicationDbProvider::class); } } From 76f273d11fdfb4b86987d082010c573ae8d496e9 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 13:32:04 +0900 Subject: [PATCH 06/24] Bind slave env value --- src/AuraSqlEnvModule.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/AuraSqlEnvModule.php b/src/AuraSqlEnvModule.php index 8e84a55..76a1490 100644 --- a/src/AuraSqlEnvModule.php +++ b/src/AuraSqlEnvModule.php @@ -4,7 +4,6 @@ namespace Ray\AuraSqlModule; -use Aura\Sql\ConnectionLocatorInterface; use Ray\AuraSqlModule\Annotation\EnvAuth; use Ray\Di\AbstractModule; @@ -45,12 +44,14 @@ protected function configure(): void 'dsn' => $this->dsn, 'user' => $this->user, 'password' => $this->password, + 'slave' => $this->slave, ] ); $this->bind()->annotatedWith('pdo_dsn')->toProvider(EnvAuthProvider::class, 'dsn'); $this->bind()->annotatedWith('pdo_user')->toProvider(EnvAuthProvider::class, 'user'); $this->bind()->annotatedWith('pdo_pass')->toProvider(EnvAuthProvider::class, 'password'); - $this->bind(ConnectionLocatorInterface::class)->toProvider(ConnectionLocatorProvider::class); + $this->bind()->annotatedWith('pdo_slave')->toProvider(EnvAuthProvider::class, 'slave'); + $this->install(new AuraSqlModule('', '', '', $this->slave, $this->options)); } } From 94d3448ccd532d25df4592a4655c219e3074c69c Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 13:32:21 +0900 Subject: [PATCH 07/24] Add testReplicationModule test --- tests/AuraSqEnvlModuleTest.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/AuraSqEnvlModuleTest.php b/tests/AuraSqEnvlModuleTest.php index acba1cc..4991139 100644 --- a/tests/AuraSqEnvlModuleTest.php +++ b/tests/AuraSqEnvlModuleTest.php @@ -18,6 +18,8 @@ public function setUp(): void putenv('TEST_DSN=sqlite::memory:'); putenv('TEST_USER=user1'); putenv('TEST_PASSWORD=password1'); + // for log db + putenv('TEST_SLAVE=SLAVE1,SLAVE2'); } public function tearDown(): void @@ -25,9 +27,10 @@ public function tearDown(): void putenv('TEST_DSN'); putenv('TEST_USER'); putenv('TEST_PASSWORD'); + putenv('TEST_SLAVE'); } - public function testModule() + public function testSingleDbModule(): void { $module = new AuraSqlEnvModule('TEST_DSN', 'TEST_USER', 'TEST_PASSWORD'); $injector = new Injector($module, __DIR__ . '/tmp'); @@ -36,4 +39,14 @@ public function testModule() $this->assertSame('user1', $injector->getInstance('', 'pdo_user')); $this->assertSame('password1', $injector->getInstance('', 'pdo_pass')); } + + public function testReplicationModule(): void + { + $module = new AuraSqlEnvModule('TEST_DSN', 'TEST_USER', 'TEST_PASSWORD', 'TEST_SLAVE'); + $injector = new Injector($module, __DIR__ . '/tmp'); + $instance = $injector->getInstance(ExtendedPdoInterface::class); + $this->assertInstanceOf(ExtendedPdo::class, $instance); + $this->assertSame('user1', $injector->getInstance('', 'pdo_user')); + $this->assertSame('password1', $injector->getInstance('', 'pdo_pass')); + } } From a161d40d12e1fb9215a8d3ac44965b6f56241b50 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 13:48:52 +0900 Subject: [PATCH 08/24] Introduce ConnectionLocatorFactory into NamedPdoModule --- src/NamedPdoModule.php | 34 +------------------------ tests/Fake/FakeNamedQualifierModule.php | 2 +- tests/NamedPdoModuleTest.php | 2 +- 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/src/NamedPdoModule.php b/src/NamedPdoModule.php index dad2eaf..b05f2e5 100644 --- a/src/NamedPdoModule.php +++ b/src/NamedPdoModule.php @@ -4,15 +4,10 @@ namespace Ray\AuraSqlModule; -use Aura\Sql\ConnectionLocator; use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; use Ray\Di\AbstractModule; -use function explode; -use function preg_match; -use function sprintf; - class NamedPdoModule extends AbstractModule { public const PARSE_PDO_DSN_REGEX = '/(.*?)\:(host|server)=.*?;(.*)/i'; @@ -62,34 +57,7 @@ private function configureSingleDsn(string $qualifer, string $dsn, string $user, private function configureMasterSlaveDsn(string $qualifer, string $dsn, string $user, string $password, string $slaveList): void { - $locator = new ConnectionLocator(); - $locator->setWrite('master', new Connection($dsn, $user, $password)); - $i = 1; - $slaves = explode(',', $slaveList); - foreach ($slaves as $slave) { - $slaveDsn = $this->changeHost($dsn, $slave); - $name = 'slave' . (string) $i++; - $locator->setRead($name, new Connection($slaveDsn, $user, $password)); - } - + $locator = ConnectionLocatorFactory::newInstance($dsn, $user, $password, $slaveList); $this->install(new AuraSqlReplicationModule($locator, $qualifer)); } - - /** - * @param string $dsn - * @param string $host - * - * @return string - */ - private function changeHost($dsn, $host) - { - preg_match(self::PARSE_PDO_DSN_REGEX, $dsn, $parts); - if (! $parts) { - return $dsn; - } - - $dsn = sprintf('%s:%s=%s;%s', $parts[1], $parts[2], $host, $parts[3]); - - return $dsn; - } } diff --git a/tests/Fake/FakeNamedQualifierModule.php b/tests/Fake/FakeNamedQualifierModule.php index 886fbd2..79e512d 100644 --- a/tests/Fake/FakeNamedQualifierModule.php +++ b/tests/Fake/FakeNamedQualifierModule.php @@ -11,6 +11,6 @@ class FakeNamedQualifierModule extends AbstractModule */ protected function configure() { - $this->install(new NamedPdoModule('log_db', 'sqlite::memory:', '', '', 'slave1')); + $this->install(new NamedPdoModule('log_db', 'mysql:host=localhost;dbname=master', '', '', 'slave1')); } } diff --git a/tests/NamedPdoModuleTest.php b/tests/NamedPdoModuleTest.php index d9c99f9..c009553 100644 --- a/tests/NamedPdoModuleTest.php +++ b/tests/NamedPdoModuleTest.php @@ -55,7 +55,7 @@ public function testNoHost() $qualifer = 'log_db'; $instance = (new Injector(new FakeNamedQualifierModule(), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); /** @var ExtendedPdo $instance */ - $this->assertSame('sqlite::memory:', $this->getDsn($instance)); + $this->assertSame('mysql:host=slave1;dbname=master', $this->getDsn($instance)); } private function getDsn(ExtendedPdo $pdo): string From 983dee8d69bbffa9149e4e56594196c37a03586f Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 28 Sep 2022 14:01:12 +0900 Subject: [PATCH 09/24] Code coverage 100% --- src/ConnectionLocatorFactory.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ConnectionLocatorFactory.php b/src/ConnectionLocatorFactory.php index 36008ad..8204361 100644 --- a/src/ConnectionLocatorFactory.php +++ b/src/ConnectionLocatorFactory.php @@ -12,6 +12,9 @@ final class ConnectionLocatorFactory { + /** + * @codeCoverageIgnore + */ private function __construct() { } From fe9c0b36fa64a2827e84a244243e718f6bf30a8f Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 01:08:06 +0900 Subject: [PATCH 10/24] Use AuraSqlReplicationModule --- src/AuraSqlEnvModule.php | 8 ++++++-- src/AuraSqlModule.php | 11 ++--------- src/Connection.php | 23 +++++++++++++++-------- src/ConnectionLocatorFactory.php | 6 +++--- src/ConnectionLocatorProvider.php | 3 ++- src/NamedPdoModule.php | 27 +++++++++++++++------------ tests/AuraSqlModuleTest.php | 8 ++++---- 7 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/AuraSqlEnvModule.php b/src/AuraSqlEnvModule.php index 76a1490..5d4427d 100644 --- a/src/AuraSqlEnvModule.php +++ b/src/AuraSqlEnvModule.php @@ -44,7 +44,6 @@ protected function configure(): void 'dsn' => $this->dsn, 'user' => $this->user, 'password' => $this->password, - 'slave' => $this->slave, ] ); $this->bind()->annotatedWith('pdo_dsn')->toProvider(EnvAuthProvider::class, 'dsn'); @@ -52,6 +51,11 @@ protected function configure(): void $this->bind()->annotatedWith('pdo_pass')->toProvider(EnvAuthProvider::class, 'password'); $this->bind()->annotatedWith('pdo_slave')->toProvider(EnvAuthProvider::class, 'slave'); - $this->install(new AuraSqlModule('', '', '', $this->slave, $this->options)); + if ($this->slave) { + $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave, true); + $this->install(new AuraSqlReplicationModule($locator)); + } + + $this->install(new AuraSqlModule('', '', '', '', $this->options)); } } diff --git a/src/AuraSqlModule.php b/src/AuraSqlModule.php index eac25ac..e80675f 100644 --- a/src/AuraSqlModule.php +++ b/src/AuraSqlModule.php @@ -4,7 +4,6 @@ namespace Ray\AuraSqlModule; -use Aura\Sql\ConnectionLocatorInterface; use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerModule; @@ -69,13 +68,7 @@ private function configureSingleDsn(): void private function configureMasterSlaveDsn(): void { - $context = ''; - $this->bind()->annotatedWith('pdo_locator_dsn')->toInstance([$context => $this->dsn]); - $this->bind()->annotatedWith('pdo_locator_user')->toInstance([$context => $this->user]); - $this->bind()->annotatedWith('pdo_locator_pass')->toInstance([$context => $this->password]); - $this->bind()->annotatedWith('pdo_locator_slave')->toInstance([$context => $this->slave]); - $this->bind(ConnectionLocatorInterface::class)->annotatedWith($context)->toProvider(ConnectionLocatorProvider::class, $context)->in(Scope::SINGLETON); - // ReadOnlyConnection when GET, otherwise WriteConnection - $this->bind(ExtendedPdoInterface::class)->toProvider(AuraSqlReplicationDbProvider::class); + $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave, false); + $this->install(new AuraSqlReplicationModule($locator)); } } diff --git a/src/Connection.php b/src/Connection.php index d812936..418d2e8 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -6,37 +6,44 @@ use Aura\Sql\ExtendedPdo; +use function getenv; + class Connection { private string $dsn; - private string $id; + private string $username; private string $password; /** @var array */ private array $options; /** @var array */ - private array $attributes; + private array $queries; private ?ExtendedPdo $pdo = null; + private bool $isEnv; /** * @phpstan-param array $options - * @phpstan-param array $attributes + * @phpstan-param array $queries */ - public function __construct(string $dsn, string $id = '', string $password = '', array $options = [], array $attributes = []) + public function __construct(string $dsn, string $username = '', string $password = '', array $options = [], array $queries = [], bool $isEnv = false) { $this->dsn = $dsn; - $this->id = $id; + $this->username = $username; $this->password = $password; $this->options = $options; - $this->attributes = $attributes; + $this->queries = $queries; + $this->isEnv = $isEnv; } public function __invoke(): ExtendedPdo { - if (! $this->pdo instanceof ExtendedPdo) { - $this->pdo = new ExtendedPdo($this->dsn, $this->id, $this->password, $this->options, $this->attributes); + if ($this->pdo instanceof ExtendedPdo) { + return $this->pdo; } + $this->pdo = $this->isEnv ? + new ExtendedPdo((string) getenv($this->dsn), (string) getenv($this->username), (string) getenv($this->password), $this->options, $this->queries) : + new ExtendedPdo($this->dsn, $this->username, $this->password, $this->options, $this->queries); return $this->pdo; } diff --git a/src/ConnectionLocatorFactory.php b/src/ConnectionLocatorFactory.php index 8204361..7df993b 100644 --- a/src/ConnectionLocatorFactory.php +++ b/src/ConnectionLocatorFactory.php @@ -19,16 +19,16 @@ private function __construct() { } - public static function newInstance(string $dsn, string $user, string $password, string $slave): ConnectionLocator + public static function newInstance(string $dsn, string $user, string $password, string $slave, bool $isEnv): ConnectionLocator { - $writes = ['master' => new Connection($dsn, $user, $password)]; + $writes = ['master' => new Connection($dsn, $user, $password, [], [], $isEnv)]; $i = 1; $slaves = explode(',', $slave); $reads = []; foreach ($slaves as $host) { $slaveDsn = self::changeHost($dsn, $host); $name = 'slave' . (string) $i++; - $reads[$name] = new Connection($slaveDsn, $user, $password); + $reads[$name] = new Connection($slaveDsn, $user, $password, [], [], $isEnv); } return new ConnectionLocator(null, $reads, $writes); diff --git a/src/ConnectionLocatorProvider.php b/src/ConnectionLocatorProvider.php index f86786e..b11c778 100644 --- a/src/ConnectionLocatorProvider.php +++ b/src/ConnectionLocatorProvider.php @@ -58,7 +58,8 @@ public function get(): ConnectionLocatorInterface $this->dsn[$this->context], $this->user[$this->context], $this->password[$this->context], - $this->slave[$this->context] + $this->slave[$this->context], + false ); } } diff --git a/src/NamedPdoModule.php b/src/NamedPdoModule.php index b05f2e5..177e366 100644 --- a/src/NamedPdoModule.php +++ b/src/NamedPdoModule.php @@ -17,19 +17,22 @@ class NamedPdoModule extends AbstractModule private string $user; private string $password; private string $slave; + private bool $isEnv; public function __construct( string $qualifer, string $dsn, string $user = '', string $pass = '', - string $slave = '' + string $slave = '', + bool $isEnv = false ) { $this->qualifer = $qualifer; $this->dsn = $dsn; $this->user = $user; $this->password = $pass; $this->slave = $slave; + $this->isEnv = $isEnv; parent::__construct(); } @@ -38,26 +41,26 @@ public function __construct( */ protected function configure(): void { - $this->slave ? $this->configureMasterSlaveDsn($this->qualifer, $this->dsn, $this->user, $this->password, $this->slave) - : $this->configureSingleDsn($this->qualifer, $this->dsn, $this->user, $this->password); + $this->slave ? $this->configureMasterSlaveDsn() + : $this->configureSingleDsn(); } - private function configureSingleDsn(string $qualifer, string $dsn, string $user, string $password): void + private function configureSingleDsn(): void { $this->bind(ExtendedPdoInterface::class) - ->annotatedWith($qualifer) + ->annotatedWith($this->qualifer) ->toConstructor( ExtendedPdo::class, - "dsn={$qualifer}_dsn,username={$qualifer}_username,password={$qualifer}_password" + "dsn={$this->qualifer}_dsn,username={$this->qualifer}_username,password={$this->qualifer}_password" ); - $this->bind()->annotatedWith("{$qualifer}_dsn")->toInstance($dsn); - $this->bind()->annotatedWith("{$qualifer}_username")->toInstance($user); - $this->bind()->annotatedWith("{$qualifer}_password")->toInstance($password); + $this->bind()->annotatedWith("{$this->qualifer}_dsn")->toInstance($this->dsn); + $this->bind()->annotatedWith("{$this->qualifer}_username")->toInstance($this->user); + $this->bind()->annotatedWith("{$this->qualifer}_password")->toInstance($this->password); } - private function configureMasterSlaveDsn(string $qualifer, string $dsn, string $user, string $password, string $slaveList): void + private function configureMasterSlaveDsn(): void { - $locator = ConnectionLocatorFactory::newInstance($dsn, $user, $password, $slaveList); - $this->install(new AuraSqlReplicationModule($locator, $qualifer)); + $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave, $this->isEnv); + $this->install(new AuraSqlReplicationModule($locator, $this->qualifer)); } } diff --git a/tests/AuraSqlModuleTest.php b/tests/AuraSqlModuleTest.php index 638580e..197adb6 100644 --- a/tests/AuraSqlModuleTest.php +++ b/tests/AuraSqlModuleTest.php @@ -10,8 +10,8 @@ use PHPUnit\Framework\TestCase; use Ray\Compiler\DiCompiler; use Ray\Compiler\ScriptInjector; -use Ray\Di\DependencyProvider; use Ray\Di\Injector; +use Ray\Di\Instance; use ReflectionProperty; use function assert; @@ -56,9 +56,9 @@ public function testSqlite() public function testSlaveModule() { $module = new AuraSqlModule('mysql:host=localhost;dbname=testdb', 'root', '', 'slave1,slave2'); - $provider = $module->getContainer()->getContainer()['Aura\Sql\ConnectionLocatorInterface-']; - assert($provider instanceof DependencyProvider); - $locator = $provider->inject($module->getContainer()); + $instance = $module->getContainer()->getContainer()['Aura\Sql\ConnectionLocatorInterface-']; + assert($instance instanceof Instance); + $locator = $instance->value; assert($locator instanceof ConnectionLocatorInterface); $this->assertInstanceOf(ConnectionLocatorInterface::class, $locator); $dsn = $this->getDsn($locator->getRead()); From 9af17a033d7234744be6325e2a16c49aa1ace7da Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 06:26:51 +0900 Subject: [PATCH 11/24] Bind ExtendedPdoProvider --- src/AuraSqlEnvModule.php | 39 ++++++++++++++++++---------------- src/Connection.php | 27 ++++++++++++++++++++--- src/ExtendedPdoProvider.php | 30 ++++++++++++++++++++++++++ tests/AuraSqEnvlModuleTest.php | 8 +++---- 4 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 src/ExtendedPdoProvider.php diff --git a/src/AuraSqlEnvModule.php b/src/AuraSqlEnvModule.php index 5d4427d..0da4ffd 100644 --- a/src/AuraSqlEnvModule.php +++ b/src/AuraSqlEnvModule.php @@ -4,13 +4,13 @@ namespace Ray\AuraSqlModule; -use Ray\AuraSqlModule\Annotation\EnvAuth; +use Aura\Sql\ExtendedPdoInterface; use Ray\Di\AbstractModule; class AuraSqlEnvModule extends AbstractModule { private string $dsn; - private string $user; + private string $username; private string $password; private string $slave; @@ -19,15 +19,20 @@ class AuraSqlEnvModule extends AbstractModule /** * @param string $dsn Data Source Name (DSN) - * @param string $user User name for the DSN string + * @param string $username User name for the DSN string * @param string $password Password for the DSN string * @param string $slave Comma separated slave host list * @param array $options A key=>value array of driver-specific connection options */ - public function __construct(string $dsn, string $user = '', string $password = '', string $slave = '', array $options = []) - { + public function __construct( + string $dsn, + string $username = '', + string $password = '', + string $slave = '', + array $options = [] + ){ $this->dsn = $dsn; - $this->user = $user; + $this->username = $username; $this->password = $password; $this->slave = $slave; $this->options = $options; @@ -39,20 +44,18 @@ public function __construct(string $dsn, string $user = '', string $password = ' */ protected function configure(): void { - $this->bind()->annotatedWith(EnvAuth::class)->toInstance( - [ - 'dsn' => $this->dsn, - 'user' => $this->user, - 'password' => $this->password, - ] + $this->bind(Connection::class)->toInstance( + new Connection($this->dsn, $this->username, $this->password, [], [], true) ); - $this->bind()->annotatedWith('pdo_dsn')->toProvider(EnvAuthProvider::class, 'dsn'); - $this->bind()->annotatedWith('pdo_user')->toProvider(EnvAuthProvider::class, 'user'); - $this->bind()->annotatedWith('pdo_pass')->toProvider(EnvAuthProvider::class, 'password'); - $this->bind()->annotatedWith('pdo_slave')->toProvider(EnvAuthProvider::class, 'slave'); - + $this->bind(ExtendedPdoInterface::class)->toProvider(ExtendedPdoProvider::class); if ($this->slave) { - $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave, true); + $locator = ConnectionLocatorFactory::newInstance( + $this->dsn, + $this->username, + $this->password, + $this->slave, + true + ); $this->install(new AuraSqlReplicationModule($locator)); } diff --git a/src/Connection.php b/src/Connection.php index 418d2e8..35ccd3c 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -26,8 +26,14 @@ class Connection * @phpstan-param array $options * @phpstan-param array $queries */ - public function __construct(string $dsn, string $username = '', string $password = '', array $options = [], array $queries = [], bool $isEnv = false) - { + public function __construct( + string $dsn, + string $username = '', + string $password = '', + array $options = [], + array $queries = [], + bool $isEnv = false + ){ $this->dsn = $dsn; $this->username = $username; $this->password = $password; @@ -41,10 +47,25 @@ public function __invoke(): ExtendedPdo if ($this->pdo instanceof ExtendedPdo) { return $this->pdo; } + $this->pdo = $this->isEnv ? - new ExtendedPdo((string) getenv($this->dsn), (string) getenv($this->username), (string) getenv($this->password), $this->options, $this->queries) : + new ExtendedPdo( + (string) getenv($this->dsn), + (string) getenv($this->username), + (string) getenv($this->password), + $this->options, + $this->queries + ) : new ExtendedPdo($this->dsn, $this->username, $this->password, $this->options, $this->queries); return $this->pdo; } + + public function isSame(string $dsn, string $username, string $password, bool $isEnv): bool + { + return $dsn === $this->dsn && + $username === $this->username && + $password === $this->password && + $isEnv === $this->isEnv; + } } diff --git a/src/ExtendedPdoProvider.php b/src/ExtendedPdoProvider.php new file mode 100644 index 0000000..e8a1092 --- /dev/null +++ b/src/ExtendedPdoProvider.php @@ -0,0 +1,30 @@ + + */ + +class ExtendedPdoProvider implements ProviderInterface +{ + private Connection $connection; + + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function get(): ExtendedPdo + { + return ($this->connection)(); + } +} diff --git a/tests/AuraSqEnvlModuleTest.php b/tests/AuraSqEnvlModuleTest.php index 4991139..7737efa 100644 --- a/tests/AuraSqEnvlModuleTest.php +++ b/tests/AuraSqEnvlModuleTest.php @@ -36,8 +36,8 @@ public function testSingleDbModule(): void $injector = new Injector($module, __DIR__ . '/tmp'); $instance = $injector->getInstance(ExtendedPdoInterface::class); $this->assertInstanceOf(ExtendedPdo::class, $instance); - $this->assertSame('user1', $injector->getInstance('', 'pdo_user')); - $this->assertSame('password1', $injector->getInstance('', 'pdo_pass')); + $connection = $injector->getInstance(Connection::class); + $this->assertTrue($connection->isSame('TEST_DSN', 'TEST_USER', 'TEST_PASSWORD', true)); } public function testReplicationModule(): void @@ -46,7 +46,7 @@ public function testReplicationModule(): void $injector = new Injector($module, __DIR__ . '/tmp'); $instance = $injector->getInstance(ExtendedPdoInterface::class); $this->assertInstanceOf(ExtendedPdo::class, $instance); - $this->assertSame('user1', $injector->getInstance('', 'pdo_user')); - $this->assertSame('password1', $injector->getInstance('', 'pdo_pass')); + $connection = $injector->getInstance(Connection::class); + $this->assertTrue($connection->isSame('TEST_DSN', 'TEST_USER', 'TEST_PASSWORD', true)); } } From 194e369397eada5bca6b3a2c3d74f12da68f1d5e Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 08:42:09 +0900 Subject: [PATCH 12/24] Extract base module --- src/AuraSqlBaseModule.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/AuraSqlBaseModule.php diff --git a/src/AuraSqlBaseModule.php b/src/AuraSqlBaseModule.php new file mode 100644 index 0000000..7a731bb --- /dev/null +++ b/src/AuraSqlBaseModule.php @@ -0,0 +1,34 @@ +dsn = $dsnKey; + parent::__construct($module); + } + + /** + * {@inheritdoc} + */ + protected function configure(): void + { + // @Transactional + $this->install(new TransactionalModule()); + $this->install(new AuraSqlPagerModule()); + preg_match(AuraSqlModule::PARSE_PDO_DSN_REGEX, $this->dsn, $parts); + $dbType = $parts[1] ?? ''; + $this->install(new AuraSqlQueryModule($dbType)); + } +} From ba922b643ae082c0464c236eb53d76cb9a10d5a6 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 08:45:37 +0900 Subject: [PATCH 13/24] Extract ConnectionLocatorFactory::newInstance to ::fromInstance and ::fromEnv --- src/AuraSqlEnvModule.php | 72 ++++++++++++++--------- src/AuraSqlMasterModule.php | 18 ++++-- src/AuraSqlModule.php | 59 +++++++++++-------- src/AuraSqlReplicationModule.php | 11 +++- src/Connection.php | 24 ++------ src/ConnectionLocatorFactory.php | 36 +++++++++++- src/ConnectionLocatorProvider.php | 65 --------------------- src/EnvConnection.php | 97 +++++++++++++++++++++++++++++++ src/NamedPdoModule.php | 57 +++++++++++++++--- tests/AuraSqEnvlModuleTest.php | 8 ++- 10 files changed, 290 insertions(+), 157 deletions(-) delete mode 100644 src/ConnectionLocatorProvider.php create mode 100644 src/EnvConnection.php diff --git a/src/AuraSqlEnvModule.php b/src/AuraSqlEnvModule.php index 0da4ffd..50efe93 100644 --- a/src/AuraSqlEnvModule.php +++ b/src/AuraSqlEnvModule.php @@ -6,6 +6,7 @@ use Aura\Sql\ExtendedPdoInterface; use Ray\Di\AbstractModule; +use Ray\Di\Scope; class AuraSqlEnvModule extends AbstractModule { @@ -14,28 +15,34 @@ class AuraSqlEnvModule extends AbstractModule private string $password; private string $slave; - /** @var array */ + /** @var array */ private array $options; + /** @var array */ + private array $queries; + /** - * @param string $dsn Data Source Name (DSN) - * @param string $username User name for the DSN string - * @param string $password Password for the DSN string - * @param string $slave Comma separated slave host list - * @param array $options A key=>value array of driver-specific connection options + * @param string $dsnKey Env key for Data Source Name (DSN) + * @param string $usernameKey Env key for Username for the DSN string + * @param string $passwordKey Env key for Password for the DSN string + * @param string $slaveKey Env key for Comma separated slave host list + * @param array $options A key=>value array of driver-specific connection options + * @param array $queries Queries to execute after the connection. */ public function __construct( - string $dsn, - string $username = '', - string $password = '', - string $slave = '', - array $options = [] - ){ - $this->dsn = $dsn; - $this->username = $username; - $this->password = $password; - $this->slave = $slave; + string $dsnKey, + string $usernameKey = '', + string $passwordKey = '', + string $slaveKey = '', + array $options = [], + array $queries = [] + ) { + $this->dsn = $dsnKey; + $this->username = $usernameKey; + $this->password = $passwordKey; + $this->slave = $slaveKey; $this->options = $options; + $this->queries = $queries; parent::__construct(); } @@ -43,22 +50,29 @@ public function __construct( * {@inheritdoc} */ protected function configure(): void + { + $this->slave ? $this->configureMasterSlaveDsn() : $this->configureSingleDsn(); + $this->install(new AuraSqlBaseModule($this->dsn)); + } + + private function configureSingleDsn(): void { $this->bind(Connection::class)->toInstance( - new Connection($this->dsn, $this->username, $this->password, [], [], true) + new Connection($this->dsn, $this->username, $this->password, $this->options, $this->queries) ); - $this->bind(ExtendedPdoInterface::class)->toProvider(ExtendedPdoProvider::class); - if ($this->slave) { - $locator = ConnectionLocatorFactory::newInstance( - $this->dsn, - $this->username, - $this->password, - $this->slave, - true - ); - $this->install(new AuraSqlReplicationModule($locator)); - } + $this->bind(ExtendedPdoInterface::class)->toProvider(ExtendedPdoProvider::class)->in(Scope::SINGLETON); + } - $this->install(new AuraSqlModule('', '', '', '', $this->options)); + public function configureMasterSlaveDsn(): void + { + $locator = ConnectionLocatorFactory::fromEnv( + $this->dsn, + $this->username, + $this->password, + $this->slave, + $this->options, + $this->queries + ); + $this->install(new AuraSqlReplicationModule($locator)); } } diff --git a/src/AuraSqlMasterModule.php b/src/AuraSqlMasterModule.php index f28ea5b..31b693c 100644 --- a/src/AuraSqlMasterModule.php +++ b/src/AuraSqlMasterModule.php @@ -25,11 +25,17 @@ class AuraSqlMasterModule extends AbstractModule * @phpstan-param array $options * @phpstan-param array $attributes */ - public function __construct(string $dsn, string $user = '', string $password = '', array $options = [], array $attributes = [], ?AbstractModule $module = null) - { - $this->dsn = $dsn; + public function __construct( + string $dsnKey, + string $user = '', + string $passwordKey = '', + array $options = [], + array $attributes = [], + ?AbstractModule $module = null + ){ + $this->dsn = $dsnKey; $this->user = $user; - $this->password = $password; + $this->password = $passwordKey; $this->options = $options; $this->attributes = $attributes; parent::__construct($module); @@ -40,7 +46,9 @@ public function __construct(string $dsn, string $user = '', string $password = ' */ protected function configure(): void { - $this->bind(ExtendedPdoInterface::class)->toConstructor(ExtendedPdo::class, 'dsn=pdo_dsn,username=pdo_user,password=pdo_pass,options=pdo_option,attributes=pdo_attributes')->in(Scope::SINGLETON); + $this->bind(ExtendedPdoInterface::class)->toConstructor(ExtendedPdo::class, + 'dsn=pdo_dsn,username=pdo_user,password=pdo_pass,options=pdo_option,attributes=pdo_attributes' + )->in(Scope::SINGLETON); $this->bind()->annotatedWith('pdo_dsn')->toInstance($this->dsn); $this->bind()->annotatedWith('pdo_user')->toInstance($this->user); $this->bind()->annotatedWith('pdo_pass')->toInstance($this->password); diff --git a/src/AuraSqlModule.php b/src/AuraSqlModule.php index e80675f..5893395 100644 --- a/src/AuraSqlModule.php +++ b/src/AuraSqlModule.php @@ -6,12 +6,9 @@ use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; -use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerModule; use Ray\Di\AbstractModule; use Ray\Di\Scope; -use function preg_match; - class AuraSqlModule extends AbstractModule { public const PARSE_PDO_DSN_REGEX = '/(.*?):(?:(host|server)=.*?;)?(.*)/i'; @@ -21,23 +18,34 @@ class AuraSqlModule extends AbstractModule private string $password; private string $slave; - /** @var array */ + /** @var array */ private array $options; + /** @var array */ + private array $queries; + /** - * @param string $dsn Data Source Name (DSN) - * @param string $user User name for the DSN string - * @param string $password Password for the DSN string - * @param string $slave Comma separated slave host list - * @param array $options A key=>value array of driver-specific connection options + * @param string $dsnKey Data Source Name (DSN) + * @param string $user User name for the DSN string + * @param string $passwordKey Password for the DSN string + * @param string $slaveKey Comma separated slave host list + * @param array $options A key=>value array of driver-specific connection options + * @param array $queries */ - public function __construct(string $dsn, string $user = '', string $password = '', string $slave = '', array $options = []) - { - $this->dsn = $dsn; + public function __construct( + string $dsnKey, + string $user = '', + string $passwordKey = '', + string $slaveKey = '', + array $options = [], + array $queries = [] + ) { + $this->dsn = $dsnKey; $this->user = $user; - $this->password = $password; - $this->slave = $slave; + $this->password = $passwordKey; + $this->slave = $slaveKey; $this->options = $options; + $this->queries = $queries; parent::__construct(); } @@ -47,12 +55,7 @@ public function __construct(string $dsn, string $user = '', string $password = ' protected function configure(): void { $this->slave ? $this->configureMasterSlaveDsn() : $this->configureSingleDsn(); - // @Transactional - $this->install(new TransactionalModule()); - $this->install(new AuraSqlPagerModule()); - preg_match(self::PARSE_PDO_DSN_REGEX, $this->dsn, $parts); - $dbType = $parts[1] ?? ''; - $this->install(new AuraSqlQueryModule($dbType)); + $this->install(new AuraSqlBaseModule($this->dsn)); } private function configureSingleDsn(): void @@ -62,13 +65,23 @@ private function configureSingleDsn(): void $this->bind()->annotatedWith('pdo_pass')->toInstance($this->password); $this->bind()->annotatedWith('pdo_slave')->toInstance($this->slave); $this->bind()->annotatedWith('pdo_options')->toInstance($this->options); - $this->bind()->annotatedWith('pdo_attributes')->toInstance($this->options); - $this->bind(ExtendedPdoInterface::class)->toConstructor(ExtendedPdo::class, 'dsn=pdo_dsn,username=pdo_user,password=pdo_pass,options=pdo_options,attributes=pdo_attributes')->in(Scope::SINGLETON); + $this->bind()->annotatedWith('pdo_queries')->toInstance($this->queries); + $this->bind(ExtendedPdoInterface::class)->toConstructor( + ExtendedPdo::class, + 'dsn=pdo_dsn,username=pdo_user,password=pdo_pass,options=pdo_options,queries=pdo_queries' + )->in(Scope::SINGLETON); } private function configureMasterSlaveDsn(): void { - $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave, false); + $locator = ConnectionLocatorFactory::fromInstance( + $this->dsn, + $this->user, + $this->password, + $this->slave, + $this->options, + $this->queries + ); $this->install(new AuraSqlReplicationModule($locator)); } } diff --git a/src/AuraSqlReplicationModule.php b/src/AuraSqlReplicationModule.php index 682e967..5837f3f 100644 --- a/src/AuraSqlReplicationModule.php +++ b/src/AuraSqlReplicationModule.php @@ -9,7 +9,6 @@ use Ray\AuraSqlModule\Annotation\ReadOnlyConnection; use Ray\AuraSqlModule\Annotation\WriteConnection; use Ray\Di\AbstractModule; -use Ray\Di\Scope; class AuraSqlReplicationModule extends AbstractModule { @@ -31,9 +30,15 @@ public function __construct( */ protected function configure(): void { - $this->bind(ConnectionLocatorInterface::class)->annotatedWith($this->qualifer)->toInstance($this->connectionLocator); + $this->bind(ConnectionLocatorInterface::class) + ->annotatedWith($this->qualifer) + ->toInstance($this->connectionLocator); + // ReadOnlyConnection when GET, otherwise WriteConnection - $this->bind(ExtendedPdoInterface::class)->annotatedWith($this->qualifer)->toProvider(AuraSqlReplicationDbProvider::class, $this->qualifer)->in(Scope::SINGLETON); + $this->bind(ExtendedPdoInterface::class) + ->annotatedWith($this->qualifer) + ->toProvider(AuraSqlReplicationDbProvider::class, $this->qualifer); + // @ReadOnlyConnection @WriteConnection $this->installReadWriteConnection(); } diff --git a/src/Connection.php b/src/Connection.php index 35ccd3c..4e710d2 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -6,8 +6,6 @@ use Aura\Sql\ExtendedPdo; -use function getenv; - class Connection { private string $dsn; @@ -20,7 +18,6 @@ class Connection /** @var array */ private array $queries; private ?ExtendedPdo $pdo = null; - private bool $isEnv; /** * @phpstan-param array $options @@ -31,15 +28,13 @@ public function __construct( string $username = '', string $password = '', array $options = [], - array $queries = [], - bool $isEnv = false - ){ + array $queries = [] + ) { $this->dsn = $dsn; $this->username = $username; $this->password = $password; $this->options = $options; $this->queries = $queries; - $this->isEnv = $isEnv; } public function __invoke(): ExtendedPdo @@ -48,24 +43,15 @@ public function __invoke(): ExtendedPdo return $this->pdo; } - $this->pdo = $this->isEnv ? - new ExtendedPdo( - (string) getenv($this->dsn), - (string) getenv($this->username), - (string) getenv($this->password), - $this->options, - $this->queries - ) : - new ExtendedPdo($this->dsn, $this->username, $this->password, $this->options, $this->queries); + $this->pdo = new ExtendedPdo($this->dsn, $this->username, $this->password, $this->options, $this->queries); return $this->pdo; } - public function isSame(string $dsn, string $username, string $password, bool $isEnv): bool + public function isSame(string $dsn, string $username, string $password): bool { return $dsn === $this->dsn && $username === $this->username && - $password === $this->password && - $isEnv === $this->isEnv; + $password === $this->password; } } diff --git a/src/ConnectionLocatorFactory.php b/src/ConnectionLocatorFactory.php index 7df993b..0c7a03c 100644 --- a/src/ConnectionLocatorFactory.php +++ b/src/ConnectionLocatorFactory.php @@ -19,21 +19,51 @@ private function __construct() { } - public static function newInstance(string $dsn, string $user, string $password, string $slave, bool $isEnv): ConnectionLocator + /** + * @param array $options + * @param array $queries + */ + public static function fromInstance( + string $dsn, + string $user, + string $password, + string $slave, + array $options, + array $queries + ): ConnectionLocator { - $writes = ['master' => new Connection($dsn, $user, $password, [], [], $isEnv)]; + $writes = ['master' => new Connection($dsn, $user, $password, $options, $queries)]; $i = 1; $slaves = explode(',', $slave); $reads = []; foreach ($slaves as $host) { $slaveDsn = self::changeHost($dsn, $host); $name = 'slave' . (string) $i++; - $reads[$name] = new Connection($slaveDsn, $user, $password, [], [], $isEnv); + $reads[$name] = new Connection($slaveDsn, $user, $password, $options, $queries); } return new ConnectionLocator(null, $reads, $writes); } + /** + * @param array $options + * @param array $queries + */ + public static function fromEnv( + string $dsn, + string $user, + string $password, + string $slave, + array $options, + array $queries + ): ConnectionLocator + { + $writes = ['master' => new EnvConnection($dsn, null, $user, $password, $options, $queries)]; + $reads = ['slave' => new EnvConnection($dsn, $slave, $user, $password, $options, $queries)]; + + return new ConnectionLocator(null, $reads, $writes); + } + private static function changeHost(string $dsn, string $host): string { preg_match(AuraSqlModule::PARSE_PDO_DSN_REGEX, $dsn, $parts); diff --git a/src/ConnectionLocatorProvider.php b/src/ConnectionLocatorProvider.php deleted file mode 100644 index b11c778..0000000 --- a/src/ConnectionLocatorProvider.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -final class ConnectionLocatorProvider implements ProviderInterface, SetContextInterface -{ - /** @var array */ - private array $dsn; - - /** @var array */ - private array $user; - - /** @var array */ - private array $password; - - /** @var array */ - private array $slave; - private string $context; - - /** - * {@inheritDoc} - */ - public function setContext($context) - { - $this->context = $context; - } - - /** - * @param array $dsn - * @param array $user - * @param array $password - * @param array $slave - * - * @Named("dsn=pdo_locator_dsn,user=pdo_locator_user,password=pdo_locator_pass,slave=pdo_locator_slave") - */ - #[Named('dsn=pdo_locator_dsn,user=pdo_locator_user,password=pdo_locator_pass,slave=pdo_locator_slave')] - public function __construct(array $dsn, array $user, array $password, array $slave) - { - $this->dsn = $dsn; - $this->user = $user; - $this->password = $password; - $this->slave = $slave; - } - - public function get(): ConnectionLocatorInterface - { - return ConnectionLocatorFactory::newInstance( - $this->dsn[$this->context], - $this->user[$this->context], - $this->password[$this->context], - $this->slave[$this->context], - false - ); - } -} diff --git a/src/EnvConnection.php b/src/EnvConnection.php new file mode 100644 index 0000000..e7a002b --- /dev/null +++ b/src/EnvConnection.php @@ -0,0 +1,97 @@ + */ + private array $options; + + /** @var array */ + private array $queries; + + /** @var array */ + private array $pdo = []; + private ?string $slave; + + /** + * @phpstan-param array $options + * @phpstan-param array $queries + */ + public function __construct( + string $dsn, + ?string $slave, + string $username = '', + string $password = '', + array $options = [], + array $queries = [] + ) { + $this->dsn = $dsn; + $this->slave = $slave; + $this->username = $username; + $this->password = $password; + $this->options = $options; + $this->queries = $queries; + } + + public function __invoke(): ExtendedPdo + { + $dsn = $this->getDsn(); + if (isset($this->pdo[$dsn])) { + return $this->pdo[$dsn]; + } + + $this->pdo[$dsn] = new ExtendedPdo( + $dsn, + (string) getenv($this->username), + (string) getenv($this->password), + $this->options, + $this->queries + ); + + return $this->pdo[$dsn]; + } + + private function getDsn(): string + { + // write + if ($this->slave === null) { + return $this->dsn; + } + + // random read + $slaveList = explode(',', (string) getenv($this->slave)); + $slave = $slaveList[array_rand($slaveList)]; + + return $this->changeHost((string) getenv($this->dsn), $slave); + } + + /** + * @psalm-pure + */ + private function changeHost(string $dsn, string $host): string + { + preg_match(AuraSqlModule::PARSE_PDO_DSN_REGEX, $dsn, $parts); + if (! $parts) { + // @codeCoverageIgnoreStart + return $dsn; + // @codeCoverageIgnoreEnd + } + + return sprintf('%s:%s=%s;%s', $parts[1], $parts[2], $host, $parts[3]); + } +} diff --git a/src/NamedPdoModule.php b/src/NamedPdoModule.php index 177e366..14baa06 100644 --- a/src/NamedPdoModule.php +++ b/src/NamedPdoModule.php @@ -4,6 +4,7 @@ namespace Ray\AuraSqlModule; +use Aura\Sql\ConnectionLocator; use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; use Ray\Di\AbstractModule; @@ -14,24 +15,43 @@ class NamedPdoModule extends AbstractModule private string $qualifer; private string $dsn; - private string $user; + private string $username; private string $password; private string $slave; + + /** @var array */ + private array $options; + + /** @var array */ + private array $queries; private bool $isEnv; + /** + * @param string $qualifer Qualifer for ExtendedPdoInterface + * @param string $dsn Data Source Name (DSN) + * @param string $username User name for the DSN string + * @param string $password Password for the DSN string + * @param string $slave Comma separated slave host list + * @param array $options A key=>value array of driver-specific connection options + * @param array $queries Queries to execute after the connection. + */ public function __construct( string $qualifer, string $dsn, - string $user = '', - string $pass = '', + string $username = '', + string $password = '', string $slave = '', + array $options = [], + array $queries = [], bool $isEnv = false ) { $this->qualifer = $qualifer; $this->dsn = $dsn; - $this->user = $user; - $this->password = $pass; + $this->username = $username; + $this->password = $password; $this->slave = $slave; + $this->options = $options; + $this->queries = $queries; $this->isEnv = $isEnv; parent::__construct(); } @@ -54,13 +74,36 @@ private function configureSingleDsn(): void "dsn={$this->qualifer}_dsn,username={$this->qualifer}_username,password={$this->qualifer}_password" ); $this->bind()->annotatedWith("{$this->qualifer}_dsn")->toInstance($this->dsn); - $this->bind()->annotatedWith("{$this->qualifer}_username")->toInstance($this->user); + $this->bind()->annotatedWith("{$this->qualifer}_username")->toInstance($this->username); $this->bind()->annotatedWith("{$this->qualifer}_password")->toInstance($this->password); } private function configureMasterSlaveDsn(): void { - $locator = ConnectionLocatorFactory::newInstance($this->dsn, $this->user, $this->password, $this->slave, $this->isEnv); + $locator = $this->getLocator(); $this->install(new AuraSqlReplicationModule($locator, $this->qualifer)); } + + private function getLocator(): ConnectionLocator + { + if ($this->isEnv) { + return ConnectionLocatorFactory::fromEnv( + $this->dsn, + $this->username, + $this->password, + $this->slave, + $this->options, + $this->queries + ); + } + + return ConnectionLocatorFactory::fromInstance( + $this->dsn, + $this->username, + $this->password, + $this->slave, + $this->options, + $this->queries + ); + } } diff --git a/tests/AuraSqEnvlModuleTest.php b/tests/AuraSqEnvlModuleTest.php index 7737efa..307433b 100644 --- a/tests/AuraSqEnvlModuleTest.php +++ b/tests/AuraSqEnvlModuleTest.php @@ -4,6 +4,7 @@ namespace Ray\AuraSqlModule; +use Aura\Sql\ConnectionLocatorInterface; use Aura\Sql\ExtendedPdo; use Aura\Sql\ExtendedPdoInterface; use PHPUnit\Framework\TestCase; @@ -15,7 +16,7 @@ class AuraSqEnvlModuleTest extends TestCase { public function setUp(): void { - putenv('TEST_DSN=sqlite::memory:'); + putenv('TEST_DSN=mysql:host=localhost;dbname=db'); putenv('TEST_USER=user1'); putenv('TEST_PASSWORD=password1'); // for log db @@ -46,7 +47,8 @@ public function testReplicationModule(): void $injector = new Injector($module, __DIR__ . '/tmp'); $instance = $injector->getInstance(ExtendedPdoInterface::class); $this->assertInstanceOf(ExtendedPdo::class, $instance); - $connection = $injector->getInstance(Connection::class); - $this->assertTrue($connection->isSame('TEST_DSN', 'TEST_USER', 'TEST_PASSWORD', true)); + $connectionLocator = $injector->getInstance(ConnectionLocatorInterface::class); + $read = $connectionLocator->getRead(); + $this->assertInstanceOf(ExtendedPdo::class, $read); } } From 501bef24a111b0cac3b33a6b4f0d2d65672aad93 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 10:00:02 +0900 Subject: [PATCH 14/24] Remove EnvAuthProvider --- src/EnvAuthProvider.php | 52 ----------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 src/EnvAuthProvider.php diff --git a/src/EnvAuthProvider.php b/src/EnvAuthProvider.php deleted file mode 100644 index adf4a0a..0000000 --- a/src/EnvAuthProvider.php +++ /dev/null @@ -1,52 +0,0 @@ - - */ -class EnvAuthProvider implements ProviderInterface, SetContextInterface -{ - private string $context; - - /** @var array */ - private array $envAuth; - - /** @param string $context */ - public function setContext($context): void - { - $this->context = $context; - } - - /** - * @param array $envAuth - * - * @EnvAuth - */ - #[EnvAuth] - public function __construct(array $envAuth) - { - $this->envAuth = $envAuth; - } - - /** - * {@inheritdoc} - */ - public function get(): string - { - $value = (string) getenv($this->envAuth[$this->context]); - assert(is_string($value)); - - return $value; - } -} From 0407ad0b1f01750aad77645b74ebb40ccdbe3ebc Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 10:00:50 +0900 Subject: [PATCH 15/24] fixup! Extract ConnectionLocatorFactory::newInstance to ::fromInstance and ::fromEnv --- src/EnvConnection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EnvConnection.php b/src/EnvConnection.php index e7a002b..787974b 100644 --- a/src/EnvConnection.php +++ b/src/EnvConnection.php @@ -70,7 +70,7 @@ private function getDsn(): string { // write if ($this->slave === null) { - return $this->dsn; + return getenv($this->dsn); } // random read From 4601c5cfe3f538cb7290330a574b86044627cab0 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 10:11:05 +0900 Subject: [PATCH 16/24] Create NamedPdoEnvModule --- src/NamedExtendedPdoProvider.php | 46 ++++++++++++++++ src/NamedPdoEnvModule.php | 92 ++++++++++++++++++++++++++++++++ tests/NamedPdoEnvModuleTest.php | 73 +++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 src/NamedExtendedPdoProvider.php create mode 100644 src/NamedPdoEnvModule.php create mode 100644 tests/NamedPdoEnvModuleTest.php diff --git a/src/NamedExtendedPdoProvider.php b/src/NamedExtendedPdoProvider.php new file mode 100644 index 0000000..e882dcc --- /dev/null +++ b/src/NamedExtendedPdoProvider.php @@ -0,0 +1,46 @@ + + */ + +class NamedExtendedPdoProvider implements ProviderInterface, SetContextInterface +{ + private InjectorInterface $injector; + private string $context; + + /** + * {@inheritDoc} + */ + public function setContext($context) + { + $this->context = $context; + } + + public function __construct(InjectorInterface $injector) + { + $this->injector = $injector; + } + + /** + * {@inheritdoc} + */ + public function get(): ExtendedPdo + { + $connection = $this->injector->getInstance(EnvConnection::class, $this->context); + assert($connection instanceof EnvConnection); + + return ($connection)(); + } +} diff --git a/src/NamedPdoEnvModule.php b/src/NamedPdoEnvModule.php new file mode 100644 index 0000000..f7efbb1 --- /dev/null +++ b/src/NamedPdoEnvModule.php @@ -0,0 +1,92 @@ + */ + private array $options; + + /** @var array */ + private array $queries; + + /** + * @param string $qualifer Qualifer for ExtendedPdoInterface + * @param string $dsn Data Source Name (DSN) + * @param string $username User name for the DSN string + * @param string $password Password for the DSN string + * @param string $slave Comma separated slave host list + * @param array $options A key=>value array of driver-specific connection options + * @param array $queries Queries to execute after the connection. + */ + public function __construct( + string $qualifer, + string $dsn, + string $username = '', + string $password = '', + string $slave = '', + array $options = [], + array $queries = [] + ) { + $this->qualifer = $qualifer; + $this->dsn = $dsn; + $this->username = $username; + $this->password = $password; + $this->slave = $slave; + $this->options = $options; + $this->queries = $queries; + parent::__construct(); + } + + /** + * {@inheritdoc} + */ + protected function configure(): void + { + $this->slave ? $this->configureMasterSlaveDsn() + : $this->configureSingleDsn(); + } + + private function configureSingleDsn(): void + { + $connection = new EnvConnection( + $this->dsn, null, $this->username, $this->password, $this->options, $this->queries + ); + $this->bind(EnvConnection::class)->annotatedWith($this->qualifer)->toInstance($connection); + $this->bind(ExtendedPdoInterface::class)->annotatedWith($this->qualifer)->toProvider( + NamedExtendedPdoProvider::class, $this->qualifer + ); + } + + private function configureMasterSlaveDsn(): void + { + $locator = $this->getLocator(); + $this->install(new AuraSqlReplicationModule($locator, $this->qualifer)); + } + + private function getLocator(): ConnectionLocator + { + return ConnectionLocatorFactory::fromEnv( + $this->dsn, + $this->username, + $this->password, + $this->slave, + $this->options, + $this->queries + ); + } +} diff --git a/tests/NamedPdoEnvModuleTest.php b/tests/NamedPdoEnvModuleTest.php new file mode 100644 index 0000000..9998636 --- /dev/null +++ b/tests/NamedPdoEnvModuleTest.php @@ -0,0 +1,73 @@ +getInstance(ExtendedPdoInterface::class, $qualifer); + $this->assertInstanceOf(ExtendedPdo::class, $instance); + } + + public function testFakeName() + { + $injector = new Injector(new FakeNamedModule(), __DIR__ . '/tmp'); + $fakeName = $injector->getInstance(FakeName::class); + $this->assertInstanceOf(ExtendedPdo::class, $fakeName->pdo); + $this->assertInstanceOf(ExtendedPdo::class, $fakeName->pdoAnno); + $this->assertInstanceOf(ExtendedPdo::class, $fakeName->pdoSetterInject); + } + + public function testReplicationMaster() + { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $qualifer = 'log_db'; + $instance = (new Injector(new FakeNamedReplicationModule(), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); + $this->assertInstanceOf(ExtendedPdo::class, $instance); + $this->assertSame('mysql:host=localhost;dbname=db', $this->getDsn($instance)); + } + + public function testReplicationSlave() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $qualifer = 'log_db'; + $instance = (new Injector(new FakeNamedReplicationModule(), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); + $this->assertInstanceOf(ExtendedPdo::class, $instance); + $this->assertStringContainsString('mysql:host=slave', $this->getDsn($instance)); + } + + public function testNoHost() + { + $qualifer = 'log_db'; + $instance = (new Injector(new FakeNamedQualifierModule(), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); + /** @var ExtendedPdo $instance */ + $this->assertSame('mysql:host=slave1;dbname=master', $this->getDsn($instance)); + } + + private function getDsn(ExtendedPdo $pdo): string + { + $prop = new ReflectionProperty(get_class($pdo), 'args'); + $prop->setAccessible(true); + $args = $prop->getValue($pdo); + + return $args[0]; // dsn + } +} From b291566b107d73f7ad1930564fc10cd26340be7e Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 10:11:20 +0900 Subject: [PATCH 17/24] Fix CS --- src/AuraSqlMasterModule.php | 5 +++-- src/ConnectionLocatorFactory.php | 12 +++++------- src/EnvConnection.php | 2 +- src/NamedPdoEnvModule.php | 10 ++++++++-- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/AuraSqlMasterModule.php b/src/AuraSqlMasterModule.php index 31b693c..27b7f9f 100644 --- a/src/AuraSqlMasterModule.php +++ b/src/AuraSqlMasterModule.php @@ -32,7 +32,7 @@ public function __construct( array $options = [], array $attributes = [], ?AbstractModule $module = null - ){ + ) { $this->dsn = $dsnKey; $this->user = $user; $this->password = $passwordKey; @@ -46,7 +46,8 @@ public function __construct( */ protected function configure(): void { - $this->bind(ExtendedPdoInterface::class)->toConstructor(ExtendedPdo::class, + $this->bind(ExtendedPdoInterface::class)->toConstructor( + ExtendedPdo::class, 'dsn=pdo_dsn,username=pdo_user,password=pdo_pass,options=pdo_option,attributes=pdo_attributes' )->in(Scope::SINGLETON); $this->bind()->annotatedWith('pdo_dsn')->toInstance($this->dsn); diff --git a/src/ConnectionLocatorFactory.php b/src/ConnectionLocatorFactory.php index 0c7a03c..3dccf47 100644 --- a/src/ConnectionLocatorFactory.php +++ b/src/ConnectionLocatorFactory.php @@ -30,8 +30,7 @@ public static function fromInstance( string $slave, array $options, array $queries - ): ConnectionLocator - { + ): ConnectionLocator { $writes = ['master' => new Connection($dsn, $user, $password, $options, $queries)]; $i = 1; $slaves = explode(',', $slave); @@ -51,15 +50,14 @@ public static function fromInstance( */ public static function fromEnv( string $dsn, - string $user, + string $username, string $password, string $slave, array $options, array $queries - ): ConnectionLocator - { - $writes = ['master' => new EnvConnection($dsn, null, $user, $password, $options, $queries)]; - $reads = ['slave' => new EnvConnection($dsn, $slave, $user, $password, $options, $queries)]; + ): ConnectionLocator { + $writes = ['master' => new EnvConnection($dsn, null, $username, $password, $options, $queries)]; + $reads = ['slave' => new EnvConnection($dsn, $slave, $username, $password, $options, $queries)]; return new ConnectionLocator(null, $reads, $writes); } diff --git a/src/EnvConnection.php b/src/EnvConnection.php index 787974b..ab290ee 100644 --- a/src/EnvConnection.php +++ b/src/EnvConnection.php @@ -70,7 +70,7 @@ private function getDsn(): string { // write if ($this->slave === null) { - return getenv($this->dsn); + return (string) getenv($this->dsn); } // random read diff --git a/src/NamedPdoEnvModule.php b/src/NamedPdoEnvModule.php index f7efbb1..7eb3fa1 100644 --- a/src/NamedPdoEnvModule.php +++ b/src/NamedPdoEnvModule.php @@ -64,11 +64,17 @@ protected function configure(): void private function configureSingleDsn(): void { $connection = new EnvConnection( - $this->dsn, null, $this->username, $this->password, $this->options, $this->queries + $this->dsn, + null, + $this->username, + $this->password, + $this->options, + $this->queries ); $this->bind(EnvConnection::class)->annotatedWith($this->qualifer)->toInstance($connection); $this->bind(ExtendedPdoInterface::class)->annotatedWith($this->qualifer)->toProvider( - NamedExtendedPdoProvider::class, $this->qualifer + NamedExtendedPdoProvider::class, + $this->qualifer ); } From 2483f1594d03fef0e6451e8952c986f5a931693f Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 10:44:41 +0900 Subject: [PATCH 18/24] Enable signleton --- src/EnvConnection.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/EnvConnection.php b/src/EnvConnection.php index ab290ee..06d3ab1 100644 --- a/src/EnvConnection.php +++ b/src/EnvConnection.php @@ -25,7 +25,7 @@ final class EnvConnection private array $queries; /** @var array */ - private array $pdo = []; + private static array $pdo = []; private ?string $slave; /** @@ -51,11 +51,11 @@ public function __construct( public function __invoke(): ExtendedPdo { $dsn = $this->getDsn(); - if (isset($this->pdo[$dsn])) { - return $this->pdo[$dsn]; + if (isset(self::$pdo[$dsn])) { + return self::$pdo[$dsn]; } - $this->pdo[$dsn] = new ExtendedPdo( + self::$pdo[$dsn] = new ExtendedPdo( $dsn, (string) getenv($this->username), (string) getenv($this->password), @@ -63,7 +63,7 @@ public function __invoke(): ExtendedPdo $this->queries ); - return $this->pdo[$dsn]; + return self::$pdo[$dsn]; } private function getDsn(): string From 168198c6753129161ce216901e5ae4220f36d8f7 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 10:45:25 +0900 Subject: [PATCH 19/24] Remove condition --- src/NamedPdoEnvModule.php | 22 ++++++++-------------- src/NamedPdoModule.php | 16 +--------------- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/src/NamedPdoEnvModule.php b/src/NamedPdoEnvModule.php index 7eb3fa1..28ebc6a 100644 --- a/src/NamedPdoEnvModule.php +++ b/src/NamedPdoEnvModule.php @@ -4,7 +4,6 @@ namespace Ray\AuraSqlModule; -use Aura\Sql\ConnectionLocator; use Aura\Sql\ExtendedPdoInterface; use Ray\Di\AbstractModule; @@ -80,19 +79,14 @@ private function configureSingleDsn(): void private function configureMasterSlaveDsn(): void { - $locator = $this->getLocator(); + $locator = ConnectionLocatorFactory::fromEnv( + $this->dsn, + $this->username, + $this->password, + $this->slave, + $this->options, + $this->queries + ); $this->install(new AuraSqlReplicationModule($locator, $this->qualifer)); } - - private function getLocator(): ConnectionLocator - { - return ConnectionLocatorFactory::fromEnv( - $this->dsn, - $this->username, - $this->password, - $this->slave, - $this->options, - $this->queries - ); - } } diff --git a/src/NamedPdoModule.php b/src/NamedPdoModule.php index 14baa06..b547d2f 100644 --- a/src/NamedPdoModule.php +++ b/src/NamedPdoModule.php @@ -24,7 +24,6 @@ class NamedPdoModule extends AbstractModule /** @var array */ private array $queries; - private bool $isEnv; /** * @param string $qualifer Qualifer for ExtendedPdoInterface @@ -42,8 +41,7 @@ public function __construct( string $password = '', string $slave = '', array $options = [], - array $queries = [], - bool $isEnv = false + array $queries = [] ) { $this->qualifer = $qualifer; $this->dsn = $dsn; @@ -52,7 +50,6 @@ public function __construct( $this->slave = $slave; $this->options = $options; $this->queries = $queries; - $this->isEnv = $isEnv; parent::__construct(); } @@ -86,17 +83,6 @@ private function configureMasterSlaveDsn(): void private function getLocator(): ConnectionLocator { - if ($this->isEnv) { - return ConnectionLocatorFactory::fromEnv( - $this->dsn, - $this->username, - $this->password, - $this->slave, - $this->options, - $this->queries - ); - } - return ConnectionLocatorFactory::fromInstance( $this->dsn, $this->username, From 4c9deb544f79a1996acf900a0ef1ed6fb9b9b90c Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 10:51:04 +0900 Subject: [PATCH 20/24] Test coverage 100% --- tests/AuraSqEnvlModuleTest.php | 4 ++++ tests/NamedPdoEnvModuleTest.php | 40 ++++----------------------------- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/tests/AuraSqEnvlModuleTest.php b/tests/AuraSqEnvlModuleTest.php index 307433b..2b1cb52 100644 --- a/tests/AuraSqEnvlModuleTest.php +++ b/tests/AuraSqEnvlModuleTest.php @@ -11,6 +11,7 @@ use Ray\Di\Injector; use function putenv; +use function spl_object_hash; class AuraSqEnvlModuleTest extends TestCase { @@ -39,6 +40,9 @@ public function testSingleDbModule(): void $this->assertInstanceOf(ExtendedPdo::class, $instance); $connection = $injector->getInstance(Connection::class); $this->assertTrue($connection->isSame('TEST_DSN', 'TEST_USER', 'TEST_PASSWORD', true)); + // test singleton + $instance2 = $injector->getInstance(ExtendedPdoInterface::class); + $this->assertSame(spl_object_hash($instance), spl_object_hash($instance2)); } public function testReplicationModule(): void diff --git a/tests/NamedPdoEnvModuleTest.php b/tests/NamedPdoEnvModuleTest.php index 9998636..de4abe8 100644 --- a/tests/NamedPdoEnvModuleTest.php +++ b/tests/NamedPdoEnvModuleTest.php @@ -8,9 +8,7 @@ use Aura\Sql\ExtendedPdoInterface; use PHPUnit\Framework\TestCase; use Ray\Di\Injector; -use ReflectionProperty; -use function get_class; use function putenv; class NamedPdoEnvModuleTest extends TestCase @@ -27,47 +25,17 @@ public function testSingletonModule() $this->assertInstanceOf(ExtendedPdo::class, $instance); } - public function testFakeName() + public function testMasterSlaveModule() { - $injector = new Injector(new FakeNamedModule(), __DIR__ . '/tmp'); - $fakeName = $injector->getInstance(FakeName::class); - $this->assertInstanceOf(ExtendedPdo::class, $fakeName->pdo); - $this->assertInstanceOf(ExtendedPdo::class, $fakeName->pdoAnno); - $this->assertInstanceOf(ExtendedPdo::class, $fakeName->pdoSetterInject); - } - - public function testReplicationMaster() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; $qualifer = 'log_db'; - $instance = (new Injector(new FakeNamedReplicationModule(), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); + $instance = (new Injector(new NamedPdoEnvModule($qualifer, 'TEST_DSN', 'TEST_USERNAME', 'TEST_PASSWORD', 'SLAVE1,SLAVE2'), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); $this->assertInstanceOf(ExtendedPdo::class, $instance); - $this->assertSame('mysql:host=localhost;dbname=db', $this->getDsn($instance)); } - public function testReplicationSlave() + public function testMasterSlaveModuleSingletonPdo() { - $_SERVER['REQUEST_METHOD'] = 'GET'; $qualifer = 'log_db'; - $instance = (new Injector(new FakeNamedReplicationModule(), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); + $instance = (new Injector(new NamedPdoEnvModule($qualifer, 'TEST_DSN', 'TEST_USERNAME', 'TEST_PASSWORD', 'SLAVE1,SLAVE2'), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); $this->assertInstanceOf(ExtendedPdo::class, $instance); - $this->assertStringContainsString('mysql:host=slave', $this->getDsn($instance)); - } - - public function testNoHost() - { - $qualifer = 'log_db'; - $instance = (new Injector(new FakeNamedQualifierModule(), __DIR__ . '/tmp'))->getInstance(ExtendedPdoInterface::class, $qualifer); - /** @var ExtendedPdo $instance */ - $this->assertSame('mysql:host=slave1;dbname=master', $this->getDsn($instance)); - } - - private function getDsn(ExtendedPdo $pdo): string - { - $prop = new ReflectionProperty(get_class($pdo), 'args'); - $prop->setAccessible(true); - $args = $prop->getValue($pdo); - - return $args[0]; // dsn } } From a34557cf33b523e2a6c02c57baec0ac00f28ce56 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 11:03:05 +0900 Subject: [PATCH 21/24] Merge to NamedEnvModule --- src/AuraSqlEnvModule.php | 25 +------------------------ tests/AuraSqEnvlModuleTest.php | 2 -- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/src/AuraSqlEnvModule.php b/src/AuraSqlEnvModule.php index 50efe93..93e7fed 100644 --- a/src/AuraSqlEnvModule.php +++ b/src/AuraSqlEnvModule.php @@ -4,9 +4,7 @@ namespace Ray\AuraSqlModule; -use Aura\Sql\ExtendedPdoInterface; use Ray\Di\AbstractModule; -use Ray\Di\Scope; class AuraSqlEnvModule extends AbstractModule { @@ -51,28 +49,7 @@ public function __construct( */ protected function configure(): void { - $this->slave ? $this->configureMasterSlaveDsn() : $this->configureSingleDsn(); + $this->install(new NamedPdoEnvModule('', $this->dsn, $this->username, $this->password, $this->slave, $this->options, $this->queries)); $this->install(new AuraSqlBaseModule($this->dsn)); } - - private function configureSingleDsn(): void - { - $this->bind(Connection::class)->toInstance( - new Connection($this->dsn, $this->username, $this->password, $this->options, $this->queries) - ); - $this->bind(ExtendedPdoInterface::class)->toProvider(ExtendedPdoProvider::class)->in(Scope::SINGLETON); - } - - public function configureMasterSlaveDsn(): void - { - $locator = ConnectionLocatorFactory::fromEnv( - $this->dsn, - $this->username, - $this->password, - $this->slave, - $this->options, - $this->queries - ); - $this->install(new AuraSqlReplicationModule($locator)); - } } diff --git a/tests/AuraSqEnvlModuleTest.php b/tests/AuraSqEnvlModuleTest.php index 2b1cb52..06a9720 100644 --- a/tests/AuraSqEnvlModuleTest.php +++ b/tests/AuraSqEnvlModuleTest.php @@ -38,8 +38,6 @@ public function testSingleDbModule(): void $injector = new Injector($module, __DIR__ . '/tmp'); $instance = $injector->getInstance(ExtendedPdoInterface::class); $this->assertInstanceOf(ExtendedPdo::class, $instance); - $connection = $injector->getInstance(Connection::class); - $this->assertTrue($connection->isSame('TEST_DSN', 'TEST_USER', 'TEST_PASSWORD', true)); // test singleton $instance2 = $injector->getInstance(ExtendedPdoInterface::class); $this->assertSame(spl_object_hash($instance), spl_object_hash($instance2)); From 1495cbd45f40a5b7245be0a993567a27f324c521 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 11:31:42 +0900 Subject: [PATCH 22/24] Remove unused class/method --- src/Connection.php | 7 ------- src/ExtendedPdoProvider.php | 30 ------------------------------ 2 files changed, 37 deletions(-) delete mode 100644 src/ExtendedPdoProvider.php diff --git a/src/Connection.php b/src/Connection.php index 4e710d2..44edc7b 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -47,11 +47,4 @@ public function __invoke(): ExtendedPdo return $this->pdo; } - - public function isSame(string $dsn, string $username, string $password): bool - { - return $dsn === $this->dsn && - $username === $this->username && - $password === $this->password; - } } diff --git a/src/ExtendedPdoProvider.php b/src/ExtendedPdoProvider.php deleted file mode 100644 index e8a1092..0000000 --- a/src/ExtendedPdoProvider.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ - -class ExtendedPdoProvider implements ProviderInterface -{ - private Connection $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * {@inheritdoc} - */ - public function get(): ExtendedPdo - { - return ($this->connection)(); - } -} From f352032996a5f653c0546981a2957061955760a0 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Thu, 29 Sep 2022 11:36:21 +0900 Subject: [PATCH 23/24] [skip ci] Update README --- README.md | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ca9965..bef0307 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,26 @@ class AppModule extends AbstractModule 'password', 'slave1,slave2,slave3' // optional slave server list $options, // optional key=>value array of driver-specific connection options - $attributes // optional key=>value attriburtes + $queris // Queries to execute after the connection. ); } } ``` + +Use AuraSqlEnvModule to get the value from the environment variable each time at runtime, instead of specifying the value directly. + +```php + $this->install( + new AuraSqlEnvModule( + 'PDO_DSN', // getenv('PDO_DSN') + 'PDO_USER', // getenv('PDO_USER') + 'PDO_PASSWORD', // getenv('PDO_PASSWORD') + 'PDO_SLAVE' // getenv('PDO_SLAVE') + $options, // optional key=>value array of driver-specific connection options + $queris // Queries to execute after the connection. + ); +``` + ### DI trait * [AuraSqlInject](https://github.com/ray-di/Ray.AuraSqlModule/blob/1.x/src/AuraSqlInject.php) for `Aura\Sql\ExtendedPdoInterface` interface @@ -80,7 +95,6 @@ public function setLoggerDb(#[Named('log_db') ExtendedPdoInterface $pdo) } ``` - ### with no replication Use `NamedPdoModule ` to inject different named `Pdo` instance for **non** Replication use. @@ -96,6 +110,18 @@ class AppModule extends AbstractModule } ``` +Or + +```php +class AppModule extends AbstractModule +{ + protected function configure() + { + $this->install(new NamedPdoEnvModule('log_db', 'LOG_DSN', 'LOG_USERNAME', + } +} + + ### with replication You can set `$qaulifer` in 2nd parameter of AuraSqlReplicationModule. From 7040a791ec6acaf950f0e10a0753fa3b9350c9a2 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Fri, 30 Sep 2022 20:07:08 +0900 Subject: [PATCH 24/24] Fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bef0307..2eaf1a1 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ class AppModule extends AbstractModule 'password', 'slave1,slave2,slave3' // optional slave server list $options, // optional key=>value array of driver-specific connection options - $queris // Queries to execute after the connection. + $queries // Queries to execute after the connection. ); } } @@ -47,7 +47,7 @@ Use AuraSqlEnvModule to get the value from the environment variable each time at 'PDO_PASSWORD', // getenv('PDO_PASSWORD') 'PDO_SLAVE' // getenv('PDO_SLAVE') $options, // optional key=>value array of driver-specific connection options - $queris // Queries to execute after the connection. + $queries // Queries to execute after the connection. ); ```