From 70eb6bb0fcb82e93b3ab0d8aa4de7a9b89a12df3 Mon Sep 17 00:00:00 2001 From: Simon Mundy Date: Tue, 24 Feb 2026 11:18:54 +1100 Subject: [PATCH 1/5] - Reintroduced Adapter-specific Platform Decorators - Added resolveDecoratorsForPlatform method to bypass recursion Signed-off-by: Simon Mundy --- src/Sql/Platform/Platform.php | 23 ++++++++++++++++++- .../Platform/PlatformDecoratorInterface.php | 3 +++ test/unit/TestAsset/DeleteDecorator.php | 6 +++++ test/unit/TestAsset/InsertDecorator.php | 6 +++++ test/unit/TestAsset/SelectDecorator.php | 6 +++++ test/unit/TestAsset/UpdateDecorator.php | 6 +++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/Sql/Platform/Platform.php b/src/Sql/Platform/Platform.php index 9d522024..87d37f27 100644 --- a/src/Sql/Platform/Platform.php +++ b/src/Sql/Platform/Platform.php @@ -60,7 +60,7 @@ public function getTypeDecorator( $platformName = $this->resolvePlatformName($adapterOrPlatform); if (! isset($this->decorators[$platformName])) { - return $subject; + $this->decorators[$platformName] = $this->resolveDecoratorsForPlatform(); } $subjectClass = $subject::class; @@ -84,9 +84,30 @@ public function getTypeDecorator( public function getDecorators(): array { $platformName = $this->resolvePlatformName($this->getDefaultPlatform()); + + if (! isset($this->decorators[$platformName])) { + $this->decorators[$platformName] = $this->resolveDecoratorsForPlatform(); + } + return $this->decorators[$platformName]; } + /** + * Resolve decorators from the adapter platform's SQL decorator. + * + * @return array + */ + private function resolveDecoratorsForPlatform(): array + { + $sqlDecorator = $this->defaultPlatform->getSqlPlatformDecorator(); + + if ($sqlDecorator instanceof self) { + return []; + } + + return $sqlDecorator->getDecorators(); + } + /** * {@inheritDoc} * diff --git a/src/Sql/Platform/PlatformDecoratorInterface.php b/src/Sql/Platform/PlatformDecoratorInterface.php index b39bedba..ae8c82af 100644 --- a/src/Sql/Platform/PlatformDecoratorInterface.php +++ b/src/Sql/Platform/PlatformDecoratorInterface.php @@ -12,4 +12,7 @@ interface PlatformDecoratorInterface public function setSubject( SqlInterface|PreparableSqlInterface|null $subject ): PlatformDecoratorInterface; + + /** @return array */ + public function getDecorators(): array; } diff --git a/test/unit/TestAsset/DeleteDecorator.php b/test/unit/TestAsset/DeleteDecorator.php index 5e30307c..f6a2881c 100644 --- a/test/unit/TestAsset/DeleteDecorator.php +++ b/test/unit/TestAsset/DeleteDecorator.php @@ -19,4 +19,10 @@ public function setSubject( $this->subject = $subject; return $this; } + + /** {@inheritDoc} */ + public function getDecorators(): array + { + return []; + } } diff --git a/test/unit/TestAsset/InsertDecorator.php b/test/unit/TestAsset/InsertDecorator.php index 04d8af64..7a79abc9 100644 --- a/test/unit/TestAsset/InsertDecorator.php +++ b/test/unit/TestAsset/InsertDecorator.php @@ -19,4 +19,10 @@ public function setSubject( $this->subject = $subject; return $this; } + + /** {@inheritDoc} */ + public function getDecorators(): array + { + return []; + } } diff --git a/test/unit/TestAsset/SelectDecorator.php b/test/unit/TestAsset/SelectDecorator.php index 5f0bb004..40ad782e 100644 --- a/test/unit/TestAsset/SelectDecorator.php +++ b/test/unit/TestAsset/SelectDecorator.php @@ -16,4 +16,10 @@ public function setSubject(?object $subject): SelectDecorator $this->subject = $subject; return $this; } + + /** {@inheritDoc} */ + public function getDecorators(): array + { + return []; + } } diff --git a/test/unit/TestAsset/UpdateDecorator.php b/test/unit/TestAsset/UpdateDecorator.php index d5c01bba..10ef52c4 100644 --- a/test/unit/TestAsset/UpdateDecorator.php +++ b/test/unit/TestAsset/UpdateDecorator.php @@ -19,4 +19,10 @@ public function setSubject( $this->subject = $subject; return $this; } + + /** {@inheritDoc} */ + public function getDecorators(): array + { + return []; + } } From 0f772e197df8137c25166bf38e6be4e1b4c8b028 Mon Sep 17 00:00:00 2001 From: Simon Mundy Date: Tue, 24 Feb 2026 19:50:11 +1100 Subject: [PATCH 2/5] Removed unnecessary getDecorators --- test/unit/TestAsset/DeleteDecorator.php | 6 ------ test/unit/TestAsset/InsertDecorator.php | 6 ------ test/unit/TestAsset/SelectDecorator.php | 6 ------ test/unit/TestAsset/UpdateDecorator.php | 6 ------ 4 files changed, 24 deletions(-) diff --git a/test/unit/TestAsset/DeleteDecorator.php b/test/unit/TestAsset/DeleteDecorator.php index f6a2881c..5e30307c 100644 --- a/test/unit/TestAsset/DeleteDecorator.php +++ b/test/unit/TestAsset/DeleteDecorator.php @@ -19,10 +19,4 @@ public function setSubject( $this->subject = $subject; return $this; } - - /** {@inheritDoc} */ - public function getDecorators(): array - { - return []; - } } diff --git a/test/unit/TestAsset/InsertDecorator.php b/test/unit/TestAsset/InsertDecorator.php index 7a79abc9..04d8af64 100644 --- a/test/unit/TestAsset/InsertDecorator.php +++ b/test/unit/TestAsset/InsertDecorator.php @@ -19,10 +19,4 @@ public function setSubject( $this->subject = $subject; return $this; } - - /** {@inheritDoc} */ - public function getDecorators(): array - { - return []; - } } diff --git a/test/unit/TestAsset/SelectDecorator.php b/test/unit/TestAsset/SelectDecorator.php index 40ad782e..5f0bb004 100644 --- a/test/unit/TestAsset/SelectDecorator.php +++ b/test/unit/TestAsset/SelectDecorator.php @@ -16,10 +16,4 @@ public function setSubject(?object $subject): SelectDecorator $this->subject = $subject; return $this; } - - /** {@inheritDoc} */ - public function getDecorators(): array - { - return []; - } } diff --git a/test/unit/TestAsset/UpdateDecorator.php b/test/unit/TestAsset/UpdateDecorator.php index 10ef52c4..d5c01bba 100644 --- a/test/unit/TestAsset/UpdateDecorator.php +++ b/test/unit/TestAsset/UpdateDecorator.php @@ -19,10 +19,4 @@ public function setSubject( $this->subject = $subject; return $this; } - - /** {@inheritDoc} */ - public function getDecorators(): array - { - return []; - } } From 56dcff692eebe4fcbad419eff1543bffc10846dd Mon Sep 17 00:00:00 2001 From: Simon Mundy Date: Tue, 24 Feb 2026 19:50:40 +1100 Subject: [PATCH 3/5] Removed unnecessary getDecorators --- src/Sql/Platform/Platform.php | 22 +------------------ .../Platform/PlatformDecoratorInterface.php | 3 --- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/Sql/Platform/Platform.php b/src/Sql/Platform/Platform.php index 87d37f27..6ce2f62b 100644 --- a/src/Sql/Platform/Platform.php +++ b/src/Sql/Platform/Platform.php @@ -60,7 +60,7 @@ public function getTypeDecorator( $platformName = $this->resolvePlatformName($adapterOrPlatform); if (! isset($this->decorators[$platformName])) { - $this->decorators[$platformName] = $this->resolveDecoratorsForPlatform(); + return $subject; } $subjectClass = $subject::class; @@ -85,29 +85,9 @@ public function getDecorators(): array { $platformName = $this->resolvePlatformName($this->getDefaultPlatform()); - if (! isset($this->decorators[$platformName])) { - $this->decorators[$platformName] = $this->resolveDecoratorsForPlatform(); - } - return $this->decorators[$platformName]; } - /** - * Resolve decorators from the adapter platform's SQL decorator. - * - * @return array - */ - private function resolveDecoratorsForPlatform(): array - { - $sqlDecorator = $this->defaultPlatform->getSqlPlatformDecorator(); - - if ($sqlDecorator instanceof self) { - return []; - } - - return $sqlDecorator->getDecorators(); - } - /** * {@inheritDoc} * diff --git a/src/Sql/Platform/PlatformDecoratorInterface.php b/src/Sql/Platform/PlatformDecoratorInterface.php index ae8c82af..b39bedba 100644 --- a/src/Sql/Platform/PlatformDecoratorInterface.php +++ b/src/Sql/Platform/PlatformDecoratorInterface.php @@ -12,7 +12,4 @@ interface PlatformDecoratorInterface public function setSubject( SqlInterface|PreparableSqlInterface|null $subject ): PlatformDecoratorInterface; - - /** @return array */ - public function getDecorators(): array; } From a0fee77354f0164c02d989cdccead5545319c12e Mon Sep 17 00:00:00 2001 From: Simon Mundy Date: Tue, 24 Feb 2026 20:16:45 +1100 Subject: [PATCH 4/5] - Extended PlatformDecoratorInterface - Derive SQL Decorator from Adapter Platform - Update tests to use concrete instances --- src/Sql/Platform/PlatformDecoratorInterface.php | 2 +- src/Sql/Sql.php | 8 ++++---- test/unit/Sql/AbstractSqlFunctionalTestCase.php | 1 + test/unit/TableGateway/AbstractTableGatewayTest.php | 4 ++++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Sql/Platform/PlatformDecoratorInterface.php b/src/Sql/Platform/PlatformDecoratorInterface.php index b39bedba..5ec33f6a 100644 --- a/src/Sql/Platform/PlatformDecoratorInterface.php +++ b/src/Sql/Platform/PlatformDecoratorInterface.php @@ -7,7 +7,7 @@ use PhpDb\Sql\PreparableSqlInterface; use PhpDb\Sql\SqlInterface; -interface PlatformDecoratorInterface +interface PlatformDecoratorInterface extends PreparableSqlInterface, SqlInterface { public function setSubject( SqlInterface|PreparableSqlInterface|null $subject diff --git a/src/Sql/Sql.php b/src/Sql/Sql.php index 50b45e48..10907b84 100644 --- a/src/Sql/Sql.php +++ b/src/Sql/Sql.php @@ -15,15 +15,15 @@ class Sql protected TableIdentifier|string|array|null $table; - protected Platform\Platform $sqlPlatform; + protected Platform\PlatformDecoratorInterface $sqlPlatform; public function __construct( AdapterInterface $adapter, array|string|TableIdentifier|null $table = null ) { - $this->adapter = $adapter; $this->table = $table; - $this->sqlPlatform = new Platform\Platform($adapter->getPlatform()); + $this->adapter = $adapter; + $this->sqlPlatform = $adapter->getPlatform()->getSqlPlatformDecorator(); } public function getAdapter(): ?AdapterInterface @@ -51,7 +51,7 @@ public function getTable(): array|string|TableIdentifier|null return $this->table; } - public function getSqlPlatform(): ?Platform\Platform + public function getSqlPlatform(): ?Platform\PlatformDecoratorInterface { return $this->sqlPlatform; } diff --git a/test/unit/Sql/AbstractSqlFunctionalTestCase.php b/test/unit/Sql/AbstractSqlFunctionalTestCase.php index 227b18f0..c93ddaae 100644 --- a/test/unit/Sql/AbstractSqlFunctionalTestCase.php +++ b/test/unit/Sql/AbstractSqlFunctionalTestCase.php @@ -265,6 +265,7 @@ public function test(PreparableSqlInterface|SqlInterface $sqlObject, string $pla $platform = $sql->getSqlPlatform(); $this->assertNotNull($platform); + $this->assertInstanceOf(Sql\Platform\AbstractPlatform::class, $platform); $platform->setTypeDecorator($type, $decorator); } } diff --git a/test/unit/TableGateway/AbstractTableGatewayTest.php b/test/unit/TableGateway/AbstractTableGatewayTest.php index b6c14f79..a53bd1ce 100644 --- a/test/unit/TableGateway/AbstractTableGatewayTest.php +++ b/test/unit/TableGateway/AbstractTableGatewayTest.php @@ -79,6 +79,10 @@ protected function setUp(): void $mockResult->expects($this->any())->method('getAffectedRows')->willReturn(5); $mockPlatform = $this->getMockBuilder(PlatformInterface::class)->getMock(); + $mockPlatform->expects($this->any())->method('getName')->willReturn('sql92'); + $mockPlatform->expects($this->any()) + ->method('getSqlPlatformDecorator') + ->willReturn(new Sql\Platform\Platform($mockPlatform)); $mockResultSet = $this->getMockBuilder(ResultSetInterface::class)->getMock(); From 2492690d3c09e5b50dcbaa2b7baa576764651372 Mon Sep 17 00:00:00 2001 From: Simon Mundy Date: Fri, 27 Feb 2026 12:33:39 +1100 Subject: [PATCH 5/5] Introduced type checks for SqlPlatform for code safety Signed-off-by: Simon Mundy --- src/Sql/Platform/AbstractPlatform.php | 2 +- .../Platform/PlatformDecoratorInterface.php | 2 +- src/Sql/Sql.php | 24 ++++++++++++++----- .../RowGateway/AbstractRowGatewayTest.php | 4 ++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Sql/Platform/AbstractPlatform.php b/src/Sql/Platform/AbstractPlatform.php index 195cc118..cc8d5d67 100644 --- a/src/Sql/Platform/AbstractPlatform.php +++ b/src/Sql/Platform/AbstractPlatform.php @@ -83,7 +83,7 @@ public function getSqlString(?PlatformInterface $adapterPlatform = null): string if (! $this->subject instanceof SqlInterface) { throw new Exception\RuntimeException( 'The subject does not appear to implement PhpDb\Sql\SqlInterface, thus calling ' - . 'prepareStatement() has no effect' + . 'getSqlString() has no effect' ); } diff --git a/src/Sql/Platform/PlatformDecoratorInterface.php b/src/Sql/Platform/PlatformDecoratorInterface.php index 5ec33f6a..b39bedba 100644 --- a/src/Sql/Platform/PlatformDecoratorInterface.php +++ b/src/Sql/Platform/PlatformDecoratorInterface.php @@ -7,7 +7,7 @@ use PhpDb\Sql\PreparableSqlInterface; use PhpDb\Sql\SqlInterface; -interface PlatformDecoratorInterface extends PreparableSqlInterface, SqlInterface +interface PlatformDecoratorInterface { public function setSubject( SqlInterface|PreparableSqlInterface|null $subject diff --git a/src/Sql/Sql.php b/src/Sql/Sql.php index 10907b84..525c4efb 100644 --- a/src/Sql/Sql.php +++ b/src/Sql/Sql.php @@ -109,10 +109,17 @@ public function prepareStatementForSqlObject( ?StatementInterface $statement = null, ?AdapterInterface $adapter = null ): StatementInterface { + if (! $this->sqlPlatform instanceof PreparableSqlInterface) { + throw new Exception\RuntimeException( + 'The subject does not implement PreparableSqlInterface' + ); + } + $adapter ??= $this->adapter; $statement ??= $adapter->getDriver()->createStatement(); - $this->sqlPlatform->setSubject($sqlObject)->prepareStatement($adapter, $statement); + $this->sqlPlatform->setSubject($sqlObject); + $this->sqlPlatform->prepareStatement($adapter, $statement); return $statement; } @@ -122,11 +129,16 @@ public function prepareStatementForSqlObject( */ public function buildSqlString(SqlInterface $sqlObject, ?AdapterInterface $adapter = null): string { - return $this - ->sqlPlatform - ->setSubject($sqlObject) - ->getSqlString( - $adapter instanceof AdapterInterface ? $adapter->getPlatform() : $this->adapter->getPlatform() + if (! $this->sqlPlatform instanceof SqlInterface) { + throw new Exception\RuntimeException( + 'The subject does not implement SqlInterface' ); + } + + $this->sqlPlatform->setSubject($sqlObject); + + return $this->sqlPlatform->getSqlString( + $adapter instanceof AdapterInterface ? $adapter->getPlatform() : $this->adapter->getPlatform() + ); } } diff --git a/test/unit/RowGateway/AbstractRowGatewayTest.php b/test/unit/RowGateway/AbstractRowGatewayTest.php index af6f1243..aa63b7bd 100644 --- a/test/unit/RowGateway/AbstractRowGatewayTest.php +++ b/test/unit/RowGateway/AbstractRowGatewayTest.php @@ -10,7 +10,6 @@ use PhpDb\Adapter\Driver\DriverInterface; use PhpDb\Adapter\Driver\ResultInterface; use PhpDb\Adapter\Driver\StatementInterface; -use PhpDb\Adapter\Platform\PlatformInterface; use PhpDb\RowGateway\AbstractRowGateway; use PhpDb\RowGateway\Exception\InvalidArgumentException; use PhpDb\RowGateway\Exception\RuntimeException; @@ -18,6 +17,7 @@ use PhpDb\RowGateway\RowGateway; use PhpDb\Sql\Select; use PhpDb\Sql\Sql; +use PhpDbTest\TestAsset\TrustingSql92Platform; use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\Attributes\RequiresPhp; @@ -77,7 +77,7 @@ protected function setUp(): void ->setConstructorArgs( [ $mockDriver, - $this->getMockBuilder(PlatformInterface::class)->getMock(), + new TrustingSql92Platform(), ] )->getMock();