From 19bdd2612d3a3b009605b3c049ccf83eb9819898 Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Wed, 1 Apr 2026 03:01:50 -0500 Subject: [PATCH 1/3] Provide Driver defaults Signed-off-by: Joey Smith --- src/Adapter/Driver/Pdo/AbstractPdo.php | 28 +++++-------------- .../Driver/Pdo/AbstractPdoConnection.php | 16 ----------- src/Adapter/Driver/Pdo/Statement.php | 10 ++----- src/Adapter/Driver/PdoConnectionInterface.php | 2 +- 4 files changed, 11 insertions(+), 45 deletions(-) diff --git a/src/Adapter/Driver/Pdo/AbstractPdo.php b/src/Adapter/Driver/Pdo/AbstractPdo.php index 1984c786c..c21860f31 100644 --- a/src/Adapter/Driver/Pdo/AbstractPdo.php +++ b/src/Adapter/Driver/Pdo/AbstractPdo.php @@ -7,8 +7,7 @@ use Override; use PDO; use PDOStatement; -use PhpDb\Adapter\Driver\ConnectionInterface; -use PhpDb\Adapter\Driver\Feature\DriverFeatureProviderInterface; +use PhpDb\Adapter\Driver\PdoConnectionInterface; use PhpDb\Adapter\Driver\PdoDriverAwareInterface; use PhpDb\Adapter\Driver\PdoDriverInterface; use PhpDb\Adapter\Driver\ResultInterface; @@ -25,29 +24,16 @@ use function preg_match; use function sprintf; +/** + * @property PdoConnectionInterface|PDO $connection + * @property ResultInterface $resultPrototype + * @property StatementInterface&PdoDriverAwareInterface $statementPrototype + */ abstract class AbstractPdo implements PdoDriverInterface, ProfilerAwareInterface { /** @internal */ protected ?ProfilerInterface $profiler; - public function __construct( - protected AbstractPdoConnection|PDO $connection, - protected StatementInterface&PdoDriverAwareInterface $statementPrototype, - protected ResultInterface $resultPrototype, - array $features = [], - ) { - if ($this->connection instanceof PdoDriverAwareInterface) { - $this->connection->setDriver($this); - } - - $this->statementPrototype->setDriver($this); - - // $features is not constructor promoted because $this->features is defined in the trait - if ($features !== [] && $this instanceof DriverFeatureProviderInterface) { - $this->addFeatures($features); - } - } - #[Override] public function setProfiler(ProfilerInterface $profiler): ProfilerAwareInterface { @@ -81,7 +67,7 @@ public function checkEnvironment(): bool } #[Override] - public function getConnection(): ConnectionInterface + public function getConnection(): PdoConnectionInterface { return $this->connection; } diff --git a/src/Adapter/Driver/Pdo/AbstractPdoConnection.php b/src/Adapter/Driver/Pdo/AbstractPdoConnection.php index e5c845ff8..d292a83ad 100644 --- a/src/Adapter/Driver/Pdo/AbstractPdoConnection.php +++ b/src/Adapter/Driver/Pdo/AbstractPdoConnection.php @@ -16,7 +16,6 @@ use PhpDb\Adapter\Exception; use PhpDb\Adapter\Exception\RuntimeException; -use function is_array; use function strtolower; abstract class AbstractPdoConnection extends AbstractConnection implements @@ -30,21 +29,6 @@ abstract class AbstractPdoConnection extends AbstractConnection implements /** @var ?PDO $resource */ protected $resource; - /** - * Constructor - * - * @throws Exception\InvalidArgumentException - */ - public function __construct( - PDO|array $connectionParameters - ) { - if (is_array($connectionParameters)) { - $this->setConnectionParameters($connectionParameters); - } elseif ($connectionParameters instanceof PDO) { - $this->setResource($connectionParameters); - } - } - #[Override] public function setDriver(PdoDriverInterface $driver): PdoDriverAwareInterface { diff --git a/src/Adapter/Driver/Pdo/Statement.php b/src/Adapter/Driver/Pdo/Statement.php index 71abff236..842ca4a31 100644 --- a/src/Adapter/Driver/Pdo/Statement.php +++ b/src/Adapter/Driver/Pdo/Statement.php @@ -44,7 +44,7 @@ class Statement implements StatementInterface, PdoDriverAwareInterface, Profiler protected bool $isPrepared = false; public function __construct( - protected ?ParameterContainer $parameterContainer = null, + protected ParameterContainer $parameterContainer = new ParameterContainer(), protected array $options = [], ) { } @@ -114,7 +114,7 @@ public function setParameterContainer(ParameterContainer $parameterContainer): S #[Override] public function getParameterContainer(): ParameterContainer { - return $this->parameterContainer ??= new ParameterContainer(); + return $this->parameterContainer; } /** @throws Exception\RuntimeException */ @@ -156,13 +156,9 @@ public function execute(null|array|ParameterContainer $parameters = null): ?Resu } /** START Standard ParameterContainer Merging Block */ - if (! $this->parameterContainer instanceof ParameterContainer) { - if ($parameters instanceof ParameterContainer) { + if ($parameters instanceof ParameterContainer) { $this->parameterContainer = $parameters; $parameters = null; - } else { - $this->parameterContainer = new ParameterContainer(); - } } if (is_array($parameters)) { diff --git a/src/Adapter/Driver/PdoConnectionInterface.php b/src/Adapter/Driver/PdoConnectionInterface.php index d160de404..c91486c6b 100644 --- a/src/Adapter/Driver/PdoConnectionInterface.php +++ b/src/Adapter/Driver/PdoConnectionInterface.php @@ -4,7 +4,7 @@ namespace PhpDb\Adapter\Driver; -interface PdoConnectionInterface +interface PdoConnectionInterface extends ConnectionInterface { public function getDsn(): string; } From 5840eadf9d4279fe3b81a51221800a23a7601775 Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Fri, 3 Apr 2026 13:15:34 -0500 Subject: [PATCH 2/3] Updates unit test Signed-off-by: Joey Smith --- src/Adapter/Driver/Pdo/AbstractPdo.php | 11 ++++---- src/Adapter/Driver/Pdo/Statement.php | 10 +++---- .../Driver/Pdo/TestAsset/TestConnection.php | 10 +++++++ .../Adapter/Driver/Pdo/TestAsset/TestPdo.php | 27 ++++++++++++++----- test/unit/TestAsset/ConnectionWrapper.php | 2 +- 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/Adapter/Driver/Pdo/AbstractPdo.php b/src/Adapter/Driver/Pdo/AbstractPdo.php index c21860f31..d916bce77 100644 --- a/src/Adapter/Driver/Pdo/AbstractPdo.php +++ b/src/Adapter/Driver/Pdo/AbstractPdo.php @@ -24,13 +24,14 @@ use function preg_match; use function sprintf; -/** - * @property PdoConnectionInterface|PDO $connection - * @property ResultInterface $resultPrototype - * @property StatementInterface&PdoDriverAwareInterface $statementPrototype - */ abstract class AbstractPdo implements PdoDriverInterface, ProfilerAwareInterface { + protected (PdoConnectionInterface&PdoDriverAwareInterface)|PDO $connection; + + protected StatementInterface&PdoDriverAwareInterface $statementPrototype; + + protected ResultInterface $resultPrototype; + /** @internal */ protected ?ProfilerInterface $profiler; diff --git a/src/Adapter/Driver/Pdo/Statement.php b/src/Adapter/Driver/Pdo/Statement.php index 842ca4a31..d2d232220 100644 --- a/src/Adapter/Driver/Pdo/Statement.php +++ b/src/Adapter/Driver/Pdo/Statement.php @@ -243,11 +243,9 @@ protected function bindParametersFromContainer(): void /** Perform a deep clone */ public function __clone(): void { - $this->isPrepared = false; - $this->parametersBound = false; - $this->resource = null; - if ($this->parameterContainer) { - $this->parameterContainer = clone $this->parameterContainer; - } + $this->isPrepared = false; + $this->parametersBound = false; + $this->resource = null; + $this->parameterContainer = clone $this->parameterContainer; } } diff --git a/test/unit/Adapter/Driver/Pdo/TestAsset/TestConnection.php b/test/unit/Adapter/Driver/Pdo/TestAsset/TestConnection.php index 8d76e1e14..836d9ecfd 100644 --- a/test/unit/Adapter/Driver/Pdo/TestAsset/TestConnection.php +++ b/test/unit/Adapter/Driver/Pdo/TestAsset/TestConnection.php @@ -9,6 +9,7 @@ use PhpDb\Adapter\Driver\ConnectionInterface; use PhpDb\Adapter\Driver\Pdo\AbstractPdoConnection; +use function is_array; use function sprintf; /** @@ -16,6 +17,15 @@ */ final class TestConnection extends AbstractPdoConnection { + public function __construct(PDO|array $connectionParameters) + { + if (is_array($connectionParameters)) { + $this->setConnectionParameters($connectionParameters); + } elseif ($connectionParameters instanceof PDO) { + $this->setResource($connectionParameters); + } + } + #[Override] public function connect(): ConnectionInterface { diff --git a/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php b/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php index 991795a70..cf345b4c3 100644 --- a/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php +++ b/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php @@ -6,6 +6,8 @@ use Override; use PDO; +use PhpDb\Adapter\Driver\Feature\DriverFeatureProviderInterface; +use PhpDb\Adapter\Driver\Feature\DriverFeatureProviderTrait; use PhpDb\Adapter\Driver\Pdo\AbstractPdo; use PhpDb\Adapter\Driver\Pdo\AbstractPdoConnection; use PhpDb\Adapter\Driver\Pdo\Result; @@ -16,8 +18,10 @@ /** * Test asset for AbstractPdo - provides a concrete implementation for testing */ -final class TestPdo extends AbstractPdo +final class TestPdo extends AbstractPdo implements DriverFeatureProviderInterface { + use DriverFeatureProviderTrait; + public function __construct( array|AbstractPdoConnection|PDO $connection, ?Statement $statement = null, @@ -28,12 +32,20 @@ public function __construct( $connection = new TestConnection($connection); } - parent::__construct( - $connection, - $statement ?? new Statement(), - $result ?? new Result(), - $features - ); + $this->connection = $connection; + $this->statementPrototype = $statement ?? new Statement(); + $this->resultPrototype = $result ?? new Result(); + + if (! $this->connection instanceof PDO) { + $this->connection->setDriver($this); + } + + $this->statementPrototype->setDriver($this); + + // $features is not constructor promoted because $this->features is defined in the trait + if ($features !== []) { + $this->addFeatures($features); + } } /** @@ -65,6 +77,7 @@ public function getDatabasePlatformName(string $nameFormat = self::NAME_FORMAT_C $pdoDriver = $this->connection->getResource()->getAttribute(PDO::ATTR_DRIVER_NAME); } + // todo: None of this belongs here return match ($nameFormat) { self::NAME_FORMAT_CAMELCASE => match ($pdoDriver) { 'sqlsrv', 'dblib', 'mssql' => 'SqlServer', diff --git a/test/unit/TestAsset/ConnectionWrapper.php b/test/unit/TestAsset/ConnectionWrapper.php index 6911391a2..0ac80f792 100644 --- a/test/unit/TestAsset/ConnectionWrapper.php +++ b/test/unit/TestAsset/ConnectionWrapper.php @@ -16,7 +16,7 @@ final class ConnectionWrapper extends AbstractPdoConnection public function __construct( PDO $connectionParameters = new PdoStubDriver() ) { - parent::__construct($connectionParameters); + $this->setResource($connectionParameters); } public function connect(): ConnectionInterface From 92639d6cd6be465aa2b57ca3118f50128b74dd17 Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Sat, 4 Apr 2026 13:30:04 -0500 Subject: [PATCH 3/3] This commit removes getDatabasePlatformName from the API. These changes will need to be made downstream in the driver packages. Signed-off-by: Joey Smith --- phpstan-baseline.neon | 6 --- src/Adapter/Driver/DriverInterface.php | 5 --- src/Adapter/Driver/Pdo/AbstractPdo.php | 3 +- .../AdapterInterfaceDelegatorTest.php | 8 +--- .../Adapter/Driver/Pdo/ConnectionTest.php | 10 ++--- test/unit/Adapter/Driver/Pdo/PdoTest.php | 9 ----- .../Adapter/Driver/Pdo/TestAsset/TestPdo.php | 39 ------------------- 7 files changed, 7 insertions(+), 73 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2bc1099cc..d78b460f6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6,12 +6,6 @@ parameters: count: 1 path: src/Sql/AbstractSql.php - - - message: '#^Cannot call method getAttribute\(\) on resource\.$#' - identifier: method.nonObject - count: 1 - path: test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php - - message: '#^Parameter \#1 \$attribute \(string\) of method PhpDbTest\\Adapter\\Driver\\TestAsset\\PdoMock\:\:getAttribute\(\) should be compatible with parameter \$attribute \(int\) of method PDO\:\:getAttribute\(\)$#' identifier: method.childParameterType diff --git a/src/Adapter/Driver/DriverInterface.php b/src/Adapter/Driver/DriverInterface.php index 1ca3138df..6cbb2e407 100644 --- a/src/Adapter/Driver/DriverInterface.php +++ b/src/Adapter/Driver/DriverInterface.php @@ -10,11 +10,6 @@ interface DriverInterface { public const PARAMETERIZATION_POSITIONAL = 'positional'; public const PARAMETERIZATION_NAMED = 'named'; - public const NAME_FORMAT_CAMELCASE = 'camelCase'; - public const NAME_FORMAT_NATURAL = 'natural'; - - /** Get database platform name */ - public function getDatabasePlatformName(string $nameFormat = DriverInterface::NAME_FORMAT_CAMELCASE): string; /** * Check environment diff --git a/src/Adapter/Driver/Pdo/AbstractPdo.php b/src/Adapter/Driver/Pdo/AbstractPdo.php index d916bce77..5d7d274e9 100644 --- a/src/Adapter/Driver/Pdo/AbstractPdo.php +++ b/src/Adapter/Driver/Pdo/AbstractPdo.php @@ -7,6 +7,7 @@ use Override; use PDO; use PDOStatement; +use PhpDb\Adapter\Driver\AbstractConnection; use PhpDb\Adapter\Driver\PdoConnectionInterface; use PhpDb\Adapter\Driver\PdoDriverAwareInterface; use PhpDb\Adapter\Driver\PdoDriverInterface; @@ -26,7 +27,7 @@ abstract class AbstractPdo implements PdoDriverInterface, ProfilerAwareInterface { - protected (PdoConnectionInterface&PdoDriverAwareInterface)|PDO $connection; + protected (PdoConnectionInterface&AbstractConnection&PdoDriverAwareInterface)|PDO $connection; protected StatementInterface&PdoDriverAwareInterface $statementPrototype; diff --git a/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php b/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php index e42c3ae2c..c382820ca 100644 --- a/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php +++ b/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php @@ -213,11 +213,6 @@ public function testDelegatorWithServiceManagerAndCustomAdapterName(): void public function testDelegatorWithPluginManager(): void { - $this->markTestSkipped( - 'Test requires factory-based plugin manager configuration to pass options to constructor' - ); - - /** @phpstan-ignore deadCode.unreachable */ $databaseAdapter = new Adapter( $this->createMock(DriverInterface::class), $this->createMock(PlatformInterface::class), @@ -241,7 +236,6 @@ public function testDelegatorWithPluginManager(): void ], ]; - /** @var AbstractPluginManager $pluginManager */ $pluginManager = new class ($container, $pluginManagerConfig) extends AbstractPluginManager { public function validate(mixed $instance): void { @@ -254,7 +248,7 @@ public function validate(mixed $instance): void ]; /** @var ConcreteAdapterAwareObject $result */ - $result = $pluginManager->get( + $result = $pluginManager->build( ConcreteAdapterAwareObject::class, $options ); diff --git a/test/unit/Adapter/Driver/Pdo/ConnectionTest.php b/test/unit/Adapter/Driver/Pdo/ConnectionTest.php index 23f4bcc8c..ccd334715 100644 --- a/test/unit/Adapter/Driver/Pdo/ConnectionTest.php +++ b/test/unit/Adapter/Driver/Pdo/ConnectionTest.php @@ -40,10 +40,8 @@ protected function setUp(): void */ public function testResource(): void { - $this->markTestSkipped('Test requires concrete driver implementation with DSN building logic'); - /** @phpstan-ignore deadCode.unreachable */ - $this->expectException(InvalidConnectionParametersException::class); - $this->connection->getResource(); + $resource = $this->connection->getResource(); + self::assertNotNull($resource); } /** @@ -65,7 +63,7 @@ public function testGetDsn(): void #[Group('2622')] public function testArrayOfConnectionParametersCreatesCorrectDsn(): void { - $this->markTestSkipped('Test requires concrete MySQL driver implementation with DSN building logic'); + $this->markTestSkipped('This test will pass with current sqlite::memory: prefix, but shouldn\'t.'); /** @phpstan-ignore deadCode.unreachable */ $this->connection->setConnectionParameters([ 'driver' => 'pdo_mysql', @@ -80,7 +78,7 @@ public function testArrayOfConnectionParametersCreatesCorrectDsn(): void } $responseString = $this->connection->getDsn(); - self::assertStringStartsWith('mysql:', $responseString); + self::assertStringStartsWith('sqlite::memory:', $responseString); self::assertStringContainsString('charset=utf8', $responseString); self::assertStringContainsString('dbname=foo', $responseString); self::assertStringContainsString('port=3306', $responseString); diff --git a/test/unit/Adapter/Driver/Pdo/PdoTest.php b/test/unit/Adapter/Driver/Pdo/PdoTest.php index 0b1e80d49..2e5ec02be 100644 --- a/test/unit/Adapter/Driver/Pdo/PdoTest.php +++ b/test/unit/Adapter/Driver/Pdo/PdoTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -#[CoversMethod(AbstractPdo::class, 'getDatabasePlatformName')] #[CoversMethod(AbstractPdo::class, 'getResultPrototype')] final class PdoTest extends TestCase { @@ -30,14 +29,6 @@ protected function setUp(): void $this->pdo = new TestPdo([]); } - public function testGetDatabasePlatformName(): void - { - // Test platform name for SqlServer - $this->pdo->getConnection()->setConnectionParameters(['pdodriver' => 'sqlsrv']); - self::assertEquals('SqlServer', $this->pdo->getDatabasePlatformName()); - self::assertEquals('SQLServer', $this->pdo->getDatabasePlatformName(DriverInterface::NAME_FORMAT_NATURAL)); - } - /** @psalm-return array */ public static function getParamsAndType(): array { diff --git a/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php b/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php index cf345b4c3..6f4a16e72 100644 --- a/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php +++ b/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php @@ -13,8 +13,6 @@ use PhpDb\Adapter\Driver\Pdo\Result; use PhpDb\Adapter\Driver\Pdo\Statement; -use function ucfirst; - /** * Test asset for AbstractPdo - provides a concrete implementation for testing */ @@ -61,41 +59,4 @@ public function createResult($resource): Result $result->initialize($resource, $this->connection->getLastGeneratedValue()); return $result; } - - /** - * Get database platform name - */ - #[Override] - public function getDatabasePlatformName(string $nameFormat = self::NAME_FORMAT_CAMELCASE): string - { - $pdoDriver = null; - if ($this->connection instanceof TestConnection) { - $pdoDriver = $this->connection->getConnectionParameters()['pdodriver'] ?? null; - } - - if ($pdoDriver === null && $this->connection->isConnected()) { - $pdoDriver = $this->connection->getResource()->getAttribute(PDO::ATTR_DRIVER_NAME); - } - - // todo: None of this belongs here - return match ($nameFormat) { - self::NAME_FORMAT_CAMELCASE => match ($pdoDriver) { - 'sqlsrv', 'dblib', 'mssql' => 'SqlServer', - 'mysql' => 'MySql', - 'oci' => 'Oracle', - 'pgsql' => 'PostgreSql', - 'sqlite' => 'Sqlite', - default => 'Sql92', - }, - self::NAME_FORMAT_NATURAL => match ($pdoDriver) { - 'sqlsrv', 'dblib', 'mssql' => 'SQLServer', - 'mysql' => 'MySQL', - 'oci' => 'Oracle', - 'pgsql' => 'PostgreSQL', - 'sqlite' => 'SQLite', - default => 'SQL92', - }, - default => $pdoDriver !== null ? ucfirst($pdoDriver) : 'SQL92', - }; - } }