diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2bc1099c..d78b460f 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 1ca3138d..6cbb2e40 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 1984c786..5d7d274e 100644 --- a/src/Adapter/Driver/Pdo/AbstractPdo.php +++ b/src/Adapter/Driver/Pdo/AbstractPdo.php @@ -7,8 +7,8 @@ use Override; use PDO; use PDOStatement; -use PhpDb\Adapter\Driver\ConnectionInterface; -use PhpDb\Adapter\Driver\Feature\DriverFeatureProviderInterface; +use PhpDb\Adapter\Driver\AbstractConnection; +use PhpDb\Adapter\Driver\PdoConnectionInterface; use PhpDb\Adapter\Driver\PdoDriverAwareInterface; use PhpDb\Adapter\Driver\PdoDriverInterface; use PhpDb\Adapter\Driver\ResultInterface; @@ -27,26 +27,14 @@ abstract class AbstractPdo implements PdoDriverInterface, ProfilerAwareInterface { - /** @internal */ - protected ?ProfilerInterface $profiler; + protected (PdoConnectionInterface&AbstractConnection&PdoDriverAwareInterface)|PDO $connection; - 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); - } + protected StatementInterface&PdoDriverAwareInterface $statementPrototype; - $this->statementPrototype->setDriver($this); + protected ResultInterface $resultPrototype; - // $features is not constructor promoted because $this->features is defined in the trait - if ($features !== [] && $this instanceof DriverFeatureProviderInterface) { - $this->addFeatures($features); - } - } + /** @internal */ + protected ?ProfilerInterface $profiler; #[Override] public function setProfiler(ProfilerInterface $profiler): ProfilerAwareInterface @@ -81,7 +69,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 e5c845ff..d292a83a 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 71abff23..d2d23222 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)) { @@ -247,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/src/Adapter/Driver/PdoConnectionInterface.php b/src/Adapter/Driver/PdoConnectionInterface.php index d160de40..c91486c6 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; } diff --git a/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php b/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php index e42c3ae2..c382820c 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 23f4bcc8..ccd33471 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 0b1e80d4..2e5ec02b 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/TestConnection.php b/test/unit/Adapter/Driver/Pdo/TestAsset/TestConnection.php index 8d76e1e1..836d9ecf 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 991795a7..6f4a16e7 100644 --- a/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php +++ b/test/unit/Adapter/Driver/Pdo/TestAsset/TestPdo.php @@ -6,18 +6,20 @@ 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; use PhpDb\Adapter\Driver\Pdo\Statement; -use function ucfirst; - /** * 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 +30,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); + } } /** @@ -49,40 +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); - } - - 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', - }; - } } diff --git a/test/unit/TestAsset/ConnectionWrapper.php b/test/unit/TestAsset/ConnectionWrapper.php index 6911391a..0ac80f79 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