diff --git a/.laminas-ci.json b/.laminas-ci.json index 10c530b..a9bb393 100644 --- a/.laminas-ci.json +++ b/.laminas-ci.json @@ -3,7 +3,7 @@ { "name": "PhpStan", "job": { - "php": "8.2", + "php": "8.3", "dependencies": "latest", "command": "composer require --dev phpstan/phpstan && vendor/bin/phpstan analyse" } diff --git a/composer.json b/composer.json index e97c8a7..538edda 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "config": { "sort-packages": true, "platform": { - "php": "8.2.99" + "php": "8.3.99" }, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true @@ -30,14 +30,14 @@ } }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "php-db/phpdb": "^0.6.0" }, "require-dev": { "ext-pdo_pgsql": "*", "ext-pgsql": "*", "laminas/laminas-coding-standard": "^3.0.1", - "phpstan/phpstan": "^2.1", + "phpstan/phpstan": "^2.2", "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^11.5.42" }, diff --git a/composer.lock b/composer.lock index 7e4a8d7..0e7a753 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": "7c98d39a182766356052ff8f400e7623", + "content-hash": "75ac9cd93692f950a839f5ff317be05b", "packages": [ { "name": "brick/varexporter", @@ -57,16 +57,16 @@ }, { "name": "laminas/laminas-servicemanager", - "version": "4.5.0", + "version": "4.5.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-servicemanager.git", - "reference": "a6996829c8ce55025cca1b57b1e8a8b165e3926c" + "reference": "11192d588876ad04ba2988984c77b4ecb5c771c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/a6996829c8ce55025cca1b57b1e8a8b165e3926c", - "reference": "a6996829c8ce55025cca1b57b1e8a8b165e3926c", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/11192d588876ad04ba2988984c77b4ecb5c771c2", + "reference": "11192d588876ad04ba2988984c77b4ecb5c771c2", "shasum": "" }, "require": { @@ -130,7 +130,7 @@ "chat": "https://laminas.dev/chat", "forum": "https://discourse.laminas.dev", "issues": "https://github.com/laminas/laminas-servicemanager/issues", - "source": "https://github.com/laminas/laminas-servicemanager/tree/4.5.0" + "source": "https://github.com/laminas/laminas-servicemanager/tree/4.5.1" }, "funding": [ { @@ -138,7 +138,7 @@ "type": "community_bridge" } ], - "time": "2025-10-14T09:41:04+00:00" + "time": "2026-05-12T09:53:32+00:00" }, { "name": "laminas/laminas-stdlib", @@ -201,20 +201,19 @@ }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v5.8.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-json": "*", "ext-tokenizer": "*", "php": ">=7.4" @@ -253,9 +252,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2026-07-04T14:30:18+00:00" }, { "name": "php-db/phpdb", @@ -263,30 +262,29 @@ "source": { "type": "git", "url": "https://github.com/php-db/phpdb.git", - "reference": "81844a5699bf397bf044bbe36ea90be361b4a021" + "reference": "b57f549b411d0b4fe46de8a469983816395be850" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-db/phpdb/zipball/81844a5699bf397bf044bbe36ea90be361b4a021", - "reference": "81844a5699bf397bf044bbe36ea90be361b4a021", + "url": "https://api.github.com/repos/php-db/phpdb/zipball/b57f549b411d0b4fe46de8a469983816395be850", + "reference": "b57f549b411d0b4fe46de8a469983816395be850", "shasum": "" }, "require": { "laminas/laminas-servicemanager": "^3.0.0 || ^4.0.0", "laminas/laminas-stdlib": "^3.20.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { "laminas/laminas-db": "*", "zendframework/zend-db": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.0.1", + "infection/infection": "^0.34.1", "laminas/laminas-eventmanager": "^3.14.0", "laminas/laminas-hydrator": "^4.6.0", + "php-db/phpdb-qa-tools": "0.1.x-dev", "phpbench/phpbench": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^11.5.42", "rector/rector": "^2.0" }, @@ -324,7 +322,7 @@ "issues": "https://github.com/php-db/phpdb/issues", "source": "https://github.com/php-db/phpdb" }, - "time": "2026-04-13T01:24:01+00:00" + "time": "2026-08-24T01:49:39+00:00" }, { "name": "psr/container", @@ -757,11 +755,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.46", + "version": "2.2.9", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a193923fc2d6325ef4e741cf3af8c3e8f54dbf25", - "reference": "a193923fc2d6325ef4e741cf3af8c3e8f54dbf25", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/13d6b4f347bad222da436580c8304fa6f83e6bd0", + "reference": "13d6b4f347bad222da436580c8304fa6f83e6bd0", "shasum": "" }, "require": { @@ -784,6 +782,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", @@ -806,7 +815,7 @@ "type": "github" } ], - "time": "2026-04-01T09:25:14+00:00" + "time": "2026-08-22T07:38:16+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -2615,14 +2624,14 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "platform-dev": { "ext-pdo_pgsql": "*", "ext-pgsql": "*" }, "platform-overrides": { - "php": "8.2.99" + "php": "8.3.99" }, "plugin-api-version": "2.9.0" } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ddb8f5c..933345b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6,14 +6,3 @@ parameters: count: 1 path: src/Container/ConnectionInterfaceFactory.php - - - message: '#^Parameter \#2 \$array of function implode expects array\, array\\> given\.$#' - identifier: argument.type - count: 1 - path: src/Metadata/Source.php - - - - message: '#^Parameter \#2 \$array of function implode expects array\, array\\> given\.$#' - identifier: argument.type - count: 1 - path: src/Metadata/Source.php diff --git a/src/Result.php b/src/Result.php index fb18b10..43e43a9 100644 --- a/src/Result.php +++ b/src/Result.php @@ -7,6 +7,9 @@ use Override; use PgSql\Result as PgSqlResult; use PhpDb\Adapter\Driver\ResultInterface; +use PhpDb\Adapter\Exception; +use PhpDb\ResultSet\ResultSet; +use PhpDb\ResultSet\ResultSetInterface; use function pg_affected_rows; use function pg_fetch_assoc; @@ -94,6 +97,26 @@ public function getGeneratedValue(): int|string|false|null return $this->generatedValue; } + /** + * @throws Exception\RuntimeException When this result is not a query result. + */ + #[Override] + public function getQueryResult(?ResultSetInterface $resultPrototype = null): ResultSetInterface + { + if (! $this->isQueryResult()) { + throw new Exception\RuntimeException( + 'Cannot produce a query result set from a result that is not a query result;' + . ' check isQueryResult() first' + ); + } + + $resultPrototype ??= new ResultSet(); + $resultSet = clone $resultPrototype; + $resultSet->initialize($this); + + return $resultSet; + } + /** * Get resource */ diff --git a/test/asset/ResultStub.php b/test/asset/ResultStub.php new file mode 100644 index 0000000..814e290 --- /dev/null +++ b/test/asset/ResultStub.php @@ -0,0 +1,32 @@ +isQueryResult; + } + + #[Override] + public function getFieldCount(): int + { + return $this->fieldCount; + } +} diff --git a/test/integration/ResultTest.php b/test/integration/ResultTest.php new file mode 100644 index 0000000..e25f435 --- /dev/null +++ b/test/integration/ResultTest.php @@ -0,0 +1,57 @@ +getAdapter()->executeQuery('SELECT id, name, value FROM test'); + + self::assertSame($result->getFieldCount(), $result->getQueryResult()->getFieldCount()); + } + + public function testGetQueryResultIteratesTheSelectedRows(): void + { + $result = $this->getAdapter()->executeQuery('SELECT name FROM test ORDER BY id'); + + $names = []; + foreach ($result->getQueryResult() as $row) { + $names[] = $row['name']; + } + + self::assertSame(['foo', 'bar'], $names); + } + + public function testAStatementReturningNoFieldsIsNotAQueryResult(): void + { + $result = $this->getAdapter()->executeQuery('SET search_path TO public'); + + self::assertFalse($result->isQueryResult()); + } + + public function testGetQueryResultRejectsAStatementThatReturnsNoFields(): void + { + $result = $this->getAdapter()->executeQuery('SET search_path TO public'); + + $this->expectException(Exception\RuntimeException::class); + $this->expectExceptionMessage( + 'Cannot produce a query result set from a result that is not a query result;' + . ' check isQueryResult() first' + ); + + $result->getQueryResult(); + } +} diff --git a/test/unit/ResultTest.php b/test/unit/ResultTest.php new file mode 100644 index 0000000..0300f96 --- /dev/null +++ b/test/unit/ResultTest.php @@ -0,0 +1,43 @@ +getQueryResult(); + + self::assertSame(3, $resultSet->getFieldCount()); + } + + public function testGetQueryResultClonesTheGivenPrototype(): void + { + $prototype = new ResultSet(); + + $resultSet = (new ResultStub(true, 1))->getQueryResult($prototype); + + self::assertNotSame($prototype, $resultSet); + } + + public function testGetQueryResultRejectsAResultThatIsNotAQueryResult(): void + { + $this->expectException(Exception\RuntimeException::class); + $this->expectExceptionMessage( + 'Cannot produce a query result set from a result that is not a query result;' + . ' check isQueryResult() first' + ); + + (new ResultStub(false))->getQueryResult(); + } +}