From cc911777a1e6f73a5b3c2a6d9f05e0b5d1cb5ed9 Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Wed, 7 Oct 2026 20:57:53 -0500 Subject: [PATCH 1/4] Fix unresolvable docblock references and prune the dead baseline entries The phpdb $resource contract change (php-db/phpdb#227, 0c4ed091) made the committed baseline stale and surfaced 21 pre-existing findings. All 21 are fixed at source: no new suppression was added, and mago pruned the 9 now-dead baseline entries it left behind (87 -> 78 blocks). Two root causes: * `@throws Exception\ExceptionInterface` resolved to PhpDb\Adapter\Exception\ExceptionInterface, which phpdb does not ship. Every affected file imports PhpDb\Adapter\Exception, so the short form named a non-existent class and the annotations were silently inert, reporting 14 throws as unhandled. Repointed to the real \PhpDb\Exception\ExceptionInterface in 16 places. * SelectDecorator::processLimit()/processOffset() tested only $parameterContainer before dereferencing the nullable ?DriverInterface $driver parameter. Adding the null check also let mago resolve the call result and removed a less-specific-nested-return-statement cascade. Runtime no-op: AbstractSql::buildSqlString() always supplies a driver, and a null driver now correctly falls through to the literal branch. Also tightens the LIMIT/OFFSET and ALTER TABLE ADD COLUMN return shapes, casts the LIMIT/OFFSET literals to string (vsprintf renders 10 and '10' identically), reindexes the ADD COLUMN sql list (the parent never reads the inner keys), and drops 4 redundant @var annotations. The lock is bumped to phpdb 0c4ed091 because CI's qa job installs from the lock; the previous reference still carried @return resource and would leave the pruned findings un-suppressed. Verified: mago format --check, lint, analyze and guard all clean; phpunit unit suite 193 tests / 385 assertions; integration suite 327 tests / 508 assertions against MySQL 8.0. --- analysis-baseline.toml | 54 ------------------- composer.lock | 14 ++--- src/Connection.php | 12 ++--- src/Container/ConnectionInterfaceFactory.php | 2 +- .../PdoConnectionInterfaceFactory.php | 2 +- src/Driver.php | 2 +- src/Pdo/Connection.php | 2 +- src/Result.php | 4 +- src/Sql/Ddl/AlterTableDecorator.php | 9 ++-- src/Sql/Ddl/CreateTableDecorator.php | 1 - src/Sql/SelectDecorator.php | 12 ++--- src/Statement.php | 4 +- 12 files changed, 30 insertions(+), 88 deletions(-) diff --git a/analysis-baseline.toml b/analysis-baseline.toml index f333111..50523e6 100644 --- a/analysis-baseline.toml +++ b/analysis-baseline.toml @@ -6,12 +6,6 @@ code = "falsable-return-statement" message = '''Function `PhpDb\Mysql\AdapterPlatform::quoteViaDriver` is declared to return `null|string` but possibly returns 'false' (inferred as `false|string`).''' count = 1 -[[issues]] -file = "src/AdapterPlatform.php" -code = "impossible-condition" -message = "This condition (type `false`) will always evaluate to false." -count = 1 - [[issues]] file = "src/AdapterPlatform.php" code = "invalid-return-statement" @@ -30,12 +24,6 @@ code = "possibly-invalid-argument" message = 'Possible argument type mismatch for argument #1 of `PhpDb\Mysql\AdapterPlatform::quoteViaDriver`: expected `string`, but possibly received `bool|float|int|string`.' count = 1 -[[issues]] -file = "src/Connection.php" -code = "incompatible-property-type" -message = 'Property `PhpDb\Mysql\Connection::$resource` has an incompatible type declaration from docblock.' -count = 1 - [[issues]] file = "src/Connection.php" code = "invalid-iterator" @@ -258,12 +246,6 @@ code = "mixed-argument" message = 'Invalid argument type for argument #1 of `PhpDb\Mysql\Statement::__construct`: expected `bool`, but found `nonnull`.' count = 1 -[[issues]] -file = "src/Driver.php" -code = "docblock-type-mismatch" -message = "Docblock type mismatch for variable `$resource`." -count = 1 - [[issues]] file = "src/Driver.php" code = "incompatible-parameter-type" @@ -456,42 +438,6 @@ code = "unused-property" message = "Property `$numberOfRows` is never used." count = 1 -[[issues]] -file = "src/Sql/SelectDecorator.php" -code = "invalid-property-assignment-value" -message = "Invalid type for property `$specifications`: expected `array>|array`, but got `array{'limit': string('LIMIT 18446744073709551615'), ...>}|array{'limit': string('LIMIT 18446744073709551615'), ...}`." -count = 1 - -[[issues]] -file = "src/Sql/SelectDecorator.php" -code = "invalid-return-statement" -message = 'Invalid return type for function `PhpDb\Mysql\Sql\SelectDecorator::processLimit`: expected `array|null`, but found `list{int|string}`.' -count = 1 - -[[issues]] -file = "src/Sql/SelectDecorator.php" -code = "invalid-return-statement" -message = 'Invalid return type for function `PhpDb\Mysql\Sql\SelectDecorator::processOffset`: expected `array|null`, but found `list{int|string}`.' -count = 1 - -[[issues]] -file = "src/Sql/SelectDecorator.php" -code = "less-specific-nested-return-statement" -message = '''Returned type `list{mixed}` is less specific than the declared return type `array|null` for function `PhpDb\Mysql\Sql\SelectDecorator::processLimit` due to nested 'mixed'.''' -count = 1 - -[[issues]] -file = "src/Sql/SelectDecorator.php" -code = "less-specific-nested-return-statement" -message = '''Returned type `list{mixed}` is less specific than the declared return type `array|null` for function `PhpDb\Mysql\Sql\SelectDecorator::processOffset` due to nested 'mixed'.''' -count = 1 - -[[issues]] -file = "src/Sql/SelectDecorator.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 2 - [[issues]] file = "src/Sql/SelectDecorator.php" code = "write-only-property" diff --git a/composer.lock b/composer.lock index 0453b9d..44f23c6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "66c86539851d7b014c3a1346f3152db0", + "content-hash": "c0726ac42b2f776c7c819829fcfd6ca9", "packages": [ { "name": "brick/varexporter", @@ -262,12 +262,12 @@ "source": { "type": "git", "url": "https://github.com/php-db/phpdb.git", - "reference": "b57f549b411d0b4fe46de8a469983816395be850" + "reference": "0c4ed091d5895168d80faa34f851b4dbc295dc4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-db/phpdb/zipball/b57f549b411d0b4fe46de8a469983816395be850", - "reference": "b57f549b411d0b4fe46de8a469983816395be850", + "url": "https://api.github.com/repos/php-db/phpdb/zipball/0c4ed091d5895168d80faa34f851b4dbc295dc4f", + "reference": "0c4ed091d5895168d80faa34f851b4dbc295dc4f", "shasum": "" }, "require": { @@ -276,7 +276,6 @@ "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { - "laminas/laminas-db": "*", "zendframework/zend-db": "*" }, "require-dev": { @@ -322,7 +321,7 @@ "issues": "https://github.com/php-db/phpdb/issues", "source": "https://github.com/php-db/phpdb" }, - "time": "2026-08-24T01:49:39+00:00" + "time": "2026-10-08T01:29:57+00:00" }, { "name": "psr/container", @@ -4485,6 +4484,7 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { + "php-db/phpdb": 20, "php-db/phpdb-qa-tools": 20 }, "prefer-stable": true, @@ -4499,5 +4499,5 @@ "platform-overrides": { "php": "8.3.99" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/src/Connection.php b/src/Connection.php index 1069a9e..7dbfa43 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -64,7 +64,7 @@ public function __construct( /** * @inheritDoc * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function beginTransaction(): ConnectionInterface @@ -82,7 +82,7 @@ public function beginTransaction(): ConnectionInterface /** * @inheritDoc * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function commit(): ConnectionInterface @@ -101,7 +101,7 @@ public function commit(): ConnectionInterface /** * @inheritDoc * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ // @mago-expect lint:halstead #[Override] @@ -207,7 +207,7 @@ public function disconnect(): ConnectionInterface /** * {@inheritDoc} * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function execute(string $sql): ?ResultInterface @@ -241,7 +241,7 @@ public function execute(string $sql): ?ResultInterface /** * @inheritDoc * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function getCurrentSchema(): string|false @@ -287,7 +287,7 @@ public function isConnected(): bool /** * @inheritDoc * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function rollback(): ConnectionInterface diff --git a/src/Container/ConnectionInterfaceFactory.php b/src/Container/ConnectionInterfaceFactory.php index 9d18ed9..74c1112 100644 --- a/src/Container/ConnectionInterfaceFactory.php +++ b/src/Container/ConnectionInterfaceFactory.php @@ -16,7 +16,7 @@ final class ConnectionInterfaceFactory /** * @param array|null $options * - * @throws \PhpDb\Adapter\Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface * * @mago-expect analysis:unused-parameter */ diff --git a/src/Container/PdoConnectionInterfaceFactory.php b/src/Container/PdoConnectionInterfaceFactory.php index 8b3abd7..f39cfc1 100644 --- a/src/Container/PdoConnectionInterfaceFactory.php +++ b/src/Container/PdoConnectionInterfaceFactory.php @@ -16,7 +16,7 @@ final class PdoConnectionInterfaceFactory /** * @param array|null $options * - * @throws \PhpDb\Adapter\Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface * * @mago-expect analysis:unused-parameter */ diff --git a/src/Driver.php b/src/Driver.php index 4f1d8c9..873013d 100644 --- a/src/Driver.php +++ b/src/Driver.php @@ -79,7 +79,7 @@ public function createResult($resource, ?bool $isBuffered = null): ResultInterfa * * @param mysqli|mysqli_stmt|string $sqlOrResource * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function createStatement($sqlOrResource = null): StatementInterface&Statement diff --git a/src/Pdo/Connection.php b/src/Pdo/Connection.php index 6bef316..702ccb6 100644 --- a/src/Pdo/Connection.php +++ b/src/Pdo/Connection.php @@ -143,7 +143,7 @@ public function connect(): ConnectionInterface /** * {@inheritDoc} * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface * @throws PDOException */ #[Override] diff --git a/src/Result.php b/src/Result.php index 4b40b06..5b0ce3f 100644 --- a/src/Result.php +++ b/src/Result.php @@ -91,7 +91,7 @@ public function count() /** * Current * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface * * @return array|null */ @@ -277,7 +277,7 @@ public function rewind() /** * Valid * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface * * @return bool */ diff --git a/src/Sql/Ddl/AlterTableDecorator.php b/src/Sql/Ddl/AlterTableDecorator.php index 9602678..adf2ddc 100644 --- a/src/Sql/Ddl/AlterTableDecorator.php +++ b/src/Sql/Ddl/AlterTableDecorator.php @@ -30,7 +30,7 @@ public function setSubject( } /** - * @return array> + * @return array{0: list} */ #[Override] protected function processAddColumns(?PlatformInterface $adapterPlatform = null): array @@ -43,11 +43,10 @@ protected function processAddColumns(?PlatformInterface $adapterPlatform = null) /** @var array $addColumns */ $addColumns = $this->addColumns; - foreach ($addColumns as $i => $column) { - /** @var array $options */ + foreach ($addColumns as $column) { $options = $column->getOptions(); - $sqls[$i] = $this->processColumnOptions( + $sqls[] = $this->processColumnOptions( $this->processExpression($column, $platform), $options, $platform, @@ -69,11 +68,9 @@ protected function processChangeColumns(?PlatformInterface $adapterPlatform = nu $sqls = []; - /** @var array $changeColumns */ $changeColumns = $this->changeColumns; foreach ($changeColumns as $name => $column) { - /** @var array $options */ $options = $column->getOptions(); $sqls[] = [ diff --git a/src/Sql/Ddl/CreateTableDecorator.php b/src/Sql/Ddl/CreateTableDecorator.php index 8668233..8eba2c8 100644 --- a/src/Sql/Ddl/CreateTableDecorator.php +++ b/src/Sql/Ddl/CreateTableDecorator.php @@ -48,7 +48,6 @@ protected function processColumns(?PlatformInterface $adapterPlatform = null): ? $columns = $this->columns; foreach ($columns as $i => $column) { - /** @var array $options */ $options = $column->getOptions(); $sqls[$i] = $this->processColumnOptions( diff --git a/src/Sql/SelectDecorator.php b/src/Sql/SelectDecorator.php index 4f05437..2baeaab 100644 --- a/src/Sql/SelectDecorator.php +++ b/src/Sql/SelectDecorator.php @@ -34,7 +34,7 @@ protected function localizeVariables(): void } } - /** @return string[]|null */ + /** @return array{0: string}|null */ #[Override] protected function processLimit( PlatformInterface $platform, @@ -47,16 +47,16 @@ protected function processLimit( if (null === $this->limit) { return null; } - if ($parameterContainer) { + if ($parameterContainer && null !== $driver) { $paramPrefix = $this->processInfo['paramPrefix']; $parameterContainer->offsetSet("{$paramPrefix}limit", $this->limit, ParameterContainer::TYPE_INTEGER); return [$driver->formatParameterName("{$paramPrefix}limit")]; } - return [$this->limit]; + return [(string) $this->limit]; } - /** @return string[]|null */ + /** @return array{0: string}|null */ #[Override] protected function processOffset( PlatformInterface $platform, @@ -66,12 +66,12 @@ protected function processOffset( if (null === $this->offset) { return null; } - if ($parameterContainer) { + if ($parameterContainer && null !== $driver) { $paramPrefix = $this->processInfo['paramPrefix']; $parameterContainer->offsetSet("{$paramPrefix}offset", $this->offset, ParameterContainer::TYPE_INTEGER); return [$driver->formatParameterName("{$paramPrefix}offset")]; } - return [$this->offset]; + return [(string) $this->offset]; } } diff --git a/src/Statement.php b/src/Statement.php index aac76ee..72165bd 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -46,7 +46,7 @@ public function __construct( * * @param array|ParameterContainer|null $parameters * - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function execute(ParameterContainer|array|null $parameters = null): ?ResultInterface @@ -134,7 +134,7 @@ public function isPrepared(): bool } /** - * @throws Exception\ExceptionInterface + * @throws \PhpDb\Exception\ExceptionInterface */ #[Override] public function prepare(?string $sql = null): StatementInterface From 20824842386cc8af33d11a7df555aa8deb2cc3b4 Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Wed, 7 Oct 2026 21:24:58 -0500 Subject: [PATCH 2/4] Alias the phpdb exception interface and drop non-ASCII dashes Import PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface in the eight files that reference it, and use the short name in @throws rather than the fully-qualified form. Replace the two em dashes with semicolons: the @todo in src/Pdo/Connection.php and the trigger comment in the CI workflow. The added import shifts the closure in src/Connection.php down one line, so mago's baselined mixed-return-statement entry is regenerated at its new position (118:31 -> 119:31). Same finding, same count, no new suppression. --- .github/workflows/continuous-integration.yml | 2 +- analysis-baseline.toml | 2 +- src/Connection.php | 13 +++++++------ src/Container/ConnectionInterfaceFactory.php | 3 ++- src/Container/DriverInterfaceFactory.php | 3 ++- src/Container/PdoConnectionInterfaceFactory.php | 3 ++- src/Driver.php | 5 +++-- src/Pdo/Connection.php | 5 +++-- src/Result.php | 5 +++-- src/Statement.php | 5 +++-- 10 files changed, 27 insertions(+), 19 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index c25b527..c75a53c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -2,7 +2,7 @@ name: "Continuous Integration" # Branch protection blocks direct pushes to release branches, and pull_request # already re-triggers on every commit pushed to a PR branch (synchronize), so -# there's no push:branches trigger here — only PRs (targeting numbered +# there's no push:branches trigger here; only PRs (targeting numbered # release branches) and pushed version tags. on: pull_request: diff --git a/analysis-baseline.toml b/analysis-baseline.toml index 50523e6..6c710f0 100644 --- a/analysis-baseline.toml +++ b/analysis-baseline.toml @@ -123,7 +123,7 @@ count = 1 [[issues]] file = "src/Connection.php" code = "mixed-return-statement" -message = "Could not infer a precise return type for function `{closure:src/Connection.php:118:31}`. Saw type `nonnull`." +message = "Could not infer a precise return type for function `{closure:src/Connection.php:119:31}`. Saw type `nonnull`." count = 1 [[issues]] diff --git a/src/Connection.php b/src/Connection.php index 7dbfa43..5ccba0c 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -15,6 +15,7 @@ use PhpDb\Adapter\Driver\ResultInterface; use PhpDb\Adapter\Exception; use PhpDb\Adapter\Exception\InvalidArgumentException; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use function constant; use function defined; @@ -64,7 +65,7 @@ public function __construct( /** * @inheritDoc * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function beginTransaction(): ConnectionInterface @@ -82,7 +83,7 @@ public function beginTransaction(): ConnectionInterface /** * @inheritDoc * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function commit(): ConnectionInterface @@ -101,7 +102,7 @@ public function commit(): ConnectionInterface /** * @inheritDoc * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ // @mago-expect lint:halstead #[Override] @@ -207,7 +208,7 @@ public function disconnect(): ConnectionInterface /** * {@inheritDoc} * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function execute(string $sql): ?ResultInterface @@ -241,7 +242,7 @@ public function execute(string $sql): ?ResultInterface /** * @inheritDoc * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function getCurrentSchema(): string|false @@ -287,7 +288,7 @@ public function isConnected(): bool /** * @inheritDoc * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function rollback(): ConnectionInterface diff --git a/src/Container/ConnectionInterfaceFactory.php b/src/Container/ConnectionInterfaceFactory.php index 74c1112..a05064d 100644 --- a/src/Container/ConnectionInterfaceFactory.php +++ b/src/Container/ConnectionInterfaceFactory.php @@ -6,6 +6,7 @@ use PhpDb\Adapter\Driver\ConnectionInterface; use PhpDb\Adapter\Exception\InvalidConnectionParametersException; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use PhpDb\Mysql\Connection; use Psr\Container\ContainerInterface; @@ -16,7 +17,7 @@ final class ConnectionInterfaceFactory /** * @param array|null $options * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface * * @mago-expect analysis:unused-parameter */ diff --git a/src/Container/DriverInterfaceFactory.php b/src/Container/DriverInterfaceFactory.php index 9cfb5b7..fc0d31d 100644 --- a/src/Container/DriverInterfaceFactory.php +++ b/src/Container/DriverInterfaceFactory.php @@ -8,6 +8,7 @@ use PhpDb\Adapter\Driver\DriverInterface; use PhpDb\Adapter\Driver\ResultInterface; use PhpDb\Exception\ContainerException; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use PhpDb\Mysql\Connection; use PhpDb\Mysql\Driver; use PhpDb\Mysql\Result; @@ -24,7 +25,7 @@ final class DriverInterfaceFactory * @throws \Laminas\ServiceManager\Exception\ExceptionInterface * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface * * @mago-expect analysis:unused-parameter */ diff --git a/src/Container/PdoConnectionInterfaceFactory.php b/src/Container/PdoConnectionInterfaceFactory.php index f39cfc1..b002507 100644 --- a/src/Container/PdoConnectionInterfaceFactory.php +++ b/src/Container/PdoConnectionInterfaceFactory.php @@ -6,6 +6,7 @@ use PhpDb\Adapter\Driver\PdoConnectionInterface; use PhpDb\Adapter\Exception\InvalidConnectionParametersException; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use PhpDb\Mysql\Pdo\Connection; use Psr\Container\ContainerInterface; @@ -16,7 +17,7 @@ final class PdoConnectionInterfaceFactory /** * @param array|null $options * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface * * @mago-expect analysis:unused-parameter */ diff --git a/src/Driver.php b/src/Driver.php index 873013d..1dae818 100644 --- a/src/Driver.php +++ b/src/Driver.php @@ -15,6 +15,7 @@ use PhpDb\Adapter\Exception; use PhpDb\Adapter\Profiler\ProfilerAwareInterface; use PhpDb\Adapter\Profiler\ProfilerInterface; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use function array_intersect_key; use function extension_loaded; @@ -32,7 +33,7 @@ final class Driver implements DriverInterface, ProfilerAwareInterface /** * @param array $options * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ public function __construct( protected readonly ConnectionInterface&Connection $connection, @@ -79,7 +80,7 @@ public function createResult($resource, ?bool $isBuffered = null): ResultInterfa * * @param mysqli|mysqli_stmt|string $sqlOrResource * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function createStatement($sqlOrResource = null): StatementInterface&Statement diff --git a/src/Pdo/Connection.php b/src/Pdo/Connection.php index 702ccb6..a0da2a4 100644 --- a/src/Pdo/Connection.php +++ b/src/Pdo/Connection.php @@ -11,6 +11,7 @@ use PhpDb\Adapter\Driver\ConnectionInterface; use PhpDb\Adapter\Driver\Pdo\AbstractPdoConnection; use PhpDb\Adapter\Exception; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use function array_diff_key; use function implode; @@ -143,7 +144,7 @@ public function connect(): ConnectionInterface /** * {@inheritDoc} * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface * @throws PDOException */ #[Override] @@ -187,7 +188,7 @@ public function getLastGeneratedValue(?string $name = null): string|int|false|nu * Return a value that is safe to interpolate into a generated DSN. * * @todo Promote to AbstractPdoConnection in php-db/phpdb as a protected method once a second - * PDO driver package needs it — the validation is generic to all semicolon-delimited + * PDO driver package needs it; the validation is generic to all semicolon-delimited * PDO DSN formats and has no MySQL-specific dependencies. * * @throws Exception\InvalidConnectionParametersException If the value contains DSN control characters. diff --git a/src/Result.php b/src/Result.php index 5b0ce3f..674ce08 100644 --- a/src/Result.php +++ b/src/Result.php @@ -11,6 +11,7 @@ use Override; use PhpDb\Adapter\Driver\ResultInterface; use PhpDb\Adapter\Exception; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use PhpDb\ResultSet\ResultSet; use PhpDb\ResultSet\ResultSetInterface; // phpcs:ignore SlevomatCodingStandard.Namespaces.UnusedUses.UnusedUse @@ -91,7 +92,7 @@ public function count() /** * Current * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface * * @return array|null */ @@ -277,7 +278,7 @@ public function rewind() /** * Valid * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface * * @return bool */ diff --git a/src/Statement.php b/src/Statement.php index 72165bd..8ba502c 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -16,6 +16,7 @@ use PhpDb\Adapter\Profiler\ProfilerAwareInterface; use PhpDb\Adapter\Profiler\ProfilerInterface; use PhpDb\Adapter\StatementContainerInterface; +use PhpDb\Exception\ExceptionInterface as PhpDbExceptionInterface; use function array_unshift; use function call_user_func_array; @@ -46,7 +47,7 @@ public function __construct( * * @param array|ParameterContainer|null $parameters * - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function execute(ParameterContainer|array|null $parameters = null): ?ResultInterface @@ -134,7 +135,7 @@ public function isPrepared(): bool } /** - * @throws \PhpDb\Exception\ExceptionInterface + * @throws PhpDbExceptionInterface */ #[Override] public function prepare(?string $sql = null): StatementInterface From 784e917f165a45fefbd73c48aafdde6ffebab034 Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Wed, 7 Oct 2026 21:32:57 -0500 Subject: [PATCH 3/4] Revert the unrelated CI workflow comment edit Restore .github/workflows/continuous-integration.yml to its previous state so this PR only touches source files and the baseline. --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index c75a53c..c25b527 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -2,7 +2,7 @@ name: "Continuous Integration" # Branch protection blocks direct pushes to release branches, and pull_request # already re-triggers on every commit pushed to a PR branch (synchronize), so -# there's no push:branches trigger here; only PRs (targeting numbered +# there's no push:branches trigger here — only PRs (targeting numbered # release branches) and pushed version tags. on: pull_request: From 8ad95df2275cd3d7c1429927b15b117513139eba Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Wed, 7 Oct 2026 22:05:14 -0500 Subject: [PATCH 4/4] Assert the bound limit/offset placeholders in the generated SQL The setSql expectation was with($this->isString()), which any string satisfies, so the container-and-driver branch of processLimit/processOffset was never actually pinned. Capture the SQL and assert the LIMIT/OFFSET placeholders. This restores the coverage that the (string) casts removed, killing the two ReturnRemoval mutants at lines 53 and 72. Refs #86. --- test/unit/Sql/SelectDecoratorTest.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/unit/Sql/SelectDecoratorTest.php b/test/unit/Sql/SelectDecoratorTest.php index 65cd091..85db47d 100644 --- a/test/unit/Sql/SelectDecoratorTest.php +++ b/test/unit/Sql/SelectDecoratorTest.php @@ -79,11 +79,18 @@ public function prepareStatementBindsLimitAndOffsetParameters(): void $parameterContainer = new ParameterContainer(); + $sql = ''; $statementContainer = $this->createMock(StatementContainerInterface::class); $statementContainer->method('getParameterContainer')->willReturn($parameterContainer); $statementContainer->expects($this->once()) ->method('setSql') - ->with($this->isString()); + ->willReturnCallback( + static function (?string $sqlString) use (&$sql, $statementContainer): StatementContainerInterface { + $sql = (string) $sqlString; + + return $statementContainer; + }, + ); $driver = $this->createStub(DriverInterface::class); $driver->method('formatParameterName') @@ -98,6 +105,8 @@ public function prepareStatementBindsLimitAndOffsetParameters(): void static::assertSame($statementContainer, $result); static::assertSame(10, $parameterContainer->offsetGet('limit')); static::assertSame(5, $parameterContainer->offsetGet('offset')); + static::assertStringContainsString('LIMIT :limit', $sql); + static::assertStringContainsString('OFFSET :offset', $sql); } #[Test]