diff --git a/phpstan.neon b/phpstan.neon index e697482b..62ab5d12 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,7 +2,7 @@ includes: - phpstan-baseline.neon parameters: - level: 7 + level: 8 paths: - src - tests diff --git a/src/Connection.php b/src/Connection.php index 0035bc12..520f2cca 100755 --- a/src/Connection.php +++ b/src/Connection.php @@ -72,7 +72,7 @@ class Connection implements ConnectionInterface /** * Instance of the db-driver defined in the configs. */ - protected ?DriverInterface $dbDriver = null; + protected DriverInterface $dbDriver; /** * The number of transactions currently opened. @@ -120,7 +120,7 @@ public function close(): void $this->logger?->warning('Rolled back open transactions on deletion of current instance of Db!'); } - if ($this->dbDriver !== null && $this->connected) { + if ($this->connected) { $this->logger?->info('closing database-connection'); $this->dbDriver->dbclose(); @@ -216,7 +216,7 @@ public function selectRow( ), ); - $row = $this->getPRow($query, array_values($identifiers), 0, $cached, $escapes); + $row = $this->getPRow($query, array_values($identifiers), 0, $cached, $escapes ?? []); if ($row === []) { return null; } @@ -317,17 +317,15 @@ public function _pQuery(string $query, array $params = [], array $escapes = []): // Increasing the counter $this->number++; - if ($this->dbDriver !== null) { - try { - $output = $this->dbDriver->_pQuery($query, $this->dbsafeParams($params, $escapes)); - } catch (QueryException $e) { - $prettifiedQuery = $this->prettifyQuery($e->getQuery(), $e->getParams()); + try { + $output = $this->dbDriver->_pQuery($query, $this->dbsafeParams($params, $escapes)); + } catch (QueryException $e) { + $prettifiedQuery = $this->prettifyQuery($e->getQuery(), $e->getParams()); - $this->logger->error($e->getMessage()); - $this->logger->error('Query: ' . $prettifiedQuery); + $this->logger?->error($e->getMessage()); + $this->logger?->error('Query: ' . $prettifiedQuery); - throw $e; - } + throw $e; } if (!$output) { @@ -618,10 +616,7 @@ private function getError(string $query, array $params): void $this->dbconnect(); } - $error = ''; - if ($this->dbDriver !== null) { - $error = $this->dbDriver->getError(); - } + $error = $this->dbDriver->getError(); // reprocess query $query = str_ireplace( @@ -665,10 +660,6 @@ public function beginTransaction(): void $this->dbconnect(); } - if ($this->dbDriver === null) { - return; - } - // just start a new transaction, if no other transaction is open. if ($this->numberOfOpenTransactions === 0) { $this->dbDriver->beginTransaction(); @@ -700,10 +691,6 @@ public function commit(): void $this->dbconnect(); } - if ($this->dbDriver === null) { - return; - } - // check, if the current tx is allowed to be committed. if ($this->numberOfOpenTransactions === 1) { $this->numberOfOpenTransactions--; @@ -743,10 +730,6 @@ public function rollBack(): void $this->dbconnect(); } - if ($this->dbDriver === null) { - return; - } - if ($this->numberOfOpenTransactions === 1) { $this->dbDriver->rollBack(); $this->currentTransactionIsDirty = false; @@ -802,15 +785,13 @@ public function getTables(?string $prefix = null): array $this->tablesCache[$prefix] = []; - if ($this->dbDriver !== null) { - // increase global counter - $this->number++; - $tables = $this->dbDriver->getTables(); + // increase global counter + $this->number++; + $tables = $this->dbDriver->getTables(); - foreach ($tables as $table) { - if (str_starts_with((string) $table['name'], $prefix)) { - $this->tablesCache[$prefix][] = $table['name']; - } + foreach ($tables as $table) { + if (str_starts_with((string) $table['name'], $prefix)) { + $this->tablesCache[$prefix][] = $table['name']; } } @@ -845,7 +826,7 @@ public function getColumnsOfTable(string $tableName): array $columnName = $column->getName(); $return[$columnName] = [ 'columnName' => $columnName, - 'columnType' => $column->getInternalType(), + 'columnType' => $column->getInternalType() ?? DataType::CHAR254, ]; } @@ -949,7 +930,7 @@ public function generateTableFromMetadata(Table $table): void { $columns = []; foreach ($table->getColumns() as $colDef) { - $columns[$colDef->getName()] = [$colDef->getInternalType(), $colDef->isNullable()]; + $columns[$colDef->getName()] = [$colDef->getInternalType() ?? DataType::CHAR254, $colDef->isNullable()]; } $primary = []; @@ -1195,10 +1176,6 @@ public function getDbInfo(): array $this->dbconnect(); } - if ($this->dbDriver === null) { - return []; - } - return $this->dbDriver->getDbInfo(); } diff --git a/src/Driver/DriverAbstract.php b/src/Driver/DriverAbstract.php index 0a246e17..2f24509a 100644 --- a/src/Driver/DriverAbstract.php +++ b/src/Driver/DriverAbstract.php @@ -38,6 +38,9 @@ abstract class DriverAbstract implements DriverInterface protected ?ConnectionParameters $config = null; + /** + * @phpstan-assert ConnectionParameters $this->config + */ public function setConfig(ConnectionParameters $params): void { $this->config = $params; diff --git a/src/Driver/MysqliDriver.php b/src/Driver/MysqliDriver.php index 7e1c56e1..1775c9eb 100755 --- a/src/Driver/MysqliDriver.php +++ b/src/Driver/MysqliDriver.php @@ -27,6 +27,7 @@ use mysqli_sql_exception; use mysqli_stmt; use Override; +use RuntimeException; use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; @@ -98,6 +99,8 @@ public function dbclose(): void return; } + $this->assertConnected(); + $this->linkDB->close(); $this->linkDB = null; $this->connected = false; @@ -237,6 +240,8 @@ public function insertOrUpdate(string $table, array $columns, array $values, arr #[Override] public function getError(): string { + $this->assertConnected(); + $error = $this->errorMessage . ' ' . $this->linkDB->error; $this->errorMessage = ''; @@ -274,7 +279,7 @@ public function getTableInformation(string $tableName): Table $table->addColumn( TableColumn::make($column['Field']) ->setInternalType($this->getCoreTypeForDbType($column)) - ->setDatabaseType($this->getDatatype($this->getCoreTypeForDbType($column))) + ->setDatabaseType($this->getDatatype($this->getCoreTypeForDbType($column) ?? DataType::CHAR254)) ->setNullable($column['Null'] === 'YES'), ); } @@ -454,6 +459,8 @@ public function deleteIndex(string $table, string $index): bool #[Override] public function beginTransaction(): void { + $this->assertConnected(); + $this->linkDB->begin_transaction(); } @@ -472,6 +479,8 @@ public function transactionBegin(): void #[Override] public function commit(): void { + $this->assertConnected(); + $this->linkDB->commit(); } @@ -487,6 +496,8 @@ public function transactionCommit(): void #[Override] public function rollBack(): void { + $this->assertConnected(); + $this->linkDB->rollback(); } @@ -505,6 +516,8 @@ public function transactionRollback(): void #[Override] public function getDbInfo(): array { + $this->assertConnected(); + return [ 'dbbserver' => 'MySQL ' . $this->linkDB->server_info, 'server_version' => $this->linkDB->server_version, @@ -542,6 +555,10 @@ public function encloseTableName(string $table): string #[Override] public function dbExport(string &$fileName, array $tables): bool { + if (!$this->config instanceof ConnectionParameters) { + throw new RuntimeException('Connection parameters not set'); + } + $dumpBin = new ExecutableFinder()->find($this->dumpBin); $dumpParams = [ $dumpBin, @@ -578,7 +595,11 @@ public function dbExport(string &$fileName, array $tables): bool public function dbImport(string $fileName): bool { if (!in_array(pathinfo($fileName, PATHINFO_EXTENSION), ['sql', 'gz'])) { - throw new \RuntimeException(trim($fileName . ' is not a valid import file')); + throw new RuntimeException(trim($fileName . ' is not a valid import file')); + } + + if (!$this->config instanceof ConnectionParameters) { + throw new RuntimeException('Connection parameters not set'); } $restoreBin = new ExecutableFinder()->find($this->restoreBin); @@ -598,7 +619,7 @@ public function dbImport(string $fileName): bool } elseif (pathinfo($fileName, PATHINFO_EXTENSION) === 'sql') { $fileCommand = sprintf('cat %s', escapeshellarg($fileName)); } else { - throw new \RuntimeException(trim($fileName . ' is not a valid import file')); + throw new RuntimeException(trim($fileName . ' is not a valid import file')); } $process = new Process([ @@ -636,6 +657,8 @@ private function getPreparedStatement(string $query): false | mysqli_stmt $this->statementsCache = []; } + $this->assertConnected(); + $statement = $this->linkDB->stmt_init(); try { @@ -677,4 +700,14 @@ public function getNthLastElementFromSlug(string $column, int $position): string { return "SUBSTRING_INDEX(SUBSTRING_INDEX($column, '/', -$position), '/', 1)"; } + + /** + * @phpstan-assert mysqli $this->linkDB + */ + private function assertConnected(): void + { + if ($this->linkDB === null) { + throw new ConnectionException('Database not connected.'); + } + } } diff --git a/src/Driver/PostgresDriver.php b/src/Driver/PostgresDriver.php index 1c985f75..bbf163b8 100755 --- a/src/Driver/PostgresDriver.php +++ b/src/Driver/PostgresDriver.php @@ -236,7 +236,7 @@ public function getTableInformation(string $tableName): Table $table->addColumn( TableColumn::make($column['column_name']) ->setInternalType($this->getCoreTypeForDbType($column)) - ->setDatabaseType($this->getDatatype($this->getCoreTypeForDbType($column))) + ->setDatabaseType($this->getDatatype($this->getCoreTypeForDbType($column) ?? DataType::CHAR254)) ->setNullable($column['is_nullable'] === 'YES'), ); } @@ -510,6 +510,10 @@ public function dbExport(string &$fileName, array $tables): bool $tablesString = '-t ' . implode(' -t ', array_map('escapeshellarg', $tables)); } + if (!$this->config instanceof ConnectionParameters) { + throw new RuntimeException('Connection parameters not set'); + } + $port = $this->config->getPort(); if (empty($port)) { $port = 5432; @@ -557,6 +561,10 @@ public function dbImport(string $fileName): bool throw new RuntimeException(trim($fileName . ' is not a valid import file')); } + if (!$this->config instanceof ConnectionParameters) { + throw new RuntimeException('Connection parameters not set'); + } + $restoreBin = new ExecutableFinder()->find($this->restoreBin); if ($this->handlesDumpCompression() && pathinfo($fileName, PATHINFO_EXTENSION) === 'gz') { $restoreParams = [ @@ -618,7 +626,7 @@ protected function processQuery(string $query): string $i++; return '$' . $i; - }, $query); + }, $query) ?? ''; return str_replace(' LIKE ', ' ILIKE ', $query); } diff --git a/src/Driver/Sqlite3Driver.php b/src/Driver/Sqlite3Driver.php index 3e18bcc2..44662b94 100755 --- a/src/Driver/Sqlite3Driver.php +++ b/src/Driver/Sqlite3Driver.php @@ -306,6 +306,8 @@ public function _pQuery(string $query, array $params): bool throw new QueryException('Could not execute statement: ' . $this->getError(), $query, $params); } + $this->assertConnected(); + $this->affectedRowsCount = $this->linkDB->changes(); return true; @@ -351,6 +353,8 @@ public function getPArray(string $query, array $params): Generator #[Override] public function getError(): string { + $this->assertConnected(); + return $this->linkDB->lastErrorMsg(); } @@ -385,7 +389,7 @@ public function getTableInformation(string $tableName): Table $table->addColumn( TableColumn::make($column['name']) ->setInternalType($this->getCoreTypeForDbType($column)) - ->setDatabaseType($this->getDatatype($this->getCoreTypeForDbType($column))) + ->setDatabaseType($this->getDatatype($this->getCoreTypeForDbType($column) ?? DataType::CHAR254)) ->setNullable($column['notnull'] == 0), ); @@ -543,6 +547,8 @@ public function transactionRollback(): void #[Override] public function getDbInfo(): array { + $this->assertConnected(); + $timeout = iterator_to_array($this->getPArray('PRAGMA busy_timeout', []), false); $encoding = iterator_to_array($this->getPArray('PRAGMA encoding', []), false); @@ -615,7 +621,7 @@ private function processQuery(string $query): string $i++; return ':param' . $i; - }, $query); + }, $query) ?? ''; } /** @@ -629,6 +635,8 @@ private function getPreparedStatement(string $query): false | SQLite3Stmt return $this->statementsCache[$name]; } + $this->assertConnected(); + $statement = $this->linkDB->prepare($query); $this->statementsCache[$name] = $statement; @@ -689,4 +697,14 @@ public function getNthLastElementFromSlug(string $column, int $position): string { return "extract_nth_last_slug_segment($column, $position)"; } + + /** + * @phpstan-assert SQLite3 $this->linkDB + */ + private function assertConnected(): void + { + if (!$this->linkDB instanceof SQLite3) { + throw new ConnectionException('Database not connected.'); + } + } }