From d7375b6c482ab309e69dc3a55bc1d4b3fa77fa49 Mon Sep 17 00:00:00 2001 From: Marc Reichel Date: Thu, 8 Jan 2026 15:47:33 +0100 Subject: [PATCH] chore: PHPStan Level 9 --- phpstan.neon | 2 +- src/Connection.php | 27 ++++++++++++----------- src/ConnectionInterface.php | 26 ++++++++++++++-------- src/DoctrineConnectionInterface.php | 22 +++++++++---------- src/Driver/DriverAbstract.php | 5 +++-- src/Driver/MysqliDriver.php | 6 ++++++ src/Driver/PostgresDriver.php | 10 +++++++-- src/Driver/Sqlite3Driver.php | 18 ++++++++++++++-- src/DriverInterface.php | 19 ++++++++++++----- src/Exception/QueryException.php | 4 ++-- tests/ConnectionMultiInsertTest.php | 1 + tests/ConnectionPreparedTest.php | 2 ++ tests/ConnectionRoundTest.php | 2 ++ tests/ConnectionStringLengthTest.php | 1 + tests/ConnectionTest.php | 32 ++++++++++++++++++++++++---- tests/ConnectionTestCase.php | 2 +- 16 files changed, 127 insertions(+), 52 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 62ab5d12..a1fb39e0 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,7 +2,7 @@ includes: - phpstan-baseline.neon parameters: - level: 8 + level: 9 paths: - src - tests diff --git a/src/Connection.php b/src/Connection.php index 520f2cca..5366251c 100755 --- a/src/Connection.php +++ b/src/Connection.php @@ -24,6 +24,7 @@ use Artemeon\Database\Schema\DataType; use Artemeon\Database\Schema\Table; use Artemeon\Database\Schema\TableIndex; +use BackedEnum; use Generator; use InvalidArgumentException; use Override; @@ -45,7 +46,7 @@ class Connection implements ConnectionInterface /** * Array to cache queries. * - * @var array> + * @var array>> */ private array $queryCache = []; @@ -310,8 +311,6 @@ public function _pQuery(string $query, array $params = [], array $escapes = []): $this->dbconnect(); } - $output = false; - $query = $this->processQuery($query); // Increasing the counter @@ -445,6 +444,8 @@ public function getPArray( /** * @inheritDoc * @throws ConnectionException + * + * @return Generator>> */ #[Override] public function getGenerator(string $query, array $params = [], int $chunkSize = 2048, bool $paging = true): Generator @@ -605,7 +606,7 @@ public function iterateColumn(string $query, array $params = []): Generator /** * Writes the last DB-Error to the screen. * - * @param list $params + * @param list $params * * @throws QueryException * @throws ConnectionException @@ -1149,7 +1150,7 @@ private function processQuery(string $query): string } /** - * @param list $params + * @param list $params */ private function addQueryToList(string $query, array $params, bool $cached, float $startTime): void { @@ -1220,11 +1221,11 @@ public function getCacheSize(): int * An internal wrapper to dbsafeString, used to process a complete array of parameters * as used by prepared statements. * - * @param array $params + * @param array $params * @param list|false $escapes An array of boolean for each param, used to block the escaping of html-special chars. * If not passed, all params will be cleaned. * - * @return list + * @return list * * @see Db::dbsafeString($string, $htmlSpecialChars = true) */ @@ -1232,7 +1233,7 @@ private function dbsafeParams(array $params, array | false $escapes = []): array { $replace = []; foreach ($params as $key => $param) { - if ($param instanceof \BackedEnum) { + if ($param instanceof BackedEnum) { $replace[$key] = $param->value; continue; @@ -1261,7 +1262,7 @@ private function dbsafeParams(array $params, array | false $escapes = []): array /** * Makes a string db-safe. * - * @return int|float|null|string + * @return ($input is float ? float : ($input is int ? int : ($input is bool ? int<0,1> : ($input is null ? null : ($input is scalar ? string : mixed))))) * @deprecated we need to get rid of this */ public function dbsafeString(mixed $input, bool $htmlSpecialChars = true, bool $addSlashes = true): mixed @@ -1284,12 +1285,12 @@ public function dbsafeString(mixed $input, bool $htmlSpecialChars = true, bool $ } // escape special chars - if ($htmlSpecialChars) { + if (is_scalar($input) && $htmlSpecialChars) { $input = html_entity_decode((string) $input, ENT_COMPAT, 'UTF-8'); $input = htmlspecialchars($input, ENT_COMPAT, 'UTF-8'); } - if ($addSlashes) { + if (is_scalar($input) && $addSlashes) { $input = addslashes((string) $input); } @@ -1392,7 +1393,7 @@ public function escape(mixed $value): mixed public function prettifyQuery(string $query, array $params): string { foreach ($params as $param) { - if (!is_numeric($param) && $param !== null) { + if (is_string($param)) { $param = "'$param'"; } @@ -1403,7 +1404,7 @@ public function prettifyQuery(string $query, array $params): string } $pos = strpos($query, '?'); - if ($pos !== false) { + if ($pos !== false && is_string($param)) { $query = substr_replace($query, $param, $pos, 1); } } diff --git a/src/ConnectionInterface.php b/src/ConnectionInterface.php index 021ca7b5..72e2d461 100755 --- a/src/ConnectionInterface.php +++ b/src/ConnectionInterface.php @@ -36,7 +36,7 @@ interface ConnectionInterface extends DoctrineConnectionInterface * Method to get an array of rows for a given query from the database. * Makes use of prepared statements. * - * @param list $params + * @param list $params * @param list $escapes * * @throws QueryException @@ -54,7 +54,7 @@ public function getPArray(string $query, array $params = [], ?int $start = null, * Returns one row from a result-set. * Makes use of prepared statements. * - * @param list $params + * @param list $params * @param list $escapes * * @throws QueryException @@ -69,7 +69,7 @@ public function getPRow(string $query, array $params = [], int $number = 0, bool * * @param string $tableName the table name from which to select the row * @param list $columns a flat list of column names to select - * @param array $identifiers mapping of column name to value to search for (e.g. ["id" => 1]) + * @param array $identifiers mapping of column name to value to search for (e.g. ["id" => 1]) * @param bool $cached whether a previously selected result can be reused * @param list|null $escapes which parameters to escape (described in {@see dbsafeParams}) * @throws QueryException @@ -91,7 +91,7 @@ public function selectRow(string $tableName, array $columns, array $identifiers, * false and don't modify the result set you will get an endless loop, so you must get sure that in the end the * result set will be empty. * - * @param list $params + * @param list $params * * @throws QueryException * @see iterateAssociative @@ -103,7 +103,7 @@ public function getGenerator(string $query, array $params = [], int $chunkSize = * * Sending a prepared statement to the database * - * @param list $params + * @param list $params * @param list $escapes An array of booleans for each param, used to block the escaping of html-special chars. * If not passed, all params will be cleaned. * @throws QueryException @@ -122,7 +122,7 @@ public function getAffectedRowsCount(): int; * INSERT INTO $table ($columns) VALUES (?, ?), (?, ?)... * * @param list $columns - * @param list $valueSets + * @param list> $valueSets * @param list|null $escapes * @throws QueryException */ @@ -135,7 +135,7 @@ public function multiInsert(string $tableName, array $columns, array $valueSets, * otherwise data might be lost. And: params are sent to the database unescaped. * * @param list $columns - * @param list $values + * @param list $values * @param list $primaryColumns * * @throws QueryException @@ -196,7 +196,7 @@ public function getDatatype(DataType $type): string; * Used to send a `CREATE TABLE` statement to the database. * By passing the query through this method, the driver can add db-specific commands. * - * @param array $columns + * @param array $columns * @param list $keys * @param list|string> $indices * @@ -312,7 +312,7 @@ public function encloseTableName(string $tableName): string; * Helper to replace all param-placeholder with the matching value, only to be used * to render a debuggable-statement. * - * @param list $params + * @param list $params */ public function prettifyQuery(string $query, array $params): string; @@ -349,6 +349,10 @@ public function getStringLengthExpression(string $targetString): string; /** * Converts a PHP value to a value, which can be inserted into a table. I.e. it truncates the value to * the fitting length for the provided datatype. + * + * @param scalar|null $value + * + * @return scalar|null */ public function convertToDatabaseValue(mixed $value, DataType $type): mixed; @@ -427,6 +431,10 @@ public function flushPreparedStatementsCache(): void; */ public function getColumnsOfTable(string $tableName): array; + /** + * @param scalar|null $value + * @return scalar|null + */ public function escape(mixed $value): mixed; public function hasOpenTransactions(): bool; diff --git a/src/DoctrineConnectionInterface.php b/src/DoctrineConnectionInterface.php index e9fbfa71..f53ebe7b 100644 --- a/src/DoctrineConnectionInterface.php +++ b/src/DoctrineConnectionInterface.php @@ -27,7 +27,7 @@ interface DoctrineConnectionInterface /** * Prepares and executes an SQL query and returns the result as an array of associative arrays. * - * @param list $params + * @param list $params * * @return list> */ @@ -37,7 +37,7 @@ public function fetchAllAssociative(string $query, array $params = []): array; * Prepares and executes an SQL query and returns the first row of the result * as an associative array. * - * @param list $params + * @param list $params * * @return array|false */ @@ -46,7 +46,7 @@ public function fetchAssociative(string $query, array $params = []): array | fal /** * Prepares and executes an SQL query and returns the result as an array of the first column values. * - * @param list $params + * @param list $params * * @return list */ @@ -55,7 +55,7 @@ public function fetchFirstColumn(string $query, array $params = []): array; /** * Prepares and executes an SQL query and returns the value of a single column of the first row of the result. * - * @param list $params + * @param list $params */ public function fetchOne(string $query, array $params = []): mixed; @@ -63,14 +63,14 @@ public function fetchOne(string $query, array $params = []): mixed; * Prepares and executes an SQL query and returns the result as an iterator over rows represented * as associative arrays. * - * @param list $params + * @param list $params */ public function iterateAssociative(string $query, array $params = []): Generator; /** * Prepares and executes an SQL query and returns the result as an iterator over the first column values. * - * @param list $params + * @param list $params */ public function iterateColumn(string $query, array $params = []): Generator; @@ -84,7 +84,7 @@ public function iterateColumn(string $query, array $params = []): Generator; * - Session control statements: ALTER SESSION, SET, DECLARE, etc. * - Other statements that don't yield a row set. * - * @param list $params + * @param list $params */ public function executeStatement(string $query, array $params = []): int; @@ -92,7 +92,7 @@ public function executeStatement(string $query, array $params = []): int; * Creates a simple insert for a single row where the values parameter is an associative array with column names to * value mapping. * - * @param array $values + * @param array $values * @param list|null $escapes */ public function insert(string $tableName, array $values, ?array $escapes = null): int; @@ -100,8 +100,8 @@ public function insert(string $tableName, array $values, ?array $escapes = null) /** * Updates a row on the provided table by the identifier columns. * - * @param array $values - * @param array $identifier + * @param array $values + * @param array $identifier * @param list|null $escapes */ public function update(string $tableName, array $values, array $identifier, ?array $escapes = null): int; @@ -109,7 +109,7 @@ public function update(string $tableName, array $values, array $identifier, ?arr /** * Deletes a row on the provided table by the identifier columns. * - * @param array $identifier + * @param array $identifier */ public function delete(string $tableName, array $identifier): int; diff --git a/src/Driver/DriverAbstract.php b/src/Driver/DriverAbstract.php index 2f24509a..e7977a39 100644 --- a/src/Driver/DriverAbstract.php +++ b/src/Driver/DriverAbstract.php @@ -25,12 +25,12 @@ /** * Base class for all database-drivers, holds methods to be used by all drivers. * - * @author sidler@mulchprod.de + * @template CacheType */ abstract class DriverAbstract implements DriverInterface { /** - * @var array + * @var array */ protected array $statementsCache = []; @@ -225,6 +225,7 @@ public function insertOrUpdate(string $table, array $columns, array $values, arr $enclosedTableName = $this->encloseTableName($table); + /** @var list|false $rows */ $rows = $this->getPArray("SELECT COUNT(*) AS cnt FROM $enclosedTableName WHERE " . implode(' AND ', $primaryCompares), $primaryValues)->current(); if ($rows === false) { diff --git a/src/Driver/MysqliDriver.php b/src/Driver/MysqliDriver.php index 1775c9eb..2801720a 100755 --- a/src/Driver/MysqliDriver.php +++ b/src/Driver/MysqliDriver.php @@ -33,6 +33,8 @@ /** * DB-driver for MySQL using the php-mysqli-interface. + * + * @template-extends DriverAbstract */ class MysqliDriver extends DriverAbstract { @@ -257,6 +259,7 @@ public function getTables(): array { $generator = $this->getPArray('SHOW TABLE STATUS', []); $result = []; + /** @var array{Name:string} $row */ foreach ($generator as $row) { $result[] = ['name' => $row['Name']]; } @@ -275,6 +278,7 @@ public function getTableInformation(string $tableName): Table // fetch all columns $columnInfo = $this->getPArray("SHOW COLUMNS FROM $tableName", []); + /** @var array{Field:non-empty-string,Type:string,Null:string} $column */ foreach ($columnInfo as $column) { $table->addColumn( TableColumn::make($column['Field']) @@ -287,6 +291,7 @@ public function getTableInformation(string $tableName): Table // fetch all indexes $indexes = $this->getPArray("SHOW INDEX FROM $tableName WHERE Key_name != 'PRIMARY'", []); $indexAggr = []; + /** @var array{Key_name:string,Column_name:string} $indexInfo */ foreach ($indexes as $indexInfo) { $indexAggr[$indexInfo['Key_name']] ??= []; $indexAggr[$indexInfo['Key_name']][] = $indexInfo['Column_name']; @@ -299,6 +304,7 @@ public function getTableInformation(string $tableName): Table // fetch all keys $keys = $this->getPArray("SHOW KEYS FROM $tableName WHERE Key_name = 'PRIMARY'", []); + /** @var array{Column_name:string} $keyInfo */ foreach ($keys as $keyInfo) { $key = new TableKey($keyInfo['Column_name']); $table->addPrimaryKey($key); diff --git a/src/Driver/PostgresDriver.php b/src/Driver/PostgresDriver.php index bbf163b8..8c10881f 100755 --- a/src/Driver/PostgresDriver.php +++ b/src/Driver/PostgresDriver.php @@ -30,6 +30,8 @@ /** * DB-driver for postgres using the php-postgres-interface. + * + * @template-extends DriverAbstract */ class PostgresDriver extends DriverAbstract { @@ -38,7 +40,7 @@ class PostgresDriver extends DriverAbstract private string $restoreBin = 'psql'; // Binary to restore db (if not in path, add the path here) /** - * @var array + * @var array */ private array $cxInfo = []; @@ -152,7 +154,7 @@ public function getPArray(string $query, array $params): Generator public function insertOrUpdate(string $table, array $columns, array $values, array $primaryColumns): bool { // get the current postgres version to validate the upsert features - if (version_compare($this->cxInfo['server'], '9.5', '<')) { + if (array_key_exists('server', $this->cxInfo) && is_string($this->cxInfo['server']) && version_compare($this->cxInfo['server'], '9.5', '<')) { // base implementation return parent::insertOrUpdate($table, $columns, $values, $primaryColumns); } @@ -214,6 +216,7 @@ public function getTables(): array [], ); $result = []; + /** @var array{name:scalar} $row */ foreach ($generator as $row) { $result[] = ['name' => strtolower((string) $row['name'])]; } @@ -232,6 +235,7 @@ public function getTableInformation(string $tableName): Table // fetch all columns $columnInfo = $this->getPArray('SELECT * FROM information_schema.columns WHERE table_name = ?', [$tableName]); + /** @var array{column_name:non-empty-string,data_type:string,character_maximum_length:int|numeric-string,is_nullable:string} $column */ foreach ($columnInfo as $column) { $table->addColumn( TableColumn::make($column['column_name']) @@ -246,6 +250,7 @@ public function getTableInformation(string $tableName): Table "select * from pg_indexes where tablename = ? AND indexname NOT LIKE '%_pkey'", [$tableName], ); + /** @var array{indexname:string,indexdef:scalar} $indexInfo */ foreach ($indexes as $indexInfo) { $index = new TableIndex($indexInfo['indexname']); // scrape the columns from the indexdef @@ -274,6 +279,7 @@ public function getTableInformation(string $tableName): Table ORDER BY t.relname, i.relname"; $keys = $this->getPArray($query, [$tableName]); + /** @var array{column_name:string} $keyInfo */ foreach ($keys as $keyInfo) { $key = new TableKey($keyInfo['column_name']); $table->addPrimaryKey($key); diff --git a/src/Driver/Sqlite3Driver.php b/src/Driver/Sqlite3Driver.php index 44662b94..064f0c73 100755 --- a/src/Driver/Sqlite3Driver.php +++ b/src/Driver/Sqlite3Driver.php @@ -24,6 +24,7 @@ use Artemeon\Database\Schema\TableKey; use Generator; use Override; +use RuntimeException; use SQLite3; use SQLite3Stmt; use Throwable; @@ -31,6 +32,8 @@ /** * DB-driver for sqlite3 using the php-sqlite3-interface. * Based on the sqlite2 driver by phwolfer. + * + * @template-extends DriverAbstract */ class Sqlite3Driver extends DriverAbstract { @@ -50,7 +53,12 @@ public function dbconnect(ConnectionParameters $params): bool if ($params->getDatabase() === ':memory:') { $this->dbFile = ':memory:'; } else { - $this->dbFile = $params->getAttribute(ConnectionParameters::SQLITE3_BASE_PATH) . '/' . $params->getDatabase() . '.db3'; + $basePath = $params->getAttribute(ConnectionParameters::SQLITE3_BASE_PATH); + if (!is_string($basePath)) { + throw new RuntimeException('Invalid SQLite base path'); + } + + $this->dbFile = $basePath . '/' . $params->getDatabase() . '.db3'; } try { @@ -97,6 +105,7 @@ private function buildAndCopyTempTables(string $targetTableName, array $sourceTa // Get existing table info $pragmaTableInfo = $this->getPArray("PRAGMA table_info('$targetTableName')", []); $columnsPragma = []; + /** @var array{name:int|string,type:string,notnull:int,dflt_value?:scalar|null,pk?:int} $row */ foreach ($pragmaTableInfo as $row) { $columnsPragma[$row['name']] = $row; } @@ -117,8 +126,10 @@ private function buildAndCopyTempTables(string $targetTableName, array $sourceTa // loop the fields $columns = []; $pks = []; + /** @var array{columnName:string,columnType:string} $column */ foreach ($targetTableInfo as $column) { - $row = null; + /** @var array{name:string,type:string} $row */ + $row = []; if (array_key_exists($column['columnName'], $columnsPragma)) { $row = $columnsPragma[$column['columnName']]; @@ -367,6 +378,7 @@ public function getTables(): array { $generator = $this->getPArray("SELECT name FROM sqlite_master WHERE type='table'", []); $result = []; + /** @var array{name:string} $row */ foreach ($generator as $row) { $result[] = ['name' => strtolower((string) $row['name'])]; } @@ -385,6 +397,7 @@ public function getTableInformation(string $tableName): Table // fetch all columns $columnInfo = $this->getPArray("PRAGMA table_info('$tableName')", []); + /** @var array{name:non-empty-string,type:string,notnull:bool|int|numeric-string,pk:bool|int|numeric-string} $column */ foreach ($columnInfo as $column) { $table->addColumn( TableColumn::make($column['name']) @@ -400,6 +413,7 @@ public function getTableInformation(string $tableName): Table // fetch all indexes $indexes = $this->getPArray("SELECT * FROM sqlite_master WHERE type = 'index' AND tbl_name = ?", [$tableName]); + /** @var array{name:string,sql?:string|null} $indexInfo */ foreach ($indexes as $indexInfo) { $index = new TableIndex($indexInfo['name']); $index->setDescription($indexInfo['sql'] ?? ''); diff --git a/src/DriverInterface.php b/src/DriverInterface.php index a79ea4c2..42f31de0 100755 --- a/src/DriverInterface.php +++ b/src/DriverInterface.php @@ -46,7 +46,7 @@ public function dbclose(): void; * The query is fired to the database by Database. * * @param list $columns - * @param list> $valueSets + * @param list> $valueSets * @param list $escapes */ public function triggerMultiInsert(string $table, array $columns, array $valueSets, ConnectionInterface $database, ?array $escapes): bool; @@ -58,7 +58,7 @@ public function triggerMultiInsert(string $table, array $columns, array $valueSe * otherwise data might be lost. * * @param list $columns - * @param list $values + * @param list $values * @param list $primaryColumns */ public function insertOrUpdate(string $table, array $columns, array $values, array $primaryColumns): bool; @@ -67,7 +67,7 @@ public function insertOrUpdate(string $table, array $columns, array $values, arr * Sends a prepared statement to the database. All params must be represented by the "?" char. * The params themselves are stored using the second params using the matching order. * - * @param list $params + * @param list $params * * @throws QueryException */ @@ -77,9 +77,11 @@ public function _pQuery(string $query, array $params): bool; * This method is used to retrieve an array of result-sets from the database using * a prepared statement. * - * @param list $params + * @param list $params * * @throws QueryException + * + * @return Generator> */ public function getPArray(string $query, array $params): Generator; @@ -107,7 +109,7 @@ public function getTableInformation(string $tableName): Table; * Used to send a CREATE table statement to the database * By passing the query through this method, the driver can add db-specific commands. * - * @param array $columns + * @param array $columns * @param list $primaryKeys */ public function createTable(string $name, array $columns, array $primaryKeys): bool; @@ -244,6 +246,10 @@ public function getDatatype(DataType $type): string; */ public function flushQueryCache(): void; + /** + * @param scalar|null $value + * @return scalar|null + */ public function escape(mixed $value): mixed; /** @@ -278,6 +284,9 @@ public function handlesDumpCompression(): bool; /** * Convert a PHP value to a value, which can be inserted into a table. I.e. it truncates the value to * the fitting length for the provided datatype. + * + * @param scalar|null $value + * @return scalar|null */ public function convertToDatabaseValue(mixed $value, DataType $type): mixed; diff --git a/src/Exception/QueryException.php b/src/Exception/QueryException.php index dd26dd8f..29c4acef 100644 --- a/src/Exception/QueryException.php +++ b/src/Exception/QueryException.php @@ -19,7 +19,7 @@ class QueryException extends Exception { /** - * @param list $params + * @param list $params */ public function __construct(string $message, private readonly string $query, private readonly array $params, ?Throwable $previous = null) { @@ -32,7 +32,7 @@ public function getQuery(): string } /** - * @return list + * @return list */ public function getParams(): array { diff --git a/tests/ConnectionMultiInsertTest.php b/tests/ConnectionMultiInsertTest.php index 12877a39..3637d505 100644 --- a/tests/ConnectionMultiInsertTest.php +++ b/tests/ConnectionMultiInsertTest.php @@ -54,6 +54,7 @@ public function testInserts(): void $this->assertEquals(123456 + $i, $row['temp_int']); $this->assertEquals(20200508095300 + $i, $row['temp_bigint']); + $this->assertIsScalar($row['temp_float']); $this->assertEquals(23.45, round((float) $row['temp_float'], 2)); $this->assertEquals('char10-' . $i, $row['temp_char10']); $this->assertEquals('char20-' . $i, $row['temp_char20']); diff --git a/tests/ConnectionPreparedTest.php b/tests/ConnectionPreparedTest.php index a09d49bf..0f17fbd6 100644 --- a/tests/ConnectionPreparedTest.php +++ b/tests/ConnectionPreparedTest.php @@ -119,11 +119,13 @@ public function testFloatHandling(): void $row = $connection->getPRow('SELECT * FROM ' . self::TEST_TABLE_NAME . ' WHERE temp_id = ?', ['id1']); $this->assertEquals(123456, $row['temp_bigint']); + $this->assertIsScalar($row['temp_float']); $this->assertEquals(1.7, round((float) $row['temp_float'], 1)); $row = $connection->getPRow('SELECT * FROM ' . self::TEST_TABLE_NAME . ' WHERE temp_id = ?', ['id2']); $this->assertEquals(123456, $row['temp_bigint']); + $this->assertIsScalar($row['temp_float']); $this->assertEquals(1.7, round((float) $row['temp_float'], 1)); } } diff --git a/tests/ConnectionRoundTest.php b/tests/ConnectionRoundTest.php index efb0fcf8..c605af5f 100644 --- a/tests/ConnectionRoundTest.php +++ b/tests/ConnectionRoundTest.php @@ -30,6 +30,7 @@ public function testRound(): void $query = 'SELECT ROUND(1.33333333333333, 8) AS val FROM ' . self::TEST_TABLE_NAME; $row = $this->getConnection()->getPRow($query); + $this->assertIsScalar($row['val']); $this->assertEquals('1.33333333', substr((string) $row['val'], 0, 10)); $this->assertEqualsWithDelta(1 + round(1 / 3, 8), (float) $row['val'], 0.0001); } @@ -43,6 +44,7 @@ public function testRoundUp(): void $query = 'SELECT ROUND(1.16666666666666, 8) AS val FROM ' . self::TEST_TABLE_NAME; $row = $this->getConnection()->getPRow($query); + $this->assertIsScalar($row['val']); $this->assertEquals('1.16666667', substr((string) $row['val'], 0, 10)); $this->assertEqualsWithDelta(1 + round(1 / 6, 8), (float) $row['val'], 0.0001); } diff --git a/tests/ConnectionStringLengthTest.php b/tests/ConnectionStringLengthTest.php index bd994490..1fa78e9e 100644 --- a/tests/ConnectionStringLengthTest.php +++ b/tests/ConnectionStringLengthTest.php @@ -31,6 +31,7 @@ public function testStringLength(): void . ' WHERE temp_char10 = ?'; $row = $this->getConnection()->getPRow($query, ['char10-3']); + $this->assertIsScalar($row['val']); $this->assertEquals(8, (int) $row['val']); } } diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 7a733098..e4e93493 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -125,12 +125,18 @@ public function testFloatHandling(): void $connection->insert(self::TEST_TABLE_NAME, ['temp_id' => 'id2', 'temp_float' => 1000.8]); $row = $connection->getPRow('SELECT * FROM ' . self::TEST_TABLE_NAME . ' where temp_id = ?', ['id1']); + if (!array_key_exists('temp_float', $row) || !is_numeric($row['temp_float'])) { + self::fail('Invalid temp_float value received.'); + } // MSSQL returns 16.799999237061 instead of 16.8 - $this->assertEquals(16.8, round((float) $row['temp_float'], 1)); + $this->assertSame(16.8, round((float) $row['temp_float'], 1)); $this->assertEquals('16.8', round((float) $row['temp_float'], 1)); $row = $connection->getPRow('SELECT * FROM ' . self::TEST_TABLE_NAME . ' where temp_id = ?', ['id2']); - $this->assertEquals(1000.8, round((float) $row['temp_float'], 1)); + if (!array_key_exists('temp_float', $row) || !is_numeric($row['temp_float'])) { + self::fail('Invalid temp_float value received.'); + } + $this->assertSame(1000.8, round((float) $row['temp_float'], 1)); $this->assertEquals('1000.8', round((float) $row['temp_float'], 1)); } @@ -240,6 +246,9 @@ public function testCreateTable(): void $this->assertTrue(count($row) >= 9, 'testDataBase getRow count'); $this->assertEquals('20200508095301', $row['temp_bigint'], 'testDataBase getRow content'); + if (!array_key_exists('temp_float', $row) || !is_numeric($row['temp_float'])) { + self::fail('Invalid temp_float value received.'); + } $this->assertEquals(23.45, round((float) $row['temp_float'], 2), 'testDataBase getRow content'); $this->assertEquals('char10-1', $row['temp_char10'], 'testDataBase getRow content'); $this->assertEquals('char20-1', $row['temp_char20'], 'testDataBase getRow content'); @@ -517,6 +526,7 @@ public function testGetGeneratorNoPaging(): void $i = 0; foreach ($result as $rows) { + /** @var array{temp_id:string} $row */ foreach ($rows as $row) { $database->_pQuery('DELETE FROM ' . self::TEST_TABLE_NAME . ' WHERE temp_id = ?', [$row['temp_id']]); $i++; @@ -707,6 +717,8 @@ public function testSqlConcat(): void } /** + * @param scalar $value + * * @throws ConnectionException * @throws QueryException */ @@ -744,7 +756,7 @@ public function testConvertToDatabaseValue(mixed $value, DataType $type): void $expect = substr((string) $expect, 0, 254); } elseif ($type === DataType::CHAR500) { $expect = substr((string) $expect, 0, 500); - } elseif ($type === DataType::FLOAT) { + } elseif ($type === DataType::FLOAT && is_scalar($actual)) { $actual = round((float) $actual, 1); } @@ -752,7 +764,7 @@ public function testConvertToDatabaseValue(mixed $value, DataType $type): void } /** - * @return array{mixed, DataType}[] + * @return array{scalar, DataType}[] */ public static function databaseValueProvider(): array { @@ -849,6 +861,10 @@ public function testGetJsonColumnExpression(): void $results = $connection->getPArray($query); foreach ($results as $result) { + if ((!is_int($result['temp_id']) && !is_string($result['temp_id'])) || !array_key_exists($result['temp_id'], $expected)) { + self::fail('Invalid temp id received.'); + } + $this->assertEquals($expected[$result['temp_id']], $result['extracted_value']); } } @@ -875,6 +891,10 @@ public function testGetNthLastElementFromSlug(): void ]; foreach ($results as $result) { + if ((!is_int($result['temp_id']) && !is_string($result['temp_id'])) || !array_key_exists($result['temp_id'], $expected)) { + self::fail('Invalid temp id received.'); + } + $this->assertEquals($expected[$result['temp_id']], $result['temp_text']); } @@ -889,6 +909,10 @@ public function testGetNthLastElementFromSlug(): void ]; foreach ($results as $result) { + if ((!is_int($result['temp_id']) && !is_string($result['temp_id'])) || !array_key_exists($result['temp_id'], $expected)) { + self::fail('Invalid temp id received.'); + } + $this->assertEquals($expected[$result['temp_id']], $result['temp_text']); } } diff --git a/tests/ConnectionTestCase.php b/tests/ConnectionTestCase.php index c0d7b61e..fb8e59c6 100644 --- a/tests/ConnectionTestCase.php +++ b/tests/ConnectionTestCase.php @@ -111,7 +111,7 @@ protected function generateSystemid(): string } /** - * @return ($assoc is true ? list> : list>) + * @return ($assoc is true ? list> : list>) */ protected function getRows(int $count, bool $assoc = true): array {