From e0180d8c8c341a166ff99649f8295cc5fd4deb49 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 30 Oct 2025 15:57:22 +0100 Subject: [PATCH 01/15] removed forum.dibiphp.com --- contributing.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contributing.md b/contributing.md index a6a9e4de8..a8fa29720 100644 --- a/contributing.md +++ b/contributing.md @@ -11,9 +11,6 @@ Dibi welcomes your contributions. There are several ways to help out: Issues ------ -Please **do not use the issue tracker to ask questions**. We will be happy to help you -on [Dibi forum](https://forum.dibiphp.com). - A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. @@ -24,8 +21,6 @@ case to convince the project's developers of the merits of this feature. Contributing ------------ -The best way to propose a feature is to discuss your ideas on [Dibi forum](https://forum.dibiphp.com) before implementing them. - Please do not fix whitespace, format code, or make a purely cosmetic patch. Thanks! :heart: From 193782e7489f79342294202af0ca0084cefa4201 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 30 Oct 2025 15:57:56 +0100 Subject: [PATCH 02/15] changed homepage to https://dibi.nette.org --- composer.json | 2 +- readme.md | 4 ++-- src/Dibi/Bridges/Nette/DibiExtension22.php | 2 +- src/Dibi/Bridges/Nette/DibiExtension3.php | 2 +- src/Dibi/Bridges/Tracy/Panel.php | 2 +- src/Dibi/Connection.php | 2 +- src/Dibi/DataSource.php | 2 +- src/Dibi/DateTime.php | 2 +- src/Dibi/Drivers/DummyDriver.php | 2 +- src/Dibi/Drivers/FirebirdDriver.php | 2 +- src/Dibi/Drivers/FirebirdReflector.php | 2 +- src/Dibi/Drivers/FirebirdResult.php | 2 +- src/Dibi/Drivers/MySqlReflector.php | 2 +- src/Dibi/Drivers/MySqliDriver.php | 2 +- src/Dibi/Drivers/MySqliResult.php | 2 +- src/Dibi/Drivers/NoDataResult.php | 2 +- src/Dibi/Drivers/OdbcDriver.php | 2 +- src/Dibi/Drivers/OdbcReflector.php | 2 +- src/Dibi/Drivers/OdbcResult.php | 2 +- src/Dibi/Drivers/OracleDriver.php | 2 +- src/Dibi/Drivers/OracleReflector.php | 2 +- src/Dibi/Drivers/OracleResult.php | 2 +- src/Dibi/Drivers/PdoDriver.php | 2 +- src/Dibi/Drivers/PdoResult.php | 2 +- src/Dibi/Drivers/PostgreDriver.php | 2 +- src/Dibi/Drivers/PostgreReflector.php | 2 +- src/Dibi/Drivers/PostgreResult.php | 2 +- src/Dibi/Drivers/Sqlite3Driver.php | 2 +- src/Dibi/Drivers/Sqlite3Result.php | 2 +- src/Dibi/Drivers/SqliteDriver.php | 2 +- src/Dibi/Drivers/SqliteReflector.php | 2 +- src/Dibi/Drivers/SqliteResult.php | 2 +- src/Dibi/Drivers/SqlsrvDriver.php | 2 +- src/Dibi/Drivers/SqlsrvReflector.php | 2 +- src/Dibi/Drivers/SqlsrvResult.php | 2 +- src/Dibi/Event.php | 2 +- src/Dibi/Expression.php | 2 +- src/Dibi/Fluent.php | 2 +- src/Dibi/HashMap.php | 2 +- src/Dibi/Helpers.php | 2 +- src/Dibi/Literal.php | 2 +- src/Dibi/Loggers/FileLogger.php | 2 +- src/Dibi/Reflection/Column.php | 2 +- src/Dibi/Reflection/Database.php | 2 +- src/Dibi/Reflection/ForeignKey.php | 2 +- src/Dibi/Reflection/Index.php | 2 +- src/Dibi/Reflection/Result.php | 2 +- src/Dibi/Reflection/Table.php | 2 +- src/Dibi/Result.php | 2 +- src/Dibi/ResultIterator.php | 2 +- src/Dibi/Row.php | 2 +- src/Dibi/Translator.php | 2 +- src/Dibi/Type.php | 2 +- src/Dibi/dibi.php | 2 +- src/Dibi/exceptions.php | 2 +- src/Dibi/interfaces.php | 2 +- 56 files changed, 57 insertions(+), 57 deletions(-) diff --git a/composer.json b/composer.json index a562a8b40..0e7f49c79 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "dibi/dibi", "description": "Dibi is Database Abstraction Library for PHP", "keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"], - "homepage": "https://dibiphp.com", + "homepage": "https://dibi.nette.org", "license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], "authors": [ { diff --git a/readme.md b/readme.md index a0a86310b..75b330b6a 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -[Dibi](https://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://nette.org/make-donation?to=dibi) +[Dibi](https://dibi.nette.org) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://nette.org/make-donation?to=dibi) ========================================================= [![Downloads this Month](https://img.shields.io/packagist/dm/dibi/dibi.svg)](https://packagist.org/packages/dibi/dibi) @@ -41,7 +41,7 @@ Usage ----- Refer to the `examples` directory for examples. Dibi documentation is -available on the [homepage](https://dibiphp.com). +available on the [homepage](https://dibi.nette.org). ### Connecting to database diff --git a/src/Dibi/Bridges/Nette/DibiExtension22.php b/src/Dibi/Bridges/Nette/DibiExtension22.php index 8f310b034..3af5ec3cf 100644 --- a/src/Dibi/Bridges/Nette/DibiExtension22.php +++ b/src/Dibi/Bridges/Nette/DibiExtension22.php @@ -1,7 +1,7 @@ Date: Sun, 30 Nov 2025 18:22:27 +0100 Subject: [PATCH 03/15] stricter null checking --- src/Dibi/Connection.php | 2 +- src/Dibi/DateTime.php | 2 +- src/Dibi/Result.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Dibi/Connection.php b/src/Dibi/Connection.php index b03e0e27b..c66a78ae5 100644 --- a/src/Dibi/Connection.php +++ b/src/Dibi/Connection.php @@ -286,7 +286,7 @@ final public function nativeQuery(#[Language('SQL')] string $sql): Result throw $e; } - $res = $this->createResultSet($res ?: new Drivers\NoDataResult(max(0, $this->driver->getAffectedRows()))); + $res = $this->createResultSet($res ?? new Drivers\NoDataResult(max(0, $this->driver->getAffectedRows()))); if ($event) { $this->onEvent($event->done($res)); } diff --git a/src/Dibi/DateTime.php b/src/Dibi/DateTime.php index 653bd41fb..a5d263e02 100644 --- a/src/Dibi/DateTime.php +++ b/src/Dibi/DateTime.php @@ -17,7 +17,7 @@ class DateTime extends \DateTimeImmutable { public function __construct(string|int $time = 'now', ?\DateTimeZone $timezone = null) { - $timezone = $timezone ?: new \DateTimeZone(date_default_timezone_get()); + $timezone ??= new \DateTimeZone(date_default_timezone_get()); if (is_numeric($time)) { $tmp = (new self('@' . $time))->setTimezone($timezone); parent::__construct($tmp->format('Y-m-d H:i:s.u'), $tmp->getTimezone()); diff --git a/src/Dibi/Result.php b/src/Dibi/Result.php index 50046ac1b..4f0ae0c15 100644 --- a/src/Dibi/Result.php +++ b/src/Dibi/Result.php @@ -202,7 +202,7 @@ final public function fetchSingle(): mixed final public function fetchAll(?int $offset = null, ?int $limit = null): array { $limit ??= -1; - $this->seek($offset ?: 0); + $this->seek($offset ?? 0); $row = $this->fetch(); if (!$row) { return []; // empty result set From 9f77781b42a5109795750d997f29c1a41091ef20 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 27 Dec 2025 03:01:55 +0100 Subject: [PATCH 04/15] cs --- src/Dibi/Bridges/Nette/DibiExtension3.php | 2 +- src/Dibi/Bridges/Tracy/Panel.php | 2 +- src/Dibi/Drivers/MySqliDriver.php | 8 ++++---- src/Dibi/Drivers/MySqliResult.php | 4 ++-- src/Dibi/Drivers/OracleDriver.php | 6 +++--- src/Dibi/Drivers/PostgreDriver.php | 2 +- src/Dibi/Event.php | 6 +++--- src/Dibi/Fluent.php | 11 ++++------- src/Dibi/Helpers.php | 2 +- src/Dibi/Reflection/Database.php | 2 +- src/Dibi/Result.php | 2 +- src/Dibi/Translator.php | 2 +- src/Dibi/exceptions.php | 18 +++++++----------- tests/dibi/bootstrap.php | 6 +++--- 14 files changed, 33 insertions(+), 40 deletions(-) diff --git a/src/Dibi/Bridges/Nette/DibiExtension3.php b/src/Dibi/Bridges/Nette/DibiExtension3.php index 69634de8c..dba443d1c 100644 --- a/src/Dibi/Bridges/Nette/DibiExtension3.php +++ b/src/Dibi/Bridges/Nette/DibiExtension3.php @@ -52,7 +52,7 @@ public function getConfigSchema(): Nette\Schema\Schema } - public function loadConfiguration() + public function loadConfiguration(): void { $container = $this->getContainerBuilder(); $config = $this->getConfig(); diff --git a/src/Dibi/Bridges/Tracy/Panel.php b/src/Dibi/Bridges/Tracy/Panel.php index 8a8c15260..68247c04e 100644 --- a/src/Dibi/Bridges/Tracy/Panel.php +++ b/src/Dibi/Bridges/Tracy/Panel.php @@ -119,7 +119,7 @@ public function getPanel(): ?string : ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN'); try { $explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), return: true); - } catch (Dibi\Exception $e) { + } catch (Dibi\Exception) { } [$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime] = $backup; diff --git a/src/Dibi/Drivers/MySqliDriver.php b/src/Dibi/Drivers/MySqliDriver.php index 1d4752662..8128474ee 100644 --- a/src/Dibi/Drivers/MySqliDriver.php +++ b/src/Dibi/Drivers/MySqliDriver.php @@ -165,13 +165,13 @@ public function query(string $sql): ?Dibi\ResultDriver public static function createException(string $message, int|string $code, string $sql): Dibi\DriverException { - if (in_array($code, [1216, 1217, 1451, 1452, 1701], true)) { + if (in_array($code, [1216, 1217, 1451, 1452, 1701], strict: true)) { return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql); - } elseif (in_array($code, [1062, 1557, 1569, 1586], true)) { + } elseif (in_array($code, [1062, 1557, 1569, 1586], strict: true)) { return new Dibi\UniqueConstraintViolationException($message, $code, $sql); - } elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], true)) { + } elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], strict: true)) { return new Dibi\NotNullConstraintViolationException($message, $code, $sql); } else { @@ -256,7 +256,7 @@ public function getResource(): ?\mysqli { try { return @$this->connection->thread_id ? $this->connection : null; - } catch (\Throwable $e) { + } catch (\Throwable) { return null; } } diff --git a/src/Dibi/Drivers/MySqliResult.php b/src/Dibi/Drivers/MySqliResult.php index 6276e5f76..6c79a3886 100644 --- a/src/Dibi/Drivers/MySqliResult.php +++ b/src/Dibi/Drivers/MySqliResult.php @@ -80,10 +80,10 @@ public function getResultColumns(): array { static $types; if ($types === null) { - $consts = get_defined_constants(true); + $consts = get_defined_constants(categorize: true); $types = []; foreach ($consts['mysqli'] ?? [] as $key => $value) { - if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) { + if (str_starts_with($key, 'MYSQLI_TYPE_')) { $types[$value] = substr($key, 12); } } diff --git a/src/Dibi/Drivers/OracleDriver.php b/src/Dibi/Drivers/OracleDriver.php index 63601761d..992cbcb05 100644 --- a/src/Dibi/Drivers/OracleDriver.php +++ b/src/Dibi/Drivers/OracleDriver.php @@ -104,13 +104,13 @@ public function query(string $sql): ?Dibi\ResultDriver public static function createException(string $message, $code, string $sql): Dibi\DriverException { - if (in_array($code, [1, 2299, 38911], true)) { + if (in_array($code, [1, 2299, 38911], strict: true)) { return new Dibi\UniqueConstraintViolationException($message, $code, $sql); - } elseif (in_array($code, [1400], true)) { + } elseif (in_array($code, [1400], strict: true)) { return new Dibi\NotNullConstraintViolationException($message, $code, $sql); - } elseif (in_array($code, [2266, 2291, 2292], true)) { + } elseif (in_array($code, [2266, 2291, 2292], strict: true)) { return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql); } else { diff --git a/src/Dibi/Drivers/PostgreDriver.php b/src/Dibi/Drivers/PostgreDriver.php index 637b96d78..a4d50ea72 100644 --- a/src/Dibi/Drivers/PostgreDriver.php +++ b/src/Dibi/Drivers/PostgreDriver.php @@ -216,7 +216,7 @@ public function rollback(?string $savepoint = null): void */ public function inTransaction(): bool { - return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], true); + return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], strict: true); } diff --git a/src/Dibi/Event.php b/src/Dibi/Event.php index 022bb301b..20d0d0d74 100644 --- a/src/Dibi/Event.php +++ b/src/Dibi/Event.php @@ -46,7 +46,7 @@ public function __construct(Connection $connection, int $type, ?string $sql = nu $this->connection = $connection; $this->type = $type; $this->sql = trim((string) $sql); - $this->time = -microtime(true); + $this->time = -microtime(as_float: true); if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) { $types = [ @@ -79,11 +79,11 @@ public function done(Result|DriverException|null $result = null): static $this->result = $result; try { $this->count = $result instanceof Result ? count($result) : null; - } catch (Exception $e) { + } catch (Exception) { $this->count = null; } - $this->time += microtime(true); + $this->time += microtime(as_float: true); \dibi::$elapsedTime = $this->time; \dibi::$totalTime += $this->time; return $this; diff --git a/src/Dibi/Fluent.php b/src/Dibi/Fluent.php index 3881fa0b0..cfb8192f4 100644 --- a/src/Dibi/Fluent.php +++ b/src/Dibi/Fluent.php @@ -98,8 +98,6 @@ class Fluent implements IDataSource 'LEFT JOIN' => 'FROM', 'RIGHT JOIN' => 'FROM', ]; - - private readonly Connection $connection; private array $setups = []; private ?string $command = null; private array $clauses = []; @@ -110,10 +108,9 @@ class Fluent implements IDataSource private static HashMap $normalizer; - public function __construct(Connection $connection) - { - $this->connection = $connection; - + public function __construct( + private readonly Connection $connection, + ) { if (!isset(self::$normalizer)) { self::$normalizer = new HashMap(self::_formatClause(...)); } @@ -378,7 +375,7 @@ public function count(): int } - private function query($args): Result + private function query(array $args): Result { $res = $this->connection->query($args); foreach ($this->setups as $setup) { diff --git a/src/Dibi/Helpers.php b/src/Dibi/Helpers.php index 747195139..17a90a1f6 100644 --- a/src/Dibi/Helpers.php +++ b/src/Dibi/Helpers.php @@ -146,7 +146,7 @@ public static function getSuggestion(array $items, string $value): ?string { $best = null; $min = (strlen($value) / 4 + 1) * 10 + .1; - $items = array_map('strval', $items); + $items = array_map(strval(...), $items); foreach (array_unique($items) as $item) { if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) { $min = $len; diff --git a/src/Dibi/Reflection/Database.php b/src/Dibi/Reflection/Database.php index 85a870403..796159492 100644 --- a/src/Dibi/Reflection/Database.php +++ b/src/Dibi/Reflection/Database.php @@ -28,7 +28,7 @@ class Database public function __construct( private readonly Dibi\Reflector $reflector, - private ?string $name = null, + private readonly ?string $name = null, ) { } diff --git a/src/Dibi/Result.php b/src/Dibi/Result.php index 4f0ae0c15..05c340ddd 100644 --- a/src/Dibi/Result.php +++ b/src/Dibi/Result.php @@ -439,7 +439,7 @@ private function detectTypes(): void foreach ($this->getResultDriver()->getResultColumns() as $col) { $this->types[$col['name']] = $col['type'] ?? $cache->{$col['nativetype']}; } - } catch (NotSupportedException $e) { + } catch (NotSupportedException) { } } diff --git a/src/Dibi/Translator.php b/src/Dibi/Translator.php index be28e1871..0b5dfb6f4 100644 --- a/src/Dibi/Translator.php +++ b/src/Dibi/Translator.php @@ -290,7 +290,7 @@ public function formatValue(mixed $value, ?string $modifier): string return implode(', ', $vx); case 'ex!': - trigger_error('Use Dibi\Expression instead of array: ' . implode(', ', array_filter($value, 'is_scalar')), E_USER_WARNING); + trigger_error('Use Dibi\Expression instead of array: ' . implode(', ', array_filter($value, is_scalar(...))), E_USER_WARNING); // break omitted case 'ex': case 'sql': diff --git a/src/Dibi/exceptions.php b/src/Dibi/exceptions.php index 9419a38b4..8293ba9a6 100644 --- a/src/Dibi/exceptions.php +++ b/src/Dibi/exceptions.php @@ -15,18 +15,14 @@ */ class Exception extends \Exception { - private ?string $sql; - - public function __construct( string $message = '', int|string $code = 0, - ?string $sql = null, + private readonly ?string $sql = null, ?\Throwable $previous = null, ) { parent::__construct($message, 0, $previous); $this->code = $code; - $this->sql = $sql; } @@ -84,16 +80,16 @@ class NotSupportedException extends Exception */ class ProcedureException extends Exception { - protected string $severity; - - /** * Construct the exception. */ - public function __construct(string $message = '', int $code = 0, string $severity = '', ?string $sql = null) - { + public function __construct( + string $message = '', + int $code = 0, + protected string $severity = '', + ?string $sql = null, + ) { parent::__construct($message, $code, $sql); - $this->severity = $severity; } diff --git a/tests/dibi/bootstrap.php b/tests/dibi/bootstrap.php index 655c40542..c55da32e7 100644 --- a/tests/dibi/bootstrap.php +++ b/tests/dibi/bootstrap.php @@ -19,7 +19,7 @@ // load connection try { $config = Tester\Environment::loadData(); -} catch (Throwable $e) { +} catch (Throwable) { $config = parse_ini_file(__DIR__ . '/../databases.ini', process_sections: true); $config = reset($config); } @@ -62,7 +62,7 @@ function reformat($s) return strtr($s, '[]', '``'); } elseif ($config['system'] === 'postgre') { return strtr($s, '[]', '""'); - } elseif (in_array($config['system'], ['odbc', 'sqlite', 'sqlsrv'], true)) { + } elseif (in_array($config['system'], ['odbc', 'sqlite', 'sqlsrv'], strict: true)) { return $s; } else { trigger_error("Unsupported driver $config[system]", E_USER_WARNING); @@ -73,7 +73,7 @@ function reformat($s) function num($n) { $config = $GLOBALS['config']; - if (substr($config['dsn'] ?? '', 0, 5) === 'odbc:') { + if (str_starts_with($config['dsn'] ?? '', 'odbc:')) { $n = is_float($n) ? "$n.0" : (string) $n; } return $n; From abe62962f03d07cb7d9b7cdf88a4859764a83e52 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 8 Jan 2026 23:42:06 +0100 Subject: [PATCH 05/15] updated .gitattributes --- .gitattributes | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.gitattributes b/.gitattributes index d4a788ade..e1bccc4bf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,9 @@ -.gitattributes export-ignore -.gitignore export-ignore -.github export-ignore -appveyor.yml export-ignore -ncs.* export-ignore -phpstan.neon export-ignore -tests/ export-ignore +.gitattributes export-ignore +.github/ export-ignore +.gitignore export-ignore +ncs.* export-ignore +phpstan*.neon export-ignore +tests/ export-ignore -*.sh eol=lf -*.php* diff=php linguist-language=PHP +*.php* diff=php +*.sh text eol=lf From 5cbe6d6d64e971742a38d1e5f3f9e5959b44c95a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 29 Dec 2025 01:02:03 +0100 Subject: [PATCH 06/15] github actions: use composer tester script --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a172a2cf3..676680ce0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -103,7 +103,7 @@ jobs: run: docker exec -i mssql /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'CREATE DATABASE dibi_test' -N -C - run: composer install --no-progress --prefer-dist - - run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src + - run: composer tester -- -p phpdbg --coverage ./coverage.xml --coverage-src ./src - if: failure() uses: actions/upload-artifact@v4 with: From 7539f5b2a567f467861c3882391d53f86b45fa0f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 28 Dec 2025 03:17:18 +0100 Subject: [PATCH 07/15] added CLAUDE.md --- .gitattributes | 1 + CLAUDE.md | 428 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 429 insertions(+) create mode 100644 CLAUDE.md diff --git a/.gitattributes b/.gitattributes index e1bccc4bf..ed8103553 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ .gitattributes export-ignore .github/ export-ignore .gitignore export-ignore +CLAUDE.md export-ignore ncs.* export-ignore phpstan*.neon export-ignore tests/ export-ignore diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..d68c40644 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,428 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**Dibi** is a PHP database abstraction library (DBAL) providing a unified interface for multiple database systems (MySQL, PostgreSQL, SQLite, Oracle, Firebird, ODBC). It is a standalone library, not an application. + +- **PHP Version**: 8.2 - 8.5 +- **Homepage**: https://dibi.nette.org +- **Author**: David Grudl (Nette framework creator) + +## Essential Commands + +### Development Workflow + +```bash +# Run all tests +composer run tester + +# Run specific test file +vendor/bin/tester tests/dibi/DataSource.phpt -s -C + +# Run tests in a directory +vendor/bin/tester tests/dibi/ -s -C + +# Static analysis (PHPStan Level 5) +composer run phpstan +``` + +### Installation + +```bash +composer install +``` + +## Architecture Overview + +### Core Components + +**Connection Layer** (`src/Dibi/Connection.php`): +- Main entry point for all database operations +- Manages connection lifecycle, transactions, and query execution +- Event system for profiling/logging +- Lazy connection initialization + +**Driver Architecture** (`src/Dibi/Drivers/`): +- 27+ driver implementations (MySqli, PDO, PostgreSQL, SQLite3, Oracle, Firebird, ODBC, etc.) +- Each driver implements: `Driver`, `ResultDriver`, and `Reflector` interfaces +- Handles database-specific SQL escaping, type conversion, and LIMIT/OFFSET injection +- Key files: `MySqliDriver.php`, `PdoDriver.php`, `PostgreDriver.php`, `Sqlite3Driver.php` + +**Query Building** (`src/Dibi/Fluent.php`): +- Fluent interface for building dynamic SQL queries +- Methods: `select()`, `from()`, `where()`, `join()`, `orderBy()`, `limit()`, etc. +- Supports SELECT, INSERT, UPDATE, DELETE operations +- Cloning support for query variants + +**Parameter Translation** (`src/Dibi/Translator.php`): +- Most complex component - converts parameter modifiers to driver-specific escaping +- Handles modifiers: `%s`, `%i`, `%n`, `%d`, `%dt`, `%and`, `%or`, `%a`, `%v`, etc. +- Smart array expansion for IN clauses +- Conditional SQL blocks with `%if...%end` +- Expression support via `Literal` and `Expression` + +**Result Handling** (`src/Dibi/Result.php`, `src/Dibi/Row.php`): +- Lazy loading of rows from drivers +- Multiple fetch methods: `fetch()`, `fetchAll()`, `fetchAssoc()`, `fetchPairs()`, `fetchSingle()` +- Automatic type detection and conversion +- Iterator implementation for memory-efficient processing + +**Database Reflection** (`src/Dibi/Reflection/`): +- Schema introspection API for tables, columns, indexes, foreign keys +- Works through `Reflector` interface implemented by each driver +- Key classes: `Database`, `Table`, `Column`, `Index`, `ForeignKey` + +**Framework Integration** (`src/Dibi/Bridges/`): +- `Nette/DibiExtension3.php` - Nette 3.x DI container integration +- `Tracy/Panel.php` - Tracy debugger panel for query visualization + +### Key Interfaces + +Defined in `src/Dibi/interfaces.php`: +- `Driver` - Database-specific operations (query, escape, reflection) +- `ResultDriver` - Result set handling +- `Reflector` - Schema introspection +- `IConnection` - Connection contract +- `IDataSource` - Generic data source abstraction + +### Static Facade + +`src/Dibi/dibi.php` provides a static facade for global access to the default connection instance (backward compatibility layer). + +## Code Standards + +### PHP Coding Style + +- **Strict types required**: `declare(strict_types=1)` in all files +- **Tabs for indentation** (not spaces) +- **Single quotes** for strings (except HTML) +- **Two empty lines between methods** +- **PSR-12 compliant** with Nette modifications +- **Type declarations**: All properties, parameters, and return types must be typed +- **Return type and opening brace on separate lines**: + +```php +public function example( + string $param, + array $options, +): void +{ + // method body +} +``` + +### Documentation (PHPDoc) + +- Never duplicate signature information without adding value +- Document array contents: `@return string[]` +- Use two spaces after type/param: `@return string description` +- Only document when explaining additional context, limitations, or unusual usage +- For exceptions, describe the problem in active voice without "Exception that is thrown when" + +### Naming Conventions + +- Avoid abbreviations unless full name is too long +- UPPERCASE for two-letter abbreviations, PascalCase/camelCase for longer ones +- PascalCase for classes, camelCase for methods/properties +- Never use prefixes like `Abstract`, `Interface`, or `I` + +## Testing Patterns + +### Test File Structure + +Tests use **Nette Tester** with `.phpt` extension: + +```php +loadFile(__DIR__ . "/data/$config[system].sql"); + +// Test description as first parameter +Assert::same(3, $conn->dataSource('SELECT * FROM products')->count()); + +Assert::exception( + fn() => $conn->query('INVALID SQL'), + Dibi\Exception::class, + 'SQL error message', +); +``` + +### Key Testing Principles + +- Each test file covers a specific class or feature +- Use `Assert::same()` for strict comparisons +- Use `Assert::match()` for pattern matching with SQL +- Use `Assert::exception()` for exception testing +- Tests are located in `tests/dibi/` +- Test data SQL files in `tests/dibi/data/` + +## Driver-Specific Development + +When modifying or adding drivers: + +1. **Implement core interfaces**: `Driver`, `ResultDriver`, `Reflector` +2. **Handle type escaping** for: text, binary, identifier, boolean, date, datetime +3. **SQL injection methods**: How to add LIMIT/OFFSET to queries +4. **Result handling**: Create driver-specific `*Result` class +5. **Schema reflection**: Create driver-specific `*Reflector` class + +## Common Development Patterns + +### Adding New Modifiers + +Modifiers are handled in `src/Dibi/Translator.php`. The translation logic maps modifiers to driver-specific escaping: + +- Check `formatValue()` method for value formatting +- Update `modifier()` method for new modifier types +- Test with multiple drivers + +### Working with Results + +Result processing happens in two stages: +1. **Driver-level**: `ResultDriver` implementation returns raw data +2. **Abstraction-level**: `Result` class processes and normalizes data + +### Exception Hierarchy + +All exceptions inherit from `Dibi\Exception`. Specific exceptions in `src/Dibi/exceptions.php`: +- `ConstraintViolationException` +- `ForeignKeyConstraintViolationException` +- `NotNullConstraintViolationException` +- `UniqueConstraintViolationException` + +## Supported Databases + +The library supports these database systems (drivers in `src/Dibi/Drivers/`): + +- **MySQL/MariaDB** (via MySqli or PDO) +- **PostgreSQL** (native or PDO) +- **SQLite 3** (native or PDO) +- **MS SQL Server** (2012+, PDO or sqlsrv) +- **Oracle** (OCI8 or PDO) +- **Firebird/Interbase** +- **ODBC** +- **DummyDriver** for testing + +## Query Language Features + +### Parameter Modifiers + +Basic value modifiers: +- `%s` - string (NULL if value is null) +- `%sN` - string, but '' translates as NULL +- `%bin` - binary data +- `%b` - boolean +- `%i` - integer +- `%iN` - integer, but 0 translates as NULL +- `%f` - float +- `%d` - date (accepts DateTime, string or UNIX timestamp) +- `%dt` - datetime (accepts DateTime, string or UNIX timestamp) +- `%n` - identifier (table or column name, handles dots for qualified names) +- `%N` - identifier, treats period as ordinary character (for aliases, database names) +- `%SQL` - direct SQL insertion (alternative: `Dibi\Literal`) +- `%ex` - expands array of SQL expressions +- `%lmt` - adds LIMIT clause +- `%ofs` - adds OFFSET clause + +LIKE modifiers for pattern matching: +- `%like~` - expression starts with string +- `%~like` - expression ends with string +- `%~like~` - expression contains string +- `%like` - expression matches string + +### Array Modifiers + +When passing arrays as parameters: +- `%and` - `key1 = value1 AND key2 = value2 AND ...` +- `%or` - `key1 = value1 OR key2 = value2 OR ...` +- `%a` (assoc) - `key1 = value1, key2 = value2, ...` (for UPDATE SET) +- `%l` or `%in` (list) - `(val1, val2, ...)` (for IN clauses) +- `%v` (values) - `(key1, key2, ...) VALUES (value1, value2, ...)` (for INSERT) +- `%m` (multi) - multiple value rows for batch INSERT +- `%by` (ordering) - `key1 ASC, key2 DESC ...` (for ORDER BY) +- `%n` (names) - `key1, key2 AS alias, ...` (for SELECT columns) + +Example usage: +```php +// UPDATE with %a +$database->query('UPDATE users SET %a', ['name' => 'Jim', 'year' => 1978]); + +// WHERE with %and +$database->query('SELECT * FROM users WHERE %and', ['name' => 'Jim', 'active' => true]); + +// INSERT with %v +$database->query('INSERT INTO users %v', ['name' => 'Jim', 'year' => 1978]); + +// Multiple INSERT with %m +$database->query('INSERT INTO users %m', [ + ['name' => 'Jim', 'year' => 1978], + ['name' => 'Jack', 'year' => 1987], +]); +``` + +### Advanced Query Features + +**Modifiers in array keys** (for UPDATE): +```php +$database->query('UPDATE table SET', [ + 'date%SQL' => 'NOW()', + 'title' => 'normal value', +]); +// UPDATE table SET `date` = NOW(), `title` = 'normal value' +``` + +**Literal SQL** (bypass escaping): +```php +$database->literal('NOW()'); // returns Dibi\Literal object +``` + +**Expressions** (with parameters): +```php +$database::expression('SHA1(?)', 'secret'); // returns Dibi\Expression object +``` + +**Conditional SQL** (`%if`, `%else`, `%end`): +```php +$database->query(' + SELECT * + FROM table + %if', isset($user), 'WHERE user=%s', $user, '%end + ORDER BY name +'); +``` + +**Nested conditions in WHERE** (arrays without keys): +```php +$database->query('SELECT * FROM table WHERE %and', [ + 'number > 10', + 'number < 100', + ['%or', ['left' => 1, 'top' => 2]], +]); +// WHERE (number > 10) AND (number < 100) AND (`left` = 1 OR `top` = 2) +``` + +**Table/column substitutions** (prefixes): +```php +$database->substitute('blog', 'wp_'); +$database->query("UPDATE [:blog:items] SET [text]='Hello'"); +// UPDATE `wp_items` SET `text`='Hello' +``` + +### Result Fetching Methods + +**Advanced fetchAssoc()** - Reshapes flat JOIN results into nested arrays: + +```php +// Simple associative array by key +$result->fetchAssoc('id'); // returns [$id => $row, ...] + +// Two-level nesting +$result->fetchAssoc('customer_id|order_id'); +// returns [$customerId => [$orderId => $row, ...], ...] + +// With intermediate row object +$result->fetchAssoc('customer_id->order_id'); +// returns [$customerId => $row with ->order_id[$orderId], ...] + +// With sequential array for duplicate keys +$result->fetchAssoc('name[]order_id'); +// returns [$name => [[$orderId => $row], [$orderId2 => $row2]], ...] +``` + +The descriptor syntax: +- `|` - creates new array level (associative by key) +- `->` - inserts row object as intermediate element +- `[]` - creates sequential array for duplicate keys + +### Transaction Methods + +Four methods for transaction handling: +```php +$database->beginTransaction(); +$database->commit(); +$database->rollback(); + +// Or using callback (auto-commits on success, auto-rolls back on exception) +$database->transaction(function () use ($database) { + $database->query('...'); + $database->query('...'); +}); +``` + +### Debugging and Testing + +**Query inspection** without execution: +```php +$database->test('SELECT * FROM users WHERE id = ?', $id); +// Echoes the SQL that would be executed +``` + +**Result dumping**: +```php +$result->dump(); // Outputs result as HTML table +``` + +**Query statistics** (available via static facade): +```php +dibi::$sql; // Latest SQL query executed +dibi::$elapsedTime; // Duration in seconds +dibi::$numOfQueries; // Total number of queries +dibi::$totalTime; // Total execution time +``` + +**Logging configuration**: +```php +$database = new Dibi\Connection([ + 'driver' => 'mysqli', + // ... connection params + 'profiler' => [ + 'file' => 'queries.log', + ], +]); +``` + +### Type Handling + +**Manual type specification**: +```php +$result->setType('id', Dibi\Type::INTEGER); +$result->setType('price', Dibi\Type::FLOAT); +$row = $result->fetch(); +// Now $row->id is guaranteed to be int +``` + +**DateTime support**: +```php +$database->query('INSERT INTO users', [ + 'created' => new DateTime, + 'expires' => new DateTime('+1 year'), +]); +``` + +### Identifier Quoting + +Dibi automatically converts bracket/backtick notation to database-specific format: +```php +$database->query("UPDATE `table` SET [status]='value'"); +// MySQL: UPDATE `table` SET `status`='value' +// ODBC: UPDATE [table] SET [status]='value' +``` + +## Important Notes + +- **Zero production dependencies** - Core library is completely standalone +- **Lazy connections** - Database connections are established only when first query is executed +- **Event system** - Use `Connection::$onEvent` for query profiling and logging +- **Multi-database testing** - Tests run against MySQL, PostgreSQL, and SQLite configurations +- **PHPStan Level 5** - All code must pass static analysis at level 5 +- **SQL injection protection** - NEVER concatenate user input into SQL, always use placeholders or modifiers From 89974b4ebe234a21fbd73442f02c3bd0e8612c1b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 15:57:10 +0200 Subject: [PATCH 08/15] opened 6.0-dev --- composer.json | 2 +- readme.md | 2 +- src/Dibi/dibi.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 0e7f49c79..8216a4e5d 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ }, "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "6.0-dev" } } } diff --git a/readme.md b/readme.md index 75b330b6a..4319c7b89 100644 --- a/readme.md +++ b/readme.md @@ -34,7 +34,7 @@ Install Dibi via Composer: composer require dibi/dibi ``` -The Dibi 5.1 requires PHP version 8.2 and supports PHP up to 8.5. +The Dibi 6.0 requires PHP version 8.2 and supports PHP up to 8.5. Usage diff --git a/src/Dibi/dibi.php b/src/Dibi/dibi.php index a7c0517ad..c9c95c4ed 100644 --- a/src/Dibi/dibi.php +++ b/src/Dibi/dibi.php @@ -37,7 +37,7 @@ */ class dibi { - public const Version = '5.1-dev'; + public const Version = '6.0-dev'; /** @deprecated use dibi::Version */ public const VERSION = self::Version; From c175932d1904ae21c2eecffde5c598adc0fba7ee Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 15:58:35 +0200 Subject: [PATCH 09/15] removed IConnection (BC break) --- src/Dibi/Connection.php | 2 +- src/Dibi/interfaces.php | 60 ----------------------------------------- 2 files changed, 1 insertion(+), 61 deletions(-) diff --git a/src/Dibi/Connection.php b/src/Dibi/Connection.php index c66a78ae5..8d778d9f1 100644 --- a/src/Dibi/Connection.php +++ b/src/Dibi/Connection.php @@ -21,7 +21,7 @@ * @property-read int $affectedRows * @property-read int $insertId */ -class Connection implements IConnection +class Connection { /** function (Event $event); Occurs after query is executed */ public ?array $onEvent = []; diff --git a/src/Dibi/interfaces.php b/src/Dibi/interfaces.php index fbf34d5d1..e9604a9eb 100644 --- a/src/Dibi/interfaces.php +++ b/src/Dibi/interfaces.php @@ -178,63 +178,3 @@ function getIndexes(string $table): array; */ function getForeignKeys(string $table): array; } - - -/** - * Dibi connection. - */ -interface IConnection -{ - /** - * Connects to a database. - */ - function connect(): void; - - /** - * Disconnects from a database. - */ - function disconnect(): void; - - /** - * Returns true when connection was established. - */ - function isConnected(): bool; - - /** - * Returns the driver and connects to a database in lazy mode. - */ - function getDriver(): Driver; - - /** - * Generates (translates) and executes SQL query. - * @throws Exception - */ - function query(...$args): Result; - - /** - * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. - * @throws Exception - */ - function getAffectedRows(): int; - - /** - * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. - * @throws Exception - */ - function getInsertId(?string $sequence = null): int; - - /** - * Begins a transaction (if supported). - */ - function begin(?string $savepoint = null): void; - - /** - * Commits statements in a transaction. - */ - function commit(?string $savepoint = null): void; - - /** - * Rollback changes in a transaction. - */ - function rollback(?string $savepoint = null): void; -} From 3e3d444cba8f856c958b87bd40bc47a10d93beb7 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 18:20:04 +0200 Subject: [PATCH 10/15] renamed interfaces (BC break) Dibi\Driver => Dibi\Drivers\Connection Dibi\ResultDriver => Dibi\Drivers\Result Dibi\Reflector => Dibi\Drivers\Engine --- src/Dibi/Connection.php | 10 +- src/Dibi/Drivers/Connection.php | 97 ++++++++++++ src/Dibi/Drivers/DummyDriver.php | 6 +- src/Dibi/Drivers/Engine.php | 40 +++++ src/Dibi/Drivers/FirebirdDriver.php | 6 +- src/Dibi/Drivers/FirebirdReflector.php | 5 +- src/Dibi/Drivers/FirebirdResult.php | 2 +- src/Dibi/Drivers/MySqlReflector.php | 4 +- src/Dibi/Drivers/MySqliDriver.php | 6 +- src/Dibi/Drivers/MySqliResult.php | 2 +- src/Dibi/Drivers/NoDataResult.php | 3 +- src/Dibi/Drivers/OdbcDriver.php | 6 +- src/Dibi/Drivers/OdbcReflector.php | 4 +- src/Dibi/Drivers/OdbcResult.php | 2 +- src/Dibi/Drivers/OracleDriver.php | 6 +- src/Dibi/Drivers/OracleReflector.php | 4 +- src/Dibi/Drivers/OracleResult.php | 2 +- src/Dibi/Drivers/PdoDriver.php | 6 +- src/Dibi/Drivers/PdoResult.php | 2 +- src/Dibi/Drivers/PostgreDriver.php | 6 +- src/Dibi/Drivers/PostgreReflector.php | 5 +- src/Dibi/Drivers/PostgreResult.php | 3 +- src/Dibi/Drivers/Result.php | 58 ++++++++ src/Dibi/Drivers/SqliteDriver.php | 6 +- src/Dibi/Drivers/SqliteReflector.php | 5 +- src/Dibi/Drivers/SqliteResult.php | 2 +- src/Dibi/Drivers/SqlsrvDriver.php | 6 +- src/Dibi/Drivers/SqlsrvReflector.php | 4 +- src/Dibi/Drivers/SqlsrvResult.php | 2 +- src/Dibi/Helpers.php | 2 +- src/Dibi/IDataSource.php | 20 +++ src/Dibi/Reflection/Column.php | 2 +- src/Dibi/Reflection/Database.php | 2 +- src/Dibi/Reflection/Result.php | 4 +- src/Dibi/Reflection/Table.php | 4 +- src/Dibi/Result.php | 6 +- src/Dibi/Translator.php | 4 +- src/Dibi/interfaces.php | 180 ----------------------- tests/dibi/Connection.connect.phpt | 2 +- tests/dibi/Fluent.fetch.limit.mssql.phpt | 2 +- 40 files changed, 284 insertions(+), 254 deletions(-) create mode 100644 src/Dibi/Drivers/Connection.php create mode 100644 src/Dibi/Drivers/Engine.php create mode 100644 src/Dibi/Drivers/Result.php create mode 100644 src/Dibi/IDataSource.php delete mode 100644 src/Dibi/interfaces.php diff --git a/src/Dibi/Connection.php b/src/Dibi/Connection.php index 8d778d9f1..a9c0566ae 100644 --- a/src/Dibi/Connection.php +++ b/src/Dibi/Connection.php @@ -29,7 +29,7 @@ class Connection /** @var string[] resultset formats */ private array $formats; - private ?Driver $driver = null; + private ?Drivers\Connection $driver = null; private ?Translator $translator = null; /** @var array */ @@ -122,12 +122,12 @@ public function __destruct() */ final public function connect(): void { - if ($this->config['driver'] instanceof Driver) { + if ($this->config['driver'] instanceof Drivers\Connection) { $this->driver = $this->config['driver']; $this->translator = new Translator($this); return; - } elseif (is_subclass_of($this->config['driver'], Driver::class)) { + } elseif (is_subclass_of($this->config['driver'], Drivers\Connection::class)) { $class = $this->config['driver']; } else { @@ -198,7 +198,7 @@ final public function getConfig(?string $key = null, $default = null): mixed /** * Returns the driver and connects to a database in lazy mode. */ - final public function getDriver(): Driver + final public function getDriver(): Drivers\Connection { if (!$this->driver) { $this->connect(); @@ -450,7 +450,7 @@ public function transaction(callable $callback): mixed /** * Result set factory. */ - public function createResultSet(ResultDriver $resultDriver): Result + public function createResultSet(Drivers\Result $resultDriver): Result { return (new Result($resultDriver, $this->config['result']['normalize'] ?? true)) ->setFormats($this->formats); diff --git a/src/Dibi/Drivers/Connection.php b/src/Dibi/Drivers/Connection.php new file mode 100644 index 000000000..18f967db2 --- /dev/null +++ b/src/Dibi/Drivers/Connection.php @@ -0,0 +1,97 @@ + buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default. * - resource (resource) => existing connection resource */ -class FirebirdDriver implements Dibi\Driver +class FirebirdDriver implements Connection { public const ErrorExceptionThrown = -836; @@ -86,7 +86,7 @@ public function disconnect(): void * Executes the SQL query. * @throws Dibi\DriverException|Dibi\Exception */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $resource = $this->inTransaction ? $this->transaction @@ -200,7 +200,7 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return new FirebirdReflector($this); } diff --git a/src/Dibi/Drivers/FirebirdReflector.php b/src/Dibi/Drivers/FirebirdReflector.php index 032a8e42f..6a8843953 100644 --- a/src/Dibi/Drivers/FirebirdReflector.php +++ b/src/Dibi/Drivers/FirebirdReflector.php @@ -9,16 +9,15 @@ namespace Dibi\Drivers; -use Dibi; /** * The reflector for Firebird/InterBase database. */ -class FirebirdReflector implements Dibi\Reflector +class FirebirdReflector implements Engine { public function __construct( - private readonly Dibi\Driver $driver, + private readonly Connection $driver, ) { } diff --git a/src/Dibi/Drivers/FirebirdResult.php b/src/Dibi/Drivers/FirebirdResult.php index fba784353..d46af1427 100644 --- a/src/Dibi/Drivers/FirebirdResult.php +++ b/src/Dibi/Drivers/FirebirdResult.php @@ -17,7 +17,7 @@ /** * The driver for Firebird/InterBase result set. */ -class FirebirdResult implements Dibi\ResultDriver +class FirebirdResult implements Result { public function __construct( /** @var resource */ diff --git a/src/Dibi/Drivers/MySqlReflector.php b/src/Dibi/Drivers/MySqlReflector.php index e782e63a3..9c0bed777 100644 --- a/src/Dibi/Drivers/MySqlReflector.php +++ b/src/Dibi/Drivers/MySqlReflector.php @@ -16,10 +16,10 @@ * The reflector for MySQL databases. * @internal */ -class MySqlReflector implements Dibi\Reflector +class MySqlReflector implements Engine { public function __construct( - private readonly Dibi\Driver $driver, + private readonly Connection $driver, ) { } diff --git a/src/Dibi/Drivers/MySqliDriver.php b/src/Dibi/Drivers/MySqliDriver.php index 8128474ee..9ba993703 100644 --- a/src/Dibi/Drivers/MySqliDriver.php +++ b/src/Dibi/Drivers/MySqliDriver.php @@ -32,7 +32,7 @@ * - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html * - resource (mysqli) => existing connection resource */ -class MySqliDriver implements Dibi\Driver +class MySqliDriver implements Connection { public const ErrorAccessDenied = 1045; public const ErrorDuplicateEntry = 1062; @@ -148,7 +148,7 @@ public function ping(): bool * Executes the SQL query. * @throws Dibi\DriverException */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $res = @$this->connection->query($sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @ @@ -265,7 +265,7 @@ public function getResource(): ?\mysqli /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return new MySqlReflector($this); } diff --git a/src/Dibi/Drivers/MySqliResult.php b/src/Dibi/Drivers/MySqliResult.php index 6c79a3886..58f8b5472 100644 --- a/src/Dibi/Drivers/MySqliResult.php +++ b/src/Dibi/Drivers/MySqliResult.php @@ -16,7 +16,7 @@ /** * The driver for MySQL result set. */ -class MySqliResult implements Dibi\ResultDriver +class MySqliResult implements Result { public function __construct( private readonly \mysqli_result $resultSet, diff --git a/src/Dibi/Drivers/NoDataResult.php b/src/Dibi/Drivers/NoDataResult.php index c9ca3832e..1e070f7fc 100644 --- a/src/Dibi/Drivers/NoDataResult.php +++ b/src/Dibi/Drivers/NoDataResult.php @@ -9,13 +9,12 @@ namespace Dibi\Drivers; -use Dibi; /** * The driver for no result set. */ -class NoDataResult implements Dibi\ResultDriver +class NoDataResult implements Result { public function __construct( private readonly int $rows, diff --git a/src/Dibi/Drivers/OdbcDriver.php b/src/Dibi/Drivers/OdbcDriver.php index 8d87897c1..2adaf669f 100644 --- a/src/Dibi/Drivers/OdbcDriver.php +++ b/src/Dibi/Drivers/OdbcDriver.php @@ -24,7 +24,7 @@ * - resource (resource) => existing connection resource * - microseconds (bool) => use microseconds in datetime format? */ -class OdbcDriver implements Dibi\Driver +class OdbcDriver implements Connection { /** @var resource */ private $connection; @@ -77,7 +77,7 @@ public function disconnect(): void * Executes the SQL query. * @throws Dibi\DriverException */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $this->affectedRows = null; $res = @odbc_exec($this->connection, $sql); // intentionally @ @@ -176,7 +176,7 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return new OdbcReflector($this); } diff --git a/src/Dibi/Drivers/OdbcReflector.php b/src/Dibi/Drivers/OdbcReflector.php index f259d4475..68a243433 100644 --- a/src/Dibi/Drivers/OdbcReflector.php +++ b/src/Dibi/Drivers/OdbcReflector.php @@ -15,10 +15,10 @@ /** * The reflector for ODBC connections. */ -class OdbcReflector implements Dibi\Reflector +class OdbcReflector implements Engine { public function __construct( - private readonly Dibi\Driver $driver, + private readonly Connection $driver, ) { } diff --git a/src/Dibi/Drivers/OdbcResult.php b/src/Dibi/Drivers/OdbcResult.php index e86d28714..1271eba76 100644 --- a/src/Dibi/Drivers/OdbcResult.php +++ b/src/Dibi/Drivers/OdbcResult.php @@ -16,7 +16,7 @@ /** * The driver interacting with result set via ODBC connections. */ -class OdbcResult implements Dibi\ResultDriver +class OdbcResult implements Result { private int $row = 0; diff --git a/src/Dibi/Drivers/OracleDriver.php b/src/Dibi/Drivers/OracleDriver.php index 992cbcb05..43475ace1 100644 --- a/src/Dibi/Drivers/OracleDriver.php +++ b/src/Dibi/Drivers/OracleDriver.php @@ -26,7 +26,7 @@ * - resource (resource) => existing connection resource * - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect */ -class OracleDriver implements Dibi\Driver +class OracleDriver implements Connection { /** @var resource */ private $connection; @@ -77,7 +77,7 @@ public function disconnect(): void * Executes the SQL query. * @throws Dibi\DriverException */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $this->affectedRows = null; $res = oci_parse($this->connection, $sql); @@ -190,7 +190,7 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return new OracleReflector($this); } diff --git a/src/Dibi/Drivers/OracleReflector.php b/src/Dibi/Drivers/OracleReflector.php index 2307063dc..bb6e52972 100644 --- a/src/Dibi/Drivers/OracleReflector.php +++ b/src/Dibi/Drivers/OracleReflector.php @@ -15,10 +15,10 @@ /** * The reflector for Oracle database. */ -class OracleReflector implements Dibi\Reflector +class OracleReflector implements Engine { public function __construct( - private readonly Dibi\Driver $driver, + private readonly Connection $driver, ) { } diff --git a/src/Dibi/Drivers/OracleResult.php b/src/Dibi/Drivers/OracleResult.php index e601f5a13..1f826f403 100644 --- a/src/Dibi/Drivers/OracleResult.php +++ b/src/Dibi/Drivers/OracleResult.php @@ -16,7 +16,7 @@ /** * The driver for Oracle result set. */ -class OracleResult implements Dibi\ResultDriver +class OracleResult implements Result { public function __construct( /** @var resource */ diff --git a/src/Dibi/Drivers/PdoDriver.php b/src/Dibi/Drivers/PdoDriver.php index a6abb8f3f..5ae38d49e 100644 --- a/src/Dibi/Drivers/PdoDriver.php +++ b/src/Dibi/Drivers/PdoDriver.php @@ -25,7 +25,7 @@ * - options (array) => driver specific options {@see PDO::__construct} * - resource (PDO) => existing connection */ -class PdoDriver implements Dibi\Driver +class PdoDriver implements Connection { private ?PDO $connection; private ?int $affectedRows; @@ -80,7 +80,7 @@ public function disconnect(): void * Executes the SQL query. * @throws Dibi\DriverException */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $res = $this->connection->query($sql); if ($res) { @@ -172,7 +172,7 @@ public function getResource(): ?PDO /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return match ($this->driverName) { 'mysql' => new MySqlReflector($this), diff --git a/src/Dibi/Drivers/PdoResult.php b/src/Dibi/Drivers/PdoResult.php index a79415036..886f116b7 100644 --- a/src/Dibi/Drivers/PdoResult.php +++ b/src/Dibi/Drivers/PdoResult.php @@ -17,7 +17,7 @@ /** * The driver for PDO result set. */ -class PdoResult implements Dibi\ResultDriver +class PdoResult implements Result { public function __construct( private ?\PDOStatement $resultSet, diff --git a/src/Dibi/Drivers/PostgreDriver.php b/src/Dibi/Drivers/PostgreDriver.php index a4d50ea72..8e90d15f8 100644 --- a/src/Dibi/Drivers/PostgreDriver.php +++ b/src/Dibi/Drivers/PostgreDriver.php @@ -27,7 +27,7 @@ * - resource (PgSql\Connection) => existing connection resource * - connect_type (int) => see pg_connect() */ -class PostgreDriver implements Dibi\Driver +class PostgreDriver implements Connection { private PgSql\Connection $connection; private ?int $affectedRows; @@ -110,7 +110,7 @@ public function ping(): bool * Executes the SQL query. * @throws Dibi\DriverException */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $this->affectedRows = null; $res = @pg_query($this->connection, $sql); // intentionally @ @@ -232,7 +232,7 @@ public function getResource(): PgSql\Connection /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return new PostgreReflector($this); } diff --git a/src/Dibi/Drivers/PostgreReflector.php b/src/Dibi/Drivers/PostgreReflector.php index ca4854fef..2fb27148a 100644 --- a/src/Dibi/Drivers/PostgreReflector.php +++ b/src/Dibi/Drivers/PostgreReflector.php @@ -9,16 +9,15 @@ namespace Dibi\Drivers; -use Dibi; /** * The reflector for PostgreSQL database. */ -class PostgreReflector implements Dibi\Reflector +class PostgreReflector implements Engine { public function __construct( - private readonly Dibi\Driver $driver, + private readonly Connection $driver, ) { } diff --git a/src/Dibi/Drivers/PostgreResult.php b/src/Dibi/Drivers/PostgreResult.php index 763dabafa..96b9d750b 100644 --- a/src/Dibi/Drivers/PostgreResult.php +++ b/src/Dibi/Drivers/PostgreResult.php @@ -9,7 +9,6 @@ namespace Dibi\Drivers; -use Dibi; use Dibi\Helpers; use PgSql; @@ -17,7 +16,7 @@ /** * The driver for PostgreSQL result set. */ -class PostgreResult implements Dibi\ResultDriver +class PostgreResult implements Result { public function __construct( private readonly PgSql\Result $resultSet, diff --git a/src/Dibi/Drivers/Result.php b/src/Dibi/Drivers/Result.php new file mode 100644 index 000000000..23372cdb7 --- /dev/null +++ b/src/Dibi/Drivers/Result.php @@ -0,0 +1,58 @@ + how to format datetime in SQL (@see date) * - resource (SQLite3) => existing connection resource */ -class SqliteDriver implements Dibi\Driver +class SqliteDriver implements Connection { private SQLite3 $connection; private string $fmtDate; @@ -73,7 +73,7 @@ public function disconnect(): void * Executes the SQL query. * @throws Dibi\DriverException */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $res = @$this->connection->query($sql); // intentionally @ if ($code = $this->connection->lastErrorCode()) { @@ -174,7 +174,7 @@ public function getResource(): ?SQLite3 /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return new SqliteReflector($this); } diff --git a/src/Dibi/Drivers/SqliteReflector.php b/src/Dibi/Drivers/SqliteReflector.php index 1d5309977..d828e85c3 100644 --- a/src/Dibi/Drivers/SqliteReflector.php +++ b/src/Dibi/Drivers/SqliteReflector.php @@ -9,16 +9,15 @@ namespace Dibi\Drivers; -use Dibi; /** * The reflector for SQLite database. */ -class SqliteReflector implements Dibi\Reflector +class SqliteReflector implements Engine { public function __construct( - private readonly Dibi\Driver $driver, + private readonly Connection $driver, ) { } diff --git a/src/Dibi/Drivers/SqliteResult.php b/src/Dibi/Drivers/SqliteResult.php index 08d389048..04df4a536 100644 --- a/src/Dibi/Drivers/SqliteResult.php +++ b/src/Dibi/Drivers/SqliteResult.php @@ -17,7 +17,7 @@ /** * The driver for SQLite result set. */ -class SqliteResult implements Dibi\ResultDriver +class SqliteResult implements Result { public function __construct( private readonly \SQLite3Result $resultSet, diff --git a/src/Dibi/Drivers/SqlsrvDriver.php b/src/Dibi/Drivers/SqlsrvDriver.php index 0e39fd5fe..383a9eb0b 100644 --- a/src/Dibi/Drivers/SqlsrvDriver.php +++ b/src/Dibi/Drivers/SqlsrvDriver.php @@ -26,7 +26,7 @@ * - charset => character encoding to set (default is UTF-8) * - resource (resource) => existing connection resource */ -class SqlsrvDriver implements Dibi\Driver +class SqlsrvDriver implements Connection { /** @var resource */ private $connection; @@ -84,7 +84,7 @@ public function disconnect(): void * Executes the SQL query. * @throws Dibi\DriverException */ - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Result { $this->affectedRows = null; $res = sqlsrv_query($this->connection, $sql); @@ -171,7 +171,7 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Dibi\Reflector + public function getReflector(): Engine { return new SqlsrvReflector($this); } diff --git a/src/Dibi/Drivers/SqlsrvReflector.php b/src/Dibi/Drivers/SqlsrvReflector.php index bbf0418dd..47109676a 100644 --- a/src/Dibi/Drivers/SqlsrvReflector.php +++ b/src/Dibi/Drivers/SqlsrvReflector.php @@ -16,10 +16,10 @@ /** * The reflector for Microsoft SQL Server and SQL Azure databases. */ -class SqlsrvReflector implements Dibi\Reflector +class SqlsrvReflector implements Engine { public function __construct( - private readonly Dibi\Driver $driver, + private readonly Connection $driver, ) { } diff --git a/src/Dibi/Drivers/SqlsrvResult.php b/src/Dibi/Drivers/SqlsrvResult.php index 94a76179f..2180d1bb2 100644 --- a/src/Dibi/Drivers/SqlsrvResult.php +++ b/src/Dibi/Drivers/SqlsrvResult.php @@ -16,7 +16,7 @@ /** * The driver for Microsoft SQL Server and SQL Azure result set. */ -class SqlsrvResult implements Dibi\ResultDriver +class SqlsrvResult implements Result { public function __construct( /** @var resource */ diff --git a/src/Dibi/Helpers.php b/src/Dibi/Helpers.php index 17a90a1f6..dbcefae25 100644 --- a/src/Dibi/Helpers.php +++ b/src/Dibi/Helpers.php @@ -159,7 +159,7 @@ public static function getSuggestion(array $items, string $value): ?string /** @internal */ - public static function escape(Driver $driver, $value, string $type): string + public static function escape(Drivers\Connection $driver, $value, string $type): string { $types = [ Type::Text => 'text', diff --git a/src/Dibi/IDataSource.php b/src/Dibi/IDataSource.php new file mode 100644 index 000000000..9b41dc875 --- /dev/null +++ b/src/Dibi/IDataSource.php @@ -0,0 +1,20 @@ +columns)) { $this->columns = []; - $reflector = $this->driver instanceof Dibi\Reflector + $reflector = $this->driver instanceof Dibi\Drivers\Engine ? $this->driver : null; foreach ($this->driver->getResultColumns() as $info) { diff --git a/src/Dibi/Reflection/Table.php b/src/Dibi/Reflection/Table.php index 53e281d21..5e1829fc6 100644 --- a/src/Dibi/Reflection/Table.php +++ b/src/Dibi/Reflection/Table.php @@ -26,7 +26,7 @@ */ class Table { - private readonly Dibi\Reflector $reflector; + private readonly Dibi\Drivers\Engine $reflector; private string $name; private bool $view; @@ -41,7 +41,7 @@ class Table private ?Index $primaryKey; - public function __construct(Dibi\Reflector $reflector, array $info) + public function __construct(Dibi\Drivers\Engine $reflector, array $info) { $this->reflector = $reflector; $this->name = $info['name']; diff --git a/src/Dibi/Result.php b/src/Dibi/Result.php index 05c340ddd..b168147eb 100644 --- a/src/Dibi/Result.php +++ b/src/Dibi/Result.php @@ -20,7 +20,7 @@ */ class Result implements IDataSource { - private ?ResultDriver $driver; + private ?Drivers\Result $driver; /** Translate table */ private array $types = []; @@ -37,7 +37,7 @@ class Result implements IDataSource private array $formats = []; - public function __construct(ResultDriver $driver, bool $normalize = true) + public function __construct(Drivers\Result $driver, bool $normalize = true) { $this->driver = $driver; if ($normalize) { @@ -62,7 +62,7 @@ final public function free(): void * Safe access to property $driver. * @throws \RuntimeException */ - final public function getResultDriver(): ResultDriver + final public function getResultDriver(): Drivers\Result { if ($this->driver === null) { throw new \RuntimeException('Result-set was released from memory.'); diff --git a/src/Dibi/Translator.php b/src/Dibi/Translator.php index 0b5dfb6f4..faa6ebffb 100644 --- a/src/Dibi/Translator.php +++ b/src/Dibi/Translator.php @@ -17,8 +17,8 @@ */ final class Translator { - private readonly Connection $connection; - private readonly Driver $driver; + private Connection $connection; + private readonly Drivers\Connection $driver; private int $cursor = 0; private array $args; diff --git a/src/Dibi/interfaces.php b/src/Dibi/interfaces.php deleted file mode 100644 index e9604a9eb..000000000 --- a/src/Dibi/interfaces.php +++ /dev/null @@ -1,180 +0,0 @@ -getConfig('lazy')); Assert::same($config['driver'], $conn->getConfig('driver')); - Assert::type(Dibi\Driver::class, $conn->getDriver()); + Assert::type(Dibi\Drivers\Connection::class, $conn->getDriver()); }); diff --git a/tests/dibi/Fluent.fetch.limit.mssql.phpt b/tests/dibi/Fluent.fetch.limit.mssql.phpt index d07ce4e87..cc3580420 100644 --- a/tests/dibi/Fluent.fetch.limit.mssql.phpt +++ b/tests/dibi/Fluent.fetch.limit.mssql.phpt @@ -19,7 +19,7 @@ class MockDriver extends Dibi\Drivers\SqlsrvDriver } - public function query(string $sql): ?Dibi\ResultDriver + public function query(string $sql): ?Dibi\Drivers\Result { return new MockResult; } From 66fb7394caf168d919c487e0f876a17a5a23b543 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 16:39:56 +0200 Subject: [PATCH 11/15] renamed driver classes --- src/Dibi/Connection.php | 17 +++++++++-- .../{DummyDriver.php => Dummy/Connection.php} | 7 +++-- .../{NoDataResult.php => Dummy/Result.php} | 5 ++-- .../FirebirdEngine.php} | 6 ++-- .../MySQLEngine.php} | 6 ++-- .../ODBCEngine.php} | 6 ++-- .../OracleEngine.php} | 6 ++-- .../PostgreSQLEngine.php} | 6 ++-- .../SQLServerEngine.php} | 7 +++-- .../SQLiteEngine.php} | 6 ++-- .../Connection.php} | 13 ++++---- .../Result.php} | 7 +++-- .../Connection.php} | 13 ++++---- .../{MySqliResult.php => MySQLi/Result.php} | 5 ++-- .../{OracleDriver.php => OCI8/Connection.php} | 13 ++++---- .../{OracleResult.php => OCI8/Result.php} | 5 ++-- .../{OdbcDriver.php => ODBC/Connection.php} | 13 ++++---- .../{OdbcResult.php => ODBC/Result.php} | 5 ++-- .../{PdoDriver.php => PDO/Connection.php} | 30 ++++++++++--------- .../Drivers/{PdoResult.php => PDO/Result.php} | 5 ++-- .../Connection.php} | 13 ++++---- .../{PostgreResult.php => PgSQL/Result.php} | 5 ++-- .../Connection.php} | 13 ++++---- .../{SqlsrvResult.php => SQLSrv/Result.php} | 5 ++-- .../Connection.php} | 13 ++++---- .../{SqliteResult.php => SQLite3/Result.php} | 5 ++-- src/Dibi/Drivers/Sqlite3Driver.php | 18 ----------- src/Dibi/Drivers/Sqlite3Result.php | 18 ----------- tests/dibi/Fluent.fetch.limit.mssql.phpt | 8 +++-- tests/dibi/PdoDriver.providedConnection.phpt | 2 +- 30 files changed, 145 insertions(+), 136 deletions(-) rename src/Dibi/Drivers/{DummyDriver.php => Dummy/Connection.php} (94%) rename src/Dibi/Drivers/{NoDataResult.php => Dummy/Result.php} (90%) rename src/Dibi/Drivers/{FirebirdReflector.php => Engines/FirebirdEngine.php} (98%) rename src/Dibi/Drivers/{MySqlReflector.php => Engines/MySQLEngine.php} (96%) rename src/Dibi/Drivers/{OdbcReflector.php => Engines/ODBCEngine.php} (93%) rename src/Dibi/Drivers/{OracleReflector.php => Engines/OracleEngine.php} (92%) rename src/Dibi/Drivers/{PostgreReflector.php => Engines/PostgreSQLEngine.php} (98%) rename src/Dibi/Drivers/{SqlsrvReflector.php => Engines/SQLServerEngine.php} (96%) rename src/Dibi/Drivers/{SqliteReflector.php => Engines/SQLiteEngine.php} (96%) rename src/Dibi/Drivers/{FirebirdDriver.php => Firebird/Connection.php} (95%) rename src/Dibi/Drivers/{FirebirdResult.php => Firebird/Result.php} (94%) rename src/Dibi/Drivers/{MySqliDriver.php => MySQLi/Connection.php} (96%) rename src/Dibi/Drivers/{MySqliResult.php => MySQLi/Result.php} (96%) rename src/Dibi/Drivers/{OracleDriver.php => OCI8/Connection.php} (96%) rename src/Dibi/Drivers/{OracleResult.php => OCI8/Result.php} (96%) rename src/Dibi/Drivers/{OdbcDriver.php => ODBC/Connection.php} (95%) rename src/Dibi/Drivers/{OdbcResult.php => ODBC/Result.php} (96%) rename src/Dibi/Drivers/{PdoDriver.php => PDO/Connection.php} (91%) rename src/Dibi/Drivers/{PdoResult.php => PDO/Result.php} (96%) rename src/Dibi/Drivers/{PostgreDriver.php => PgSQL/Connection.php} (96%) rename src/Dibi/Drivers/{PostgreResult.php => PgSQL/Result.php} (95%) rename src/Dibi/Drivers/{SqlsrvDriver.php => SQLSrv/Connection.php} (95%) rename src/Dibi/Drivers/{SqlsrvResult.php => SQLSrv/Result.php} (95%) rename src/Dibi/Drivers/{SqliteDriver.php => SQLite3/Connection.php} (95%) rename src/Dibi/Drivers/{SqliteResult.php => SQLite3/Result.php} (96%) delete mode 100644 src/Dibi/Drivers/Sqlite3Driver.php delete mode 100644 src/Dibi/Drivers/Sqlite3Result.php diff --git a/src/Dibi/Connection.php b/src/Dibi/Connection.php index a9c0566ae..ad6457dbb 100644 --- a/src/Dibi/Connection.php +++ b/src/Dibi/Connection.php @@ -23,6 +23,18 @@ */ class Connection { + private const Drivers = [ + 'firebird' => Drivers\Ibase\Connection::class, + 'mysqli' => Drivers\MySQLi\Connection::class, + 'odbc' => Drivers\ODBC\Connection::class, + 'oracle' => Drivers\OCI8\Connection::class, + 'pdo' => Drivers\PDO\Connection::class, + 'postgre' => Drivers\PgSQL\Connection::class, + 'sqlite3' => Drivers\SQLite3\Connection::class, + 'sqlite' => Drivers\SQLite3\Connection::class, + 'sqlsrv' => Drivers\SQLSrv\Connection::class, + ]; + /** function (Event $event); Occurs after query is executed */ public ?array $onEvent = []; private array $config; @@ -131,8 +143,7 @@ final public function connect(): void $class = $this->config['driver']; } else { - $class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($this->config['driver']))); - $class = "Dibi\\Drivers\\{$class}Driver"; + $class = self::Drivers[strtolower($this->config['driver'])] ?? throw new Exception("Unknown driver '{$this->config['driver']}'."); if (!class_exists($class)) { throw new Exception("Unable to create instance of Dibi driver '$class'."); } @@ -286,7 +297,7 @@ final public function nativeQuery(#[Language('SQL')] string $sql): Result throw $e; } - $res = $this->createResultSet($res ?? new Drivers\NoDataResult(max(0, $this->driver->getAffectedRows()))); + $res = $this->createResultSet($res ?? new Drivers\Dummy\Result(max(0, $this->driver->getAffectedRows()))); if ($event) { $this->onEvent($event->done($res)); } diff --git a/src/Dibi/Drivers/DummyDriver.php b/src/Dibi/Drivers/Dummy/Connection.php similarity index 94% rename from src/Dibi/Drivers/DummyDriver.php rename to src/Dibi/Drivers/Dummy/Connection.php index 4119f86c8..894db0755 100644 --- a/src/Dibi/Drivers/DummyDriver.php +++ b/src/Dibi/Drivers/Dummy/Connection.php @@ -7,15 +7,16 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Dummy; use Dibi; +use Dibi\Drivers; /** * The dummy driver for testing purposes. */ -class DummyDriver implements Connection, Result, Engine +class Connection implements Drivers\Connection, Drivers\Result, Drivers\Engine { public function disconnect(): void { @@ -64,7 +65,7 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { return $this; } diff --git a/src/Dibi/Drivers/NoDataResult.php b/src/Dibi/Drivers/Dummy/Result.php similarity index 90% rename from src/Dibi/Drivers/NoDataResult.php rename to src/Dibi/Drivers/Dummy/Result.php index 1e070f7fc..34f901ec0 100644 --- a/src/Dibi/Drivers/NoDataResult.php +++ b/src/Dibi/Drivers/Dummy/Result.php @@ -7,14 +7,15 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Dummy; +use Dibi\Drivers; /** * The driver for no result set. */ -class NoDataResult implements Result +class Result implements Drivers\Result { public function __construct( private readonly int $rows, diff --git a/src/Dibi/Drivers/FirebirdReflector.php b/src/Dibi/Drivers/Engines/FirebirdEngine.php similarity index 98% rename from src/Dibi/Drivers/FirebirdReflector.php rename to src/Dibi/Drivers/Engines/FirebirdEngine.php index 6a8843953..3ad6d9519 100644 --- a/src/Dibi/Drivers/FirebirdReflector.php +++ b/src/Dibi/Drivers/Engines/FirebirdEngine.php @@ -7,14 +7,16 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Engines; +use Dibi\Drivers\Connection; +use Dibi\Drivers\Engine; /** * The reflector for Firebird/InterBase database. */ -class FirebirdReflector implements Engine +class FirebirdEngine implements Engine { public function __construct( private readonly Connection $driver, diff --git a/src/Dibi/Drivers/MySqlReflector.php b/src/Dibi/Drivers/Engines/MySQLEngine.php similarity index 96% rename from src/Dibi/Drivers/MySqlReflector.php rename to src/Dibi/Drivers/Engines/MySQLEngine.php index 9c0bed777..722a78479 100644 --- a/src/Dibi/Drivers/MySqlReflector.php +++ b/src/Dibi/Drivers/Engines/MySQLEngine.php @@ -7,16 +7,18 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Engines; use Dibi; +use Dibi\Drivers\Connection; +use Dibi\Drivers\Engine; /** * The reflector for MySQL databases. * @internal */ -class MySqlReflector implements Engine +class MySQLEngine implements Engine { public function __construct( private readonly Connection $driver, diff --git a/src/Dibi/Drivers/OdbcReflector.php b/src/Dibi/Drivers/Engines/ODBCEngine.php similarity index 93% rename from src/Dibi/Drivers/OdbcReflector.php rename to src/Dibi/Drivers/Engines/ODBCEngine.php index 68a243433..35ed5b068 100644 --- a/src/Dibi/Drivers/OdbcReflector.php +++ b/src/Dibi/Drivers/Engines/ODBCEngine.php @@ -7,15 +7,17 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Engines; use Dibi; +use Dibi\Drivers\Connection; +use Dibi\Drivers\Engine; /** * The reflector for ODBC connections. */ -class OdbcReflector implements Engine +class ODBCEngine implements Engine { public function __construct( private readonly Connection $driver, diff --git a/src/Dibi/Drivers/OracleReflector.php b/src/Dibi/Drivers/Engines/OracleEngine.php similarity index 92% rename from src/Dibi/Drivers/OracleReflector.php rename to src/Dibi/Drivers/Engines/OracleEngine.php index bb6e52972..3fa723e33 100644 --- a/src/Dibi/Drivers/OracleReflector.php +++ b/src/Dibi/Drivers/Engines/OracleEngine.php @@ -7,15 +7,17 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Engines; use Dibi; +use Dibi\Drivers\Connection; +use Dibi\Drivers\Engine; /** * The reflector for Oracle database. */ -class OracleReflector implements Engine +class OracleEngine implements Engine { public function __construct( private readonly Connection $driver, diff --git a/src/Dibi/Drivers/PostgreReflector.php b/src/Dibi/Drivers/Engines/PostgreSQLEngine.php similarity index 98% rename from src/Dibi/Drivers/PostgreReflector.php rename to src/Dibi/Drivers/Engines/PostgreSQLEngine.php index 2fb27148a..5622c8c72 100644 --- a/src/Dibi/Drivers/PostgreReflector.php +++ b/src/Dibi/Drivers/Engines/PostgreSQLEngine.php @@ -7,14 +7,16 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Engines; +use Dibi\Drivers\Connection; +use Dibi\Drivers\Engine; /** * The reflector for PostgreSQL database. */ -class PostgreReflector implements Engine +class PostgreSQLEngine implements Engine { public function __construct( private readonly Connection $driver, diff --git a/src/Dibi/Drivers/SqlsrvReflector.php b/src/Dibi/Drivers/Engines/SQLServerEngine.php similarity index 96% rename from src/Dibi/Drivers/SqlsrvReflector.php rename to src/Dibi/Drivers/Engines/SQLServerEngine.php index 47109676a..0b6035ef1 100644 --- a/src/Dibi/Drivers/SqlsrvReflector.php +++ b/src/Dibi/Drivers/Engines/SQLServerEngine.php @@ -7,16 +7,17 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Engines; use Dibi; -use function sprintf; +use Dibi\Drivers\Connection; +use Dibi\Drivers\Engine; /** * The reflector for Microsoft SQL Server and SQL Azure databases. */ -class SqlsrvReflector implements Engine +class SQLServerEngine implements Engine { public function __construct( private readonly Connection $driver, diff --git a/src/Dibi/Drivers/SqliteReflector.php b/src/Dibi/Drivers/Engines/SQLiteEngine.php similarity index 96% rename from src/Dibi/Drivers/SqliteReflector.php rename to src/Dibi/Drivers/Engines/SQLiteEngine.php index d828e85c3..f556081f0 100644 --- a/src/Dibi/Drivers/SqliteReflector.php +++ b/src/Dibi/Drivers/Engines/SQLiteEngine.php @@ -7,14 +7,16 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Engines; +use Dibi\Drivers\Connection; +use Dibi\Drivers\Engine; /** * The reflector for SQLite database. */ -class SqliteReflector implements Engine +class SQLiteEngine implements Engine { public function __construct( private readonly Connection $driver, diff --git a/src/Dibi/Drivers/FirebirdDriver.php b/src/Dibi/Drivers/Firebird/Connection.php similarity index 95% rename from src/Dibi/Drivers/FirebirdDriver.php rename to src/Dibi/Drivers/Firebird/Connection.php index 2ffce0e71..72490511e 100644 --- a/src/Dibi/Drivers/FirebirdDriver.php +++ b/src/Dibi/Drivers/Firebird/Connection.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Ibase; use Dibi; +use Dibi\Drivers; use Dibi\Helpers; use function is_resource; @@ -25,7 +26,7 @@ * - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default. * - resource (resource) => existing connection resource */ -class FirebirdDriver implements Connection +class Connection implements Drivers\Connection { public const ErrorExceptionThrown = -836; @@ -200,9 +201,9 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { - return new FirebirdReflector($this); + return new Drivers\Engines\FirebirdEngine($this); } @@ -210,9 +211,9 @@ public function getReflector(): Engine * Result set driver factory. * @param resource $resource */ - public function createResultDriver($resource): FirebirdResult + public function createResultDriver($resource): Result { - return new FirebirdResult($resource); + return new Result($resource); } diff --git a/src/Dibi/Drivers/FirebirdResult.php b/src/Dibi/Drivers/Firebird/Result.php similarity index 94% rename from src/Dibi/Drivers/FirebirdResult.php rename to src/Dibi/Drivers/Firebird/Result.php index d46af1427..c979973a2 100644 --- a/src/Dibi/Drivers/FirebirdResult.php +++ b/src/Dibi/Drivers/Firebird/Result.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\Ibase; use Dibi; +use Dibi\Drivers; use Dibi\Helpers; use function is_resource; @@ -17,7 +18,7 @@ /** * The driver for Firebird/InterBase result set. */ -class FirebirdResult implements Result +class Result implements Drivers\Result { public function __construct( /** @var resource */ @@ -46,7 +47,7 @@ public function fetch(bool $assoc): ?array : @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @ if (ibase_errcode()) { - if (ibase_errcode() === FirebirdDriver::ERROR_EXCEPTION_THROWN) { + if (ibase_errcode() === Connection::ERROR_EXCEPTION_THROWN) { preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match); throw new Dibi\ProcedureException($match[3], $match[1], $match[2]); diff --git a/src/Dibi/Drivers/MySqliDriver.php b/src/Dibi/Drivers/MySQLi/Connection.php similarity index 96% rename from src/Dibi/Drivers/MySqliDriver.php rename to src/Dibi/Drivers/MySQLi/Connection.php index 9ba993703..3f5b1e4b0 100644 --- a/src/Dibi/Drivers/MySqliDriver.php +++ b/src/Dibi/Drivers/MySQLi/Connection.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\MySQLi; use Dibi; +use Dibi\Drivers; use function in_array; use const MYSQLI_REPORT_OFF, MYSQLI_STORE_RESULT, MYSQLI_USE_RESULT, PREG_SET_ORDER; @@ -32,7 +33,7 @@ * - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html * - resource (mysqli) => existing connection resource */ -class MySqliDriver implements Connection +class Connection implements Drivers\Connection { public const ErrorAccessDenied = 1045; public const ErrorDuplicateEntry = 1062; @@ -265,18 +266,18 @@ public function getResource(): ?\mysqli /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { - return new MySqlReflector($this); + return new Drivers\Engines\MySQLEngine($this); } /** * Result set driver factory. */ - public function createResultDriver(\mysqli_result $result): MySqliResult + public function createResultDriver(\mysqli_result $result): Result { - return new MySqliResult($result, $this->buffered); + return new Result($result, $this->buffered); } diff --git a/src/Dibi/Drivers/MySqliResult.php b/src/Dibi/Drivers/MySQLi/Result.php similarity index 96% rename from src/Dibi/Drivers/MySqliResult.php rename to src/Dibi/Drivers/MySQLi/Result.php index 58f8b5472..3ff354fcd 100644 --- a/src/Dibi/Drivers/MySqliResult.php +++ b/src/Dibi/Drivers/MySQLi/Result.php @@ -7,16 +7,17 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\MySQLi; use Dibi; +use Dibi\Drivers; use const MYSQLI_TYPE_LONG, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_TIME, MYSQLI_TYPE_TINY; /** * The driver for MySQL result set. */ -class MySqliResult implements Result +class Result implements Drivers\Result { public function __construct( private readonly \mysqli_result $resultSet, diff --git a/src/Dibi/Drivers/OracleDriver.php b/src/Dibi/Drivers/OCI8/Connection.php similarity index 96% rename from src/Dibi/Drivers/OracleDriver.php rename to src/Dibi/Drivers/OCI8/Connection.php index 43475ace1..5658ac8b4 100644 --- a/src/Dibi/Drivers/OracleDriver.php +++ b/src/Dibi/Drivers/OCI8/Connection.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\OCI8; use Dibi; +use Dibi\Drivers; use function in_array, is_resource; @@ -26,7 +27,7 @@ * - resource (resource) => existing connection resource * - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect */ -class OracleDriver implements Connection +class Connection implements Drivers\Connection { /** @var resource */ private $connection; @@ -190,9 +191,9 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { - return new OracleReflector($this); + return new Drivers\Engines\OracleEngine($this); } @@ -200,9 +201,9 @@ public function getReflector(): Engine * Result set driver factory. * @param resource $resource */ - public function createResultDriver($resource): OracleResult + public function createResultDriver($resource): Result { - return new OracleResult($resource); + return new Result($resource); } diff --git a/src/Dibi/Drivers/OracleResult.php b/src/Dibi/Drivers/OCI8/Result.php similarity index 96% rename from src/Dibi/Drivers/OracleResult.php rename to src/Dibi/Drivers/OCI8/Result.php index 1f826f403..e9c5c5844 100644 --- a/src/Dibi/Drivers/OracleResult.php +++ b/src/Dibi/Drivers/OCI8/Result.php @@ -7,16 +7,17 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\OCI8; use Dibi; +use Dibi\Drivers; use function is_resource; /** * The driver for Oracle result set. */ -class OracleResult implements Result +class Result implements Drivers\Result { public function __construct( /** @var resource */ diff --git a/src/Dibi/Drivers/OdbcDriver.php b/src/Dibi/Drivers/ODBC/Connection.php similarity index 95% rename from src/Dibi/Drivers/OdbcDriver.php rename to src/Dibi/Drivers/ODBC/Connection.php index 2adaf669f..8213cff5a 100644 --- a/src/Dibi/Drivers/OdbcDriver.php +++ b/src/Dibi/Drivers/ODBC/Connection.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\ODBC; use Dibi; +use Dibi\Drivers; use function is_resource; @@ -24,7 +25,7 @@ * - resource (resource) => existing connection resource * - microseconds (bool) => use microseconds in datetime format? */ -class OdbcDriver implements Connection +class Connection implements Drivers\Connection { /** @var resource */ private $connection; @@ -176,9 +177,9 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { - return new OdbcReflector($this); + return new Drivers\Engines\ODBCEngine($this); } @@ -186,9 +187,9 @@ public function getReflector(): Engine * Result set driver factory. * @param resource $resource */ - public function createResultDriver($resource): OdbcResult + public function createResultDriver($resource): Result { - return new OdbcResult($resource); + return new Result($resource); } diff --git a/src/Dibi/Drivers/OdbcResult.php b/src/Dibi/Drivers/ODBC/Result.php similarity index 96% rename from src/Dibi/Drivers/OdbcResult.php rename to src/Dibi/Drivers/ODBC/Result.php index 1271eba76..5ae5aad97 100644 --- a/src/Dibi/Drivers/OdbcResult.php +++ b/src/Dibi/Drivers/ODBC/Result.php @@ -7,16 +7,17 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\ODBC; use Dibi; +use Dibi\Drivers; use function is_resource; /** * The driver interacting with result set via ODBC connections. */ -class OdbcResult implements Result +class Result implements Drivers\Result { private int $row = 0; diff --git a/src/Dibi/Drivers/PdoDriver.php b/src/Dibi/Drivers/PDO/Connection.php similarity index 91% rename from src/Dibi/Drivers/PdoDriver.php rename to src/Dibi/Drivers/PDO/Connection.php index 5ae38d49e..56223a5c1 100644 --- a/src/Dibi/Drivers/PdoDriver.php +++ b/src/Dibi/Drivers/PDO/Connection.php @@ -7,9 +7,11 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\PDO; use Dibi; +use Dibi\Drivers; +use Dibi\Drivers\Engines; use Dibi\Helpers; use PDO; use function sprintf; @@ -25,7 +27,7 @@ * - options (array) => driver specific options {@see PDO::__construct} * - resource (PDO) => existing connection */ -class PdoDriver implements Connection +class Connection implements Drivers\Connection { private ?PDO $connection; private ?int $affectedRows; @@ -94,10 +96,10 @@ public function query(string $sql): ?Result $code ??= 0; $message = "SQLSTATE[$sqlState]: $message"; throw match ($this->driverName) { - 'mysql' => MySqliDriver::createException($message, $code, $sql), - 'oci' => OracleDriver::createException($message, $code, $sql), - 'pgsql' => PostgreDriver::createException($message, $sqlState, $sql), - 'sqlite' => SqliteDriver::createException($message, $code, $sql), + 'mysql' => Drivers\MySQLi\Connection::createException($message, $code, $sql), + 'oci' => Drivers\OCI8\Connection::createException($message, $code, $sql), + 'pgsql' => Drivers\PgSQL\Connection::createException($message, $sqlState, $sql), + 'sqlite' => Drivers\SQLite3\Connection::createException($message, $code, $sql), default => new Dibi\DriverException($message, $code, $sql), }; } @@ -172,14 +174,14 @@ public function getResource(): ?PDO /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { return match ($this->driverName) { - 'mysql' => new MySqlReflector($this), - 'oci' => new OracleReflector($this), - 'pgsql' => new PostgreReflector($this), - 'sqlite' => new SqliteReflector($this), - 'mssql', 'dblib', 'sqlsrv' => new SqlsrvReflector($this), + 'mysql' => new Engines\MySQLEngine($this), + 'oci' => new Engines\OracleEngine($this), + 'pgsql' => new Engines\PostgreSQLEngine($this), + 'sqlite' => new Engines\SQLiteEngine($this), + 'mssql', 'dblib', 'sqlsrv' => new Engines\SQLServerEngine($this), default => throw new Dibi\NotSupportedException, }; } @@ -188,9 +190,9 @@ public function getReflector(): Engine /** * Result set driver factory. */ - public function createResultDriver(\PDOStatement $result): PdoResult + public function createResultDriver(\PDOStatement $result): Result { - return new PdoResult($result, $this->driverName); + return new Result($result, $this->driverName); } diff --git a/src/Dibi/Drivers/PdoResult.php b/src/Dibi/Drivers/PDO/Result.php similarity index 96% rename from src/Dibi/Drivers/PdoResult.php rename to src/Dibi/Drivers/PDO/Result.php index 886f116b7..9365c4c4d 100644 --- a/src/Dibi/Drivers/PdoResult.php +++ b/src/Dibi/Drivers/PDO/Result.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\PDO; use Dibi; +use Dibi\Drivers; use Dibi\Helpers; use PDO; @@ -17,7 +18,7 @@ /** * The driver for PDO result set. */ -class PdoResult implements Result +class Result implements Drivers\Result { public function __construct( private ?\PDOStatement $resultSet, diff --git a/src/Dibi/Drivers/PostgreDriver.php b/src/Dibi/Drivers/PgSQL/Connection.php similarity index 96% rename from src/Dibi/Drivers/PostgreDriver.php rename to src/Dibi/Drivers/PgSQL/Connection.php index 8e90d15f8..a5645e03d 100644 --- a/src/Dibi/Drivers/PostgreDriver.php +++ b/src/Dibi/Drivers/PgSQL/Connection.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\PgSQL; use Dibi; +use Dibi\Drivers; use Dibi\Helpers; use PgSql; use function in_array, is_array, is_resource, strlen; @@ -27,7 +28,7 @@ * - resource (PgSql\Connection) => existing connection resource * - connect_type (int) => see pg_connect() */ -class PostgreDriver implements Connection +class Connection implements Drivers\Connection { private PgSql\Connection $connection; private ?int $affectedRows; @@ -232,18 +233,18 @@ public function getResource(): PgSql\Connection /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { - return new PostgreReflector($this); + return new Drivers\Engines\PostgreSQLEngine($this); } /** * Result set driver factory. */ - public function createResultDriver(PgSql\Result $resource): PostgreResult + public function createResultDriver(PgSql\Result $resource): Result { - return new PostgreResult($resource); + return new Result($resource); } diff --git a/src/Dibi/Drivers/PostgreResult.php b/src/Dibi/Drivers/PgSQL/Result.php similarity index 95% rename from src/Dibi/Drivers/PostgreResult.php rename to src/Dibi/Drivers/PgSQL/Result.php index 96b9d750b..92553fe72 100644 --- a/src/Dibi/Drivers/PostgreResult.php +++ b/src/Dibi/Drivers/PgSQL/Result.php @@ -7,8 +7,9 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\PgSQL; +use Dibi\Drivers; use Dibi\Helpers; use PgSql; @@ -16,7 +17,7 @@ /** * The driver for PostgreSQL result set. */ -class PostgreResult implements Result +class Result implements Drivers\Result { public function __construct( private readonly PgSql\Result $resultSet, diff --git a/src/Dibi/Drivers/SqlsrvDriver.php b/src/Dibi/Drivers/SQLSrv/Connection.php similarity index 95% rename from src/Dibi/Drivers/SqlsrvDriver.php rename to src/Dibi/Drivers/SQLSrv/Connection.php index 383a9eb0b..e5b3953a0 100644 --- a/src/Dibi/Drivers/SqlsrvDriver.php +++ b/src/Dibi/Drivers/SQLSrv/Connection.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\SQLSrv; use Dibi; +use Dibi\Drivers; use Dibi\Helpers; use function is_resource, sprintf; @@ -26,7 +27,7 @@ * - charset => character encoding to set (default is UTF-8) * - resource (resource) => existing connection resource */ -class SqlsrvDriver implements Connection +class Connection implements Drivers\Connection { /** @var resource */ private $connection; @@ -171,9 +172,9 @@ public function getResource(): mixed /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { - return new SqlsrvReflector($this); + return new Drivers\Engines\SQLServerEngine($this); } @@ -181,9 +182,9 @@ public function getReflector(): Engine * Result set driver factory. * @param resource $resource */ - public function createResultDriver($resource): SqlsrvResult + public function createResultDriver($resource): Result { - return new SqlsrvResult($resource); + return new Result($resource); } diff --git a/src/Dibi/Drivers/SqlsrvResult.php b/src/Dibi/Drivers/SQLSrv/Result.php similarity index 95% rename from src/Dibi/Drivers/SqlsrvResult.php rename to src/Dibi/Drivers/SQLSrv/Result.php index 2180d1bb2..c9e91b88b 100644 --- a/src/Dibi/Drivers/SqlsrvResult.php +++ b/src/Dibi/Drivers/SQLSrv/Result.php @@ -7,16 +7,17 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\SQLSrv; use Dibi; +use Dibi\Drivers; use function is_resource; /** * The driver for Microsoft SQL Server and SQL Azure result set. */ -class SqlsrvResult implements Result +class Result implements Drivers\Result { public function __construct( /** @var resource */ diff --git a/src/Dibi/Drivers/SqliteDriver.php b/src/Dibi/Drivers/SQLite3/Connection.php similarity index 95% rename from src/Dibi/Drivers/SqliteDriver.php rename to src/Dibi/Drivers/SQLite3/Connection.php index dab671a2c..0391d7fa7 100644 --- a/src/Dibi/Drivers/SqliteDriver.php +++ b/src/Dibi/Drivers/SQLite3/Connection.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\SQLite3; use Dibi; +use Dibi\Drivers; use Dibi\Helpers; use SQLite3; @@ -23,7 +24,7 @@ * - formatDateTime => how to format datetime in SQL (@see date) * - resource (SQLite3) => existing connection resource */ -class SqliteDriver implements Connection +class Connection implements Drivers\Connection { private SQLite3 $connection; private string $fmtDate; @@ -174,18 +175,18 @@ public function getResource(): ?SQLite3 /** * Returns the connection reflector. */ - public function getReflector(): Engine + public function getReflector(): Drivers\Engine { - return new SqliteReflector($this); + return new Drivers\Engines\SQLiteEngine($this); } /** * Result set driver factory. */ - public function createResultDriver(\SQLite3Result $result): SqliteResult + public function createResultDriver(\SQLite3Result $result): Result { - return new SqliteResult($result); + return new Result($result); } diff --git a/src/Dibi/Drivers/SqliteResult.php b/src/Dibi/Drivers/SQLite3/Result.php similarity index 96% rename from src/Dibi/Drivers/SqliteResult.php rename to src/Dibi/Drivers/SQLite3/Result.php index 04df4a536..cf89a9db2 100644 --- a/src/Dibi/Drivers/SqliteResult.php +++ b/src/Dibi/Drivers/SQLite3/Result.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Dibi\Drivers; +namespace Dibi\Drivers\SQLite3; use Dibi; +use Dibi\Drivers; use Dibi\Helpers; use const SQLITE3_ASSOC, SQLITE3_BLOB, SQLITE3_FLOAT, SQLITE3_INTEGER, SQLITE3_NULL, SQLITE3_NUM, SQLITE3_TEXT; @@ -17,7 +18,7 @@ /** * The driver for SQLite result set. */ -class SqliteResult implements Result +class Result implements Drivers\Result { public function __construct( private readonly \SQLite3Result $resultSet, diff --git a/src/Dibi/Drivers/Sqlite3Driver.php b/src/Dibi/Drivers/Sqlite3Driver.php deleted file mode 100644 index 0a99af83c..000000000 --- a/src/Dibi/Drivers/Sqlite3Driver.php +++ /dev/null @@ -1,18 +0,0 @@ -setAttribute(PDO::ATTR_ERRMODE, $errorMode); } - new Dibi\Drivers\PdoDriver(['resource' => $pdo]); + new Dibi\Drivers\PDO\Connection(['resource' => $pdo]); } From 5c8cc9292ca24a434ebe1aa913153fe96d15d14c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 3 Sep 2024 18:10:42 +0200 Subject: [PATCH 12/15] drivers: escape*() methods moved to Engine [WIP] --- src/Dibi/Connection.php | 12 +- src/Dibi/DataSource.php | 2 +- src/Dibi/Drivers/Connection.php | 20 --- src/Dibi/Drivers/Engine.php | 20 +++ src/Dibi/Drivers/Engines/FirebirdEngine.php | 53 ++++++ src/Dibi/Drivers/Engines/MySQLEngine.php | 64 ++++++- src/Dibi/Drivers/Engines/ODBCEngine.php | 57 +++++++ src/Dibi/Drivers/Engines/OracleEngine.php | 66 +++++++ src/Dibi/Drivers/Engines/PostgreSQLEngine.php | 69 +++++++- src/Dibi/Drivers/Engines/SQLServerEngine.php | 59 +++++++ src/Dibi/Drivers/Engines/SQLiteEngine.php | 64 ++++++- src/Dibi/Drivers/Firebird/Connection.php | 52 ------ src/Dibi/Drivers/MySQLi/Connection.php | 60 ------- src/Dibi/Drivers/OCI8/Connection.php | 66 ------- src/Dibi/Drivers/ODBC/Connection.php | 57 ------- src/Dibi/Drivers/PDO/Connection.php | 161 ------------------ src/Dibi/Drivers/PgSQL/Connection.php | 62 ------- src/Dibi/Drivers/SQLSrv/Connection.php | 59 ------- src/Dibi/Drivers/SQLite3/Connection.php | 55 ------ src/Dibi/Translator.php | 34 ++-- tests/dibi/Connection.objectTranslator.phpt | 10 +- 21 files changed, 478 insertions(+), 624 deletions(-) diff --git a/src/Dibi/Connection.php b/src/Dibi/Connection.php index ad6457dbb..4031fbb3c 100644 --- a/src/Dibi/Connection.php +++ b/src/Dibi/Connection.php @@ -42,6 +42,7 @@ class Connection /** @var string[] resultset formats */ private array $formats; private ?Drivers\Connection $driver = null; + private Drivers\Engine $engine; private ?Translator $translator = null; /** @var array */ @@ -670,6 +671,15 @@ public function loadFile(string $file, ?callable $onProgress = null): int } + public function getDatabaseEngine(): Drivers\Engine + { + if (!$this->driver) { // TODO + $this->connect(); + } + return $this->engine ??= $this->driver->getReflector(); + } + + /** * Gets a information about the current database. */ @@ -679,7 +689,7 @@ public function getDatabaseInfo(): Reflection\Database $this->connect(); } - return new Reflection\Database($this->driver->getReflector(), $this->config['database'] ?? null); + return new Reflection\Database($this->getDatabaseEngine(), $this->config['database'] ?? null); } diff --git a/src/Dibi/DataSource.php b/src/Dibi/DataSource.php index 1ecc0ec54..c2c6d9a55 100644 --- a/src/Dibi/DataSource.php +++ b/src/Dibi/DataSource.php @@ -35,7 +35,7 @@ class DataSource implements IDataSource public function __construct(string $sql, Connection $connection) { $this->sql = strpbrk($sql, " \t\r\n") === false - ? $connection->getDriver()->escapeIdentifier($sql) // table name + ? $connection->getDatabaseEngine()->escapeIdentifier($sql) // table name : '(' . $sql . ') t'; // SQL command $this->connection = $connection; } diff --git a/src/Dibi/Drivers/Connection.php b/src/Dibi/Drivers/Connection.php index 18f967db2..6549e57f3 100644 --- a/src/Dibi/Drivers/Connection.php +++ b/src/Dibi/Drivers/Connection.php @@ -74,24 +74,4 @@ function getReflector(): Engine; function escapeText(string $value): string; function escapeBinary(string $value): string; - - function escapeIdentifier(string $value): string; - - function escapeBool(bool $value): string; - - function escapeDate(\DateTimeInterface $value): string; - - function escapeDateTime(\DateTimeInterface $value): string; - - function escapeDateInterval(\DateInterval $value): string; - - /** - * Encodes string for use in a LIKE statement. - */ - function escapeLike(string $value, int $pos): string; - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - function applyLimit(string &$sql, ?int $limit, ?int $offset): void; } diff --git a/src/Dibi/Drivers/Engine.php b/src/Dibi/Drivers/Engine.php index ad3863f58..75c254635 100644 --- a/src/Dibi/Drivers/Engine.php +++ b/src/Dibi/Drivers/Engine.php @@ -15,6 +15,26 @@ */ interface Engine { + function escapeIdentifier(string $value): string; + + function escapeBool(bool $value): string; + + function escapeDate(\DateTimeInterface $value): string; + + function escapeDateTime(\DateTimeInterface $value): string; + + function escapeDateInterval(\DateInterval $value): string; + + /** + * Encodes string for use in a LIKE statement. + */ + function escapeLike(string $value, int $pos): string; + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + function applyLimit(string &$sql, ?int $limit, ?int $offset): void; + /** * Returns list of tables. * @return array of {name [, (bool) view ]} diff --git a/src/Dibi/Drivers/Engines/FirebirdEngine.php b/src/Dibi/Drivers/Engines/FirebirdEngine.php index 3ad6d9519..0c24b166f 100644 --- a/src/Dibi/Drivers/Engines/FirebirdEngine.php +++ b/src/Dibi/Drivers/Engines/FirebirdEngine.php @@ -9,6 +9,7 @@ namespace Dibi\Drivers\Engines; +use Dibi; use Dibi\Drivers\Connection; use Dibi\Drivers\Engine; @@ -24,6 +25,58 @@ public function __construct( } + public function escapeIdentifier(string $value): string + { + return '"' . str_replace('"', '""', $value) . '"'; + } + + + public function escapeBool(bool $value): string + { + return $value ? '1' : '0'; + } + + + public function escapeDate(\DateTimeInterface $value): string + { + return $value->format("'Y-m-d'"); + } + + + public function escapeDateTime(\DateTimeInterface $value): string + { + return "'" . substr($value->format('Y-m-d H:i:s.u'), 0, -2) . "'"; + } + + + public function escapeDateInterval(\DateInterval $value): string + { + throw new Dibi\NotImplementedException; + } + + + /** + * Encodes string for use in a LIKE statement. + */ + public function escapeLike(string $value, int $pos): string + { + $value = addcslashes($this->driver->escapeText($value), '%_\\'); + return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; + } + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + { + if ($limit > 0 || $offset > 0) { + // http://www.firebirdsql.org/refdocs/langrefupd20-select.html + $sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . $limit : '') . ($offset > 0 ? ' SKIP ' . $offset : '') . ' * FROM (' . $sql . ')'; + } + } + + /** * Returns list of tables. */ diff --git a/src/Dibi/Drivers/Engines/MySQLEngine.php b/src/Dibi/Drivers/Engines/MySQLEngine.php index 722a78479..1db5491b5 100644 --- a/src/Dibi/Drivers/Engines/MySQLEngine.php +++ b/src/Dibi/Drivers/Engines/MySQLEngine.php @@ -26,6 +26,66 @@ public function __construct( } + public function escapeIdentifier(string $value): string + { + return '`' . str_replace('`', '``', $value) . '`'; + } + + + public function escapeBool(bool $value): string + { + return $value ? '1' : '0'; + } + + + public function escapeDate(\DateTimeInterface $value): string + { + return $value->format("'Y-m-d'"); + } + + + public function escapeDateTime(\DateTimeInterface $value): string + { + return $value->format("'Y-m-d H:i:s.u'"); + } + + + public function escapeDateInterval(\DateInterval $value): string + { + if ($value->y || $value->m || $value->d) { + throw new Dibi\NotSupportedException('Only time interval is supported.'); + } + + return $value->format("'%r%H:%I:%S.%f'"); + } + + + /** + * Encodes string for use in a LIKE statement. + */ + public function escapeLike(string $value, int $pos): string + { + $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); + return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); + } + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + { + if ($limit < 0 || $offset < 0) { + throw new Dibi\NotSupportedException('Negative offset or limit.'); + + } elseif ($limit !== null || $offset) { + // see http://dev.mysql.com/doc/refman/5.0/en/select.html + $sql .= ' LIMIT ' . ($limit ?? '18446744073709551615') + . ($offset ? ' OFFSET ' . $offset : ''); + } + } + + /** * Returns list of tables. */ @@ -49,7 +109,7 @@ public function getTables(): array */ public function getColumns(string $table): array { - $res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}"); + $res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->escapeIdentifier($table)}"); $columns = []; while ($row = $res->fetch(true)) { $type = explode('(', $row['Type']); @@ -74,7 +134,7 @@ public function getColumns(string $table): array */ public function getIndexes(string $table): array { - $res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}"); + $res = $this->driver->query("SHOW INDEX FROM {$this->escapeIdentifier($table)}"); $indexes = []; while ($row = $res->fetch(true)) { $indexes[$row['Key_name']]['name'] = $row['Key_name']; diff --git a/src/Dibi/Drivers/Engines/ODBCEngine.php b/src/Dibi/Drivers/Engines/ODBCEngine.php index 35ed5b068..3a3ad1d3c 100644 --- a/src/Dibi/Drivers/Engines/ODBCEngine.php +++ b/src/Dibi/Drivers/Engines/ODBCEngine.php @@ -25,6 +25,63 @@ public function __construct( } + public function escapeIdentifier(string $value): string + { + return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']'; + } + + + public function escapeBool(bool $value): string + { + return $value ? '1' : '0'; + } + + + public function escapeDate(\DateTimeInterface $value): string + { + return $value->format('#m/d/Y#'); + } + + + public function escapeDateTime(\DateTimeInterface $value): string + { + return $value->format($this->microseconds ? '#m/d/Y H:i:s.u#' : '#m/d/Y H:i:s#'); // TODO + } + + + public function escapeDateInterval(\DateInterval $value): string + { + throw new Dibi\NotImplementedException; + } + + + /** + * Encodes string for use in a LIKE statement. + */ + public function escapeLike(string $value, int $pos): string + { + $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); + return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); + } + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + { + if ($offset) { + throw new Dibi\NotSupportedException('Offset is not supported by this database.'); + + } elseif ($limit < 0) { + throw new Dibi\NotSupportedException('Negative offset or limit.'); + + } elseif ($limit !== null) { + $sql = 'SELECT TOP ' . $limit . ' * FROM (' . $sql . ') t'; + } + } + + /** * Returns list of tables. */ diff --git a/src/Dibi/Drivers/Engines/OracleEngine.php b/src/Dibi/Drivers/Engines/OracleEngine.php index 3fa723e33..620b15526 100644 --- a/src/Dibi/Drivers/Engines/OracleEngine.php +++ b/src/Dibi/Drivers/Engines/OracleEngine.php @@ -25,6 +25,72 @@ public function __construct( } + public function escapeIdentifier(string $value): string + { + // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm + return '"' . str_replace('"', '""', $value) . '"'; + } + + + public function escapeBool(bool $value): string + { + return $value ? '1' : '0'; + } + + + public function escapeDate(\DateTimeInterface $value): string + { + return $this->nativeDate // TODO + ? "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')" + : $value->format('U'); + } + + + public function escapeDateTime(\DateTimeInterface $value): string + { + return $this->nativeDate // TODO + ? "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')" + : $value->format('U'); + } + + + public function escapeDateInterval(\DateInterval $value): string + { + throw new Dibi\NotImplementedException; + } + + + /** + * Encodes string for use in a LIKE statement. + */ + public function escapeLike(string $value, int $pos): string + { + $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_"); + $value = str_replace("'", "''", $value); + return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); + } + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + { + if ($limit < 0 || $offset < 0) { + throw new Dibi\NotSupportedException('Negative offset or limit.'); + + } elseif ($offset) { + // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html + $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' + . ($limit !== null ? 'WHERE ROWNUM <= ' . ($offset + $limit) : '') + . ') WHERE "__rnum" > ' . $offset; + + } elseif ($limit !== null) { + $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit; + } + } + + /** * Returns list of tables. */ diff --git a/src/Dibi/Drivers/Engines/PostgreSQLEngine.php b/src/Dibi/Drivers/Engines/PostgreSQLEngine.php index 5622c8c72..338be7395 100644 --- a/src/Dibi/Drivers/Engines/PostgreSQLEngine.php +++ b/src/Dibi/Drivers/Engines/PostgreSQLEngine.php @@ -9,6 +9,7 @@ namespace Dibi\Drivers\Engines; +use Dibi; use Dibi\Drivers\Connection; use Dibi\Drivers\Engine; @@ -24,6 +25,68 @@ public function __construct( } + public function escapeIdentifier(string $value): string + { + // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + return '"' . str_replace('"', '""', $value) . '"'; + } + + + public function escapeBool(bool $value): string + { + return $value ? 'TRUE' : 'FALSE'; + } + + + public function escapeDate(\DateTimeInterface $value): string + { + return $value->format("'Y-m-d'"); + } + + + public function escapeDateTime(\DateTimeInterface $value): string + { + return $value->format("'Y-m-d H:i:s.u'"); + } + + + public function escapeDateInterval(\DateInterval $value): string + { + throw new Dibi\NotImplementedException; + } + + + /** + * Encodes string for use in a LIKE statement. + */ + public function escapeLike(string $value, int $pos): string + { + $bs = $this->driver->escapeText('\\'); // standard_conforming_strings = on/off + $value = $this->driver->escapeText($value); + $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); + return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); + } + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + { + if ($limit < 0 || $offset < 0) { + throw new Dibi\NotSupportedException('Negative offset or limit.'); + } + + if ($limit !== null) { + $sql .= ' LIMIT ' . $limit; + } + + if ($offset) { + $sql .= ' OFFSET ' . $offset; + } + } + + /** * Returns list of tables. */ @@ -64,7 +127,7 @@ public function getTables(): array */ public function getColumns(string $table): array { - $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table)); + $_table = $this->driver->escapeText($this->escapeIdentifier($table)); $res = $this->driver->query(" SELECT * @@ -124,7 +187,7 @@ public function getColumns(string $table): array */ public function getIndexes(string $table): array { - $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table)); + $_table = $this->driver->escapeText($this->escapeIdentifier($table)); $res = $this->driver->query(" SELECT a.attnum AS ordinal_position, @@ -174,7 +237,7 @@ public function getIndexes(string $table): array */ public function getForeignKeys(string $table): array { - $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table)); + $_table = $this->driver->escapeText($this->escapeIdentifier($table)); $res = $this->driver->query(" SELECT diff --git a/src/Dibi/Drivers/Engines/SQLServerEngine.php b/src/Dibi/Drivers/Engines/SQLServerEngine.php index 0b6035ef1..ff1b620a5 100644 --- a/src/Dibi/Drivers/Engines/SQLServerEngine.php +++ b/src/Dibi/Drivers/Engines/SQLServerEngine.php @@ -25,6 +25,65 @@ public function __construct( } + public function escapeIdentifier(string $value): string + { + // @see https://msdn.microsoft.com/en-us/library/ms176027.aspx + return '[' . str_replace(']', ']]', $value) . ']'; + } + + + public function escapeBool(bool $value): string + { + return $value ? '1' : '0'; + } + + + public function escapeDate(\DateTimeInterface $value): string + { + return $value->format("'Y-m-d'"); + } + + + public function escapeDateTime(\DateTimeInterface $value): string + { + return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')'; + } + + + public function escapeDateInterval(\DateInterval $value): string + { + throw new Dibi\NotImplementedException; + } + + + /** + * Encodes string for use in a LIKE statement. + */ + public function escapeLike(string $value, int $pos): string + { + $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); + return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); + } + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + { + if ($limit < 0 || $offset < 0) { + throw new Dibi\NotSupportedException('Negative offset or limit.'); + + } elseif ($limit !== null) { + // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx + $sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit); + } elseif ($offset) { + // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx + $sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset); + } + } + + /** * Returns list of tables. */ diff --git a/src/Dibi/Drivers/Engines/SQLiteEngine.php b/src/Dibi/Drivers/Engines/SQLiteEngine.php index f556081f0..703c03dfd 100644 --- a/src/Dibi/Drivers/Engines/SQLiteEngine.php +++ b/src/Dibi/Drivers/Engines/SQLiteEngine.php @@ -9,6 +9,7 @@ namespace Dibi\Drivers\Engines; +use Dibi; use Dibi\Drivers\Connection; use Dibi\Drivers\Engine; @@ -24,6 +25,61 @@ public function __construct( } + public function escapeIdentifier(string $value): string + { + return '[' . strtr($value, '[]', ' ') . ']'; + } + + + public function escapeBool(bool $value): string + { + return $value ? '1' : '0'; + } + + + public function escapeDate(\DateTimeInterface $value): string + { + return $value->format($this->fmtDate); // TODO + } + + + public function escapeDateTime(\DateTimeInterface $value): string + { + return $value->format($this->fmtDateTime); // TODO + } + + + public function escapeDateInterval(\DateInterval $value): string + { + throw new Dibi\NotImplementedException; + } + + + /** + * Encodes string for use in a LIKE statement. + */ + public function escapeLike(string $value, int $pos): string + { + $value = addcslashes($this->driver->escapeText($value), '%_\\'); + return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; + } + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(string &$sql, ?int $limit, ?int $offset): void + { + if ($limit < 0 || $offset < 0) { + throw new Dibi\NotSupportedException('Negative offset or limit.'); + + } elseif ($limit !== null || $offset) { + $sql .= ' LIMIT ' . ($limit ?? '-1') + . ($offset ? ' OFFSET ' . $offset : ''); + } + } + + /** * Returns list of tables. */ @@ -49,7 +105,7 @@ public function getTables(): array */ public function getColumns(string $table): array { - $res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})"); + $res = $this->driver->query("PRAGMA table_info({$this->escapeIdentifier($table)})"); $columns = []; while ($row = $res->fetch(true)) { $column = $row['name']; @@ -76,7 +132,7 @@ public function getColumns(string $table): array */ public function getIndexes(string $table): array { - $res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})"); + $res = $this->driver->query("PRAGMA index_list({$this->escapeIdentifier($table)})"); $indexes = []; while ($row = $res->fetch(true)) { $indexes[$row['name']]['name'] = $row['name']; @@ -84,7 +140,7 @@ public function getIndexes(string $table): array } foreach ($indexes as $index => $values) { - $res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})"); + $res = $this->driver->query("PRAGMA index_info({$this->escapeIdentifier($index)})"); while ($row = $res->fetch(true)) { $indexes[$index]['columns'][$row['seqno']] = $row['name']; } @@ -127,7 +183,7 @@ public function getIndexes(string $table): array */ public function getForeignKeys(string $table): array { - $res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})"); + $res = $this->driver->query("PRAGMA foreign_key_list({$this->escapeIdentifier($table)})"); $keys = []; while ($row = $res->fetch(true)) { $keys[$row['id']]['name'] = $row['id']; // foreign key name diff --git a/src/Dibi/Drivers/Firebird/Connection.php b/src/Dibi/Drivers/Firebird/Connection.php index 72490511e..37f8b30ba 100644 --- a/src/Dibi/Drivers/Firebird/Connection.php +++ b/src/Dibi/Drivers/Firebird/Connection.php @@ -233,56 +233,4 @@ public function escapeBinary(string $value): string { return "'" . str_replace("'", "''", $value) . "'"; } - - - public function escapeIdentifier(string $value): string - { - return '"' . str_replace('"', '""', $value) . '"'; - } - - - public function escapeBool(bool $value): string - { - return $value ? '1' : '0'; - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $value->format("'Y-m-d'"); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return "'" . substr($value->format('Y-m-d H:i:s.u'), 0, -2) . "'"; - } - - - public function escapeDateInterval(\DateInterval $value): string - { - throw new Dibi\NotImplementedException; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $value = addcslashes($this->escapeText($value), '%_\\'); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($limit > 0 || $offset > 0) { - // http://www.firebirdsql.org/refdocs/langrefupd20-select.html - $sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . $limit : '') . ($offset > 0 ? ' SKIP ' . $offset : '') . ' * FROM (' . $sql . ')'; - } - } } diff --git a/src/Dibi/Drivers/MySQLi/Connection.php b/src/Dibi/Drivers/MySQLi/Connection.php index 3f5b1e4b0..4e9988e06 100644 --- a/src/Dibi/Drivers/MySQLi/Connection.php +++ b/src/Dibi/Drivers/MySQLi/Connection.php @@ -297,64 +297,4 @@ public function escapeBinary(string $value): string { return "_binary'" . $this->connection->escape_string($value) . "'"; } - - - public function escapeIdentifier(string $value): string - { - return '`' . str_replace('`', '``', $value) . '`'; - } - - - public function escapeBool(bool $value): string - { - return $value ? '1' : '0'; - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $value->format("'Y-m-d'"); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return $value->format("'Y-m-d H:i:s.u'"); - } - - - public function escapeDateInterval(\DateInterval $value): string - { - if ($value->y || $value->m || $value->d) { - throw new Dibi\NotSupportedException('Only time interval is supported.'); - } - - return $value->format("'%r%H:%I:%S.%f'"); - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($limit < 0 || $offset < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - - } elseif ($limit !== null || $offset) { - // see http://dev.mysql.com/doc/refman/5.0/en/select.html - $sql .= ' LIMIT ' . ($limit ?? '18446744073709551615') - . ($offset ? ' OFFSET ' . $offset : ''); - } - } } diff --git a/src/Dibi/Drivers/OCI8/Connection.php b/src/Dibi/Drivers/OCI8/Connection.php index 5658ac8b4..2af128e89 100644 --- a/src/Dibi/Drivers/OCI8/Connection.php +++ b/src/Dibi/Drivers/OCI8/Connection.php @@ -223,70 +223,4 @@ public function escapeBinary(string $value): string { return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested } - - - public function escapeIdentifier(string $value): string - { - // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm - return '"' . str_replace('"', '""', $value) . '"'; - } - - - public function escapeBool(bool $value): string - { - return $value ? '1' : '0'; - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $this->nativeDate - ? "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')" - : $value->format('U'); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return $this->nativeDate - ? "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')" - : $value->format('U'); - } - - - public function escapeDateInterval(\DateInterval $value): string - { - throw new Dibi\NotImplementedException; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_"); - $value = str_replace("'", "''", $value); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($limit < 0 || $offset < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - - } elseif ($offset) { - // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html - $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' - . ($limit !== null ? 'WHERE ROWNUM <= ' . ($offset + $limit) : '') - . ') WHERE "__rnum" > ' . $offset; - - } elseif ($limit !== null) { - $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit; - } - } } diff --git a/src/Dibi/Drivers/ODBC/Connection.php b/src/Dibi/Drivers/ODBC/Connection.php index 8213cff5a..b76863db8 100644 --- a/src/Dibi/Drivers/ODBC/Connection.php +++ b/src/Dibi/Drivers/ODBC/Connection.php @@ -209,61 +209,4 @@ public function escapeBinary(string $value): string { return "'" . str_replace("'", "''", $value) . "'"; } - - - public function escapeIdentifier(string $value): string - { - return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']'; - } - - - public function escapeBool(bool $value): string - { - return $value ? '1' : '0'; - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $value->format('#m/d/Y#'); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return $value->format($this->microseconds ? '#m/d/Y H:i:s.u#' : '#m/d/Y H:i:s#'); - } - - - public function escapeDateInterval(\DateInterval $value): string - { - throw new Dibi\NotImplementedException; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($offset) { - throw new Dibi\NotSupportedException('Offset is not supported by this database.'); - - } elseif ($limit < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - - } elseif ($limit !== null) { - $sql = 'SELECT TOP ' . $limit . ' * FROM (' . $sql . ') t'; - } - } } diff --git a/src/Dibi/Drivers/PDO/Connection.php b/src/Dibi/Drivers/PDO/Connection.php index 56223a5c1..7e56a9d7f 100644 --- a/src/Dibi/Drivers/PDO/Connection.php +++ b/src/Dibi/Drivers/PDO/Connection.php @@ -220,165 +220,4 @@ public function escapeBinary(string $value): string default => $this->connection->quote($value, PDO::PARAM_LOB), }; } - - - public function escapeIdentifier(string $value): string - { - return match ($this->driverName) { - 'mysql' => '`' . str_replace('`', '``', $value) . '`', - 'oci', 'pgsql' => '"' . str_replace('"', '""', $value) . '"', - 'sqlite' => '[' . strtr($value, '[]', ' ') . ']', - 'odbc', 'mssql' => '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']', - 'dblib', 'sqlsrv' => '[' . str_replace(']', ']]', $value) . ']', - default => $value, - }; - } - - - public function escapeBool(bool $value): string - { - if ($this->driverName === 'pgsql') { - return $value ? 'TRUE' : 'FALSE'; - } else { - return $value ? '1' : '0'; - } - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $value->format($this->driverName === 'odbc' ? '#m/d/Y#' : "'Y-m-d'"); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return match ($this->driverName) { - 'odbc' => $value->format('#m/d/Y H:i:s.u#'), - 'mssql', 'dblib', 'sqlsrv' => 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')', - default => $value->format("'Y-m-d H:i:s.u'"), - }; - } - - - public function escapeDateInterval(\DateInterval $value): string - { - throw new Dibi\NotImplementedException; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - switch ($this->driverName) { - case 'mysql': - $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - - case 'oci': - $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_"); - $value = str_replace("'", "''", $value); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - - case 'pgsql': - $bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off - $value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1); - $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - - case 'sqlite': - $value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\'); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; - - case 'odbc': - case 'mssql': - case 'dblib': - case 'sqlsrv': - $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - - default: - throw new Dibi\NotImplementedException; - } - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($limit < 0 || $offset < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - } - - switch ($this->driverName) { - case 'mysql': - if ($limit !== null || $offset) { - // see http://dev.mysql.com/doc/refman/5.0/en/select.html - $sql .= ' LIMIT ' . ($limit ?? '18446744073709551615') - . ($offset ? ' OFFSET ' . $offset : ''); - } - - break; - - case 'pgsql': - if ($limit !== null) { - $sql .= ' LIMIT ' . $limit; - } - - if ($offset) { - $sql .= ' OFFSET ' . $offset; - } - - break; - - case 'sqlite': - if ($limit !== null || $offset) { - $sql .= ' LIMIT ' . ($limit ?? '-1') - . ($offset ? ' OFFSET ' . $offset : ''); - } - - break; - - case 'oci': - if ($offset) { - // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html - $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' - . ($limit !== null ? 'WHERE ROWNUM <= ' . ($offset + $limit) : '') - . ') WHERE "__rnum" > ' . $offset; - - } elseif ($limit !== null) { - $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit; - } - - break; - - case 'mssql': - case 'sqlsrv': - case 'dblib': - // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx - if ($limit !== null) { - $sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit); - } elseif ($offset) { - $sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset); - } - - break; - - case 'odbc': - if ($offset) { - throw new Dibi\NotSupportedException('Offset is not supported by this database.'); - - } elseif ($limit !== null) { - $sql = 'SELECT TOP ' . $limit . ' * FROM (' . $sql . ') t'; - break; - } - // break omitted - default: - throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.'); - } - } } diff --git a/src/Dibi/Drivers/PgSQL/Connection.php b/src/Dibi/Drivers/PgSQL/Connection.php index a5645e03d..42a3ecd47 100644 --- a/src/Dibi/Drivers/PgSQL/Connection.php +++ b/src/Dibi/Drivers/PgSQL/Connection.php @@ -272,66 +272,4 @@ public function escapeBinary(string $value): string return "'" . pg_escape_bytea($this->connection, $value) . "'"; } - - - public function escapeIdentifier(string $value): string - { - // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS - return '"' . str_replace('"', '""', $value) . '"'; - } - - - public function escapeBool(bool $value): string - { - return $value ? 'TRUE' : 'FALSE'; - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $value->format("'Y-m-d'"); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return $value->format("'Y-m-d H:i:s.u'"); - } - - - public function escapeDateInterval(\DateInterval $value): string - { - throw new Dibi\NotImplementedException; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off - $value = pg_escape_string($this->connection, $value); - $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($limit < 0 || $offset < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - } - - if ($limit !== null) { - $sql .= ' LIMIT ' . $limit; - } - - if ($offset) { - $sql .= ' OFFSET ' . $offset; - } - } } diff --git a/src/Dibi/Drivers/SQLSrv/Connection.php b/src/Dibi/Drivers/SQLSrv/Connection.php index e5b3953a0..f38e297bb 100644 --- a/src/Dibi/Drivers/SQLSrv/Connection.php +++ b/src/Dibi/Drivers/SQLSrv/Connection.php @@ -204,63 +204,4 @@ public function escapeBinary(string $value): string { return '0x' . bin2hex($value); } - - - public function escapeIdentifier(string $value): string - { - // @see https://msdn.microsoft.com/en-us/library/ms176027.aspx - return '[' . str_replace(']', ']]', $value) . ']'; - } - - - public function escapeBool(bool $value): string - { - return $value ? '1' : '0'; - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $value->format("'Y-m-d'"); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return 'CONVERT(DATETIME2(7), ' . $value->format("'Y-m-d H:i:s.u'") . ')'; - } - - - public function escapeDateInterval(\DateInterval $value): string - { - throw new Dibi\NotImplementedException; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'"); - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($limit < 0 || $offset < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - - } elseif ($limit !== null) { - // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx - $sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit); - } elseif ($offset) { - // requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx - $sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset); - } - } } diff --git a/src/Dibi/Drivers/SQLite3/Connection.php b/src/Dibi/Drivers/SQLite3/Connection.php index 0391d7fa7..59693a562 100644 --- a/src/Dibi/Drivers/SQLite3/Connection.php +++ b/src/Dibi/Drivers/SQLite3/Connection.php @@ -208,61 +208,6 @@ public function escapeBinary(string $value): string } - public function escapeIdentifier(string $value): string - { - return '[' . strtr($value, '[]', ' ') . ']'; - } - - - public function escapeBool(bool $value): string - { - return $value ? '1' : '0'; - } - - - public function escapeDate(\DateTimeInterface $value): string - { - return $value->format($this->fmtDate); - } - - - public function escapeDateTime(\DateTimeInterface $value): string - { - return $value->format($this->fmtDateTime); - } - - - public function escapeDateInterval(\DateInterval $value): string - { - throw new Dibi\NotImplementedException; - } - - - /** - * Encodes string for use in a LIKE statement. - */ - public function escapeLike(string $value, int $pos): string - { - $value = addcslashes($this->connection->escapeString($value), '%_\\'); - return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'"; - } - - - /** - * Injects LIMIT/OFFSET to the SQL query. - */ - public function applyLimit(string &$sql, ?int $limit, ?int $offset): void - { - if ($limit < 0 || $offset < 0) { - throw new Dibi\NotSupportedException('Negative offset or limit.'); - - } elseif ($limit !== null || $offset) { - $sql .= ' LIMIT ' . ($limit ?? '-1') - . ($offset ? ' OFFSET ' . $offset : ''); - } - } - - /********************* user defined functions ****************d*g**/ diff --git a/src/Dibi/Translator.php b/src/Dibi/Translator.php index faa6ebffb..94270efeb 100644 --- a/src/Dibi/Translator.php +++ b/src/Dibi/Translator.php @@ -17,8 +17,9 @@ */ final class Translator { - private Connection $connection; + private readonly Connection $connection; private readonly Drivers\Connection $driver; + private readonly Drivers\Engine $engine; private int $cursor = 0; private array $args; @@ -36,6 +37,7 @@ public function __construct(Connection $connection) { $this->connection = $connection; $this->driver = $connection->getDriver(); + $this->engine = $connection->getDatabaseEngine(); $this->identifiers = new HashMap($this->delimite(...)); } @@ -144,7 +146,7 @@ public function translate(array $args): string // apply limit if ($this->limit !== null || $this->offset !== null) { - $this->driver->applyLimit($sql, $this->limit, $this->offset); + $this->engine->applyLimit($sql, $this->limit, $this->offset); } return $sql; @@ -209,7 +211,7 @@ public function formatValue(mixed $value, ?string $modifier): string case 'n': // key, key, ... identifier names foreach ($value as $k => $v) { if (is_string($k)) { - $vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->driver->escapeIdentifier($v)); + $vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->engine->escapeIdentifier($v)); } else { $pair = explode('%', $v, 2); // split into identifier & modifier $vx[] = $this->identifiers->{$pair[0]}; @@ -346,7 +348,7 @@ public function formatValue(mixed $value, ?string $modifier): string case 's': // string return $value === null ? 'NULL' - : $this->driver->escapeText((string) $value); + : $this->engine->escapeText((string) $value); case 'bin':// binary return $value === null @@ -356,7 +358,7 @@ public function formatValue(mixed $value, ?string $modifier): string case 'b': // boolean return $value === null ? 'NULL' - : $this->driver->escapeBool((bool) $value); + : $this->engine->escapeBool((bool) $value); case 'sN': // string or null case 'sn': @@ -406,15 +408,15 @@ public function formatValue(mixed $value, ?string $modifier): string } return $modifier === 'd' - ? $this->driver->escapeDate($value) - : $this->driver->escapeDateTime($value); + ? $this->engine->escapeDate($value) + : $this->engine->escapeDateTime($value); case 'by': case 'n': // composed identifier name return $this->identifiers->$value; case 'N': // identifier name - return $this->driver->escapeIdentifier($value); + return $this->engine->escapeIdentifier($value); case 'ex': case 'sql': // preserve as dibi-SQL (TODO: leave only %ex) @@ -450,16 +452,16 @@ public function formatValue(mixed $value, ?string $modifier): string return (string) $value; case 'like~': // LIKE string% - return $this->driver->escapeLike($value, 2); + return $this->engine->escapeLike($value, 2); case '~like': // LIKE %string - return $this->driver->escapeLike($value, 1); + return $this->engine->escapeLike($value, 1); case '~like~': // LIKE %string% - return $this->driver->escapeLike($value, 3); + return $this->engine->escapeLike($value, 3); case 'like': // LIKE string - return $this->driver->escapeLike($value, 0); + return $this->engine->escapeLike($value, 0); case 'and': case 'or': @@ -485,16 +487,16 @@ public function formatValue(mixed $value, ?string $modifier): string return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); } elseif (is_bool($value)) { - return $this->driver->escapeBool($value); + return $this->engine->escapeBool($value); } elseif ($value === null) { return 'NULL'; } elseif ($value instanceof \DateTimeInterface) { - return $this->driver->escapeDateTime($value); + return $this->engine->escapeDateTime($value); } elseif ($value instanceof \DateInterval) { - return $this->driver->escapeDateInterval($value); + return $this->engine->escapeDateInterval($value); } elseif ($value instanceof Literal) { return (string) $value; @@ -653,7 +655,7 @@ public function delimite(string $value): string $parts = explode('.', $value); foreach ($parts as &$v) { if ($v !== '*') { - $v = $this->driver->escapeIdentifier($v); + $v = $this->engine->escapeIdentifier($v); } } diff --git a/tests/dibi/Connection.objectTranslator.phpt b/tests/dibi/Connection.objectTranslator.phpt index 32938af26..58e3be453 100644 --- a/tests/dibi/Connection.objectTranslator.phpt +++ b/tests/dibi/Connection.objectTranslator.phpt @@ -49,7 +49,7 @@ test('DateTime', function () use ($conn) { // Without object translator, DateTime child is translated by driver Assert::same( - $conn->getDriver()->escapeDateTime($stamp), + $conn->getDatabaseEngine()->escapeDateTime($stamp), $conn->translate('?', $stamp), ); @@ -67,15 +67,15 @@ test('DateTime', function () use ($conn) { // With modifier, it is still translated by driver Assert::same( - $conn->getDriver()->escapeDateTime($stamp), + $conn->getDatabaseEngine()->escapeDateTime($stamp), $conn->translate('%dt', $stamp), ); Assert::same( - $conn->getDriver()->escapeDateTime($stamp), + $conn->getDatabaseEngine()->escapeDateTime($stamp), $conn->translate('%t', $stamp), ); Assert::same( - $conn->getDriver()->escapeDate($stamp), + $conn->getDatabaseEngine()->escapeDate($stamp), $conn->translate('%d', $stamp), ); @@ -83,7 +83,7 @@ test('DateTime', function () use ($conn) { // DateTimeImmutable as a Time parent is not affected and still translated by driver $dt = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', '2022-11-22 12:13:14'); Assert::same( - $conn->getDriver()->escapeDateTime($dt), + $conn->getDatabaseEngine()->escapeDateTime($dt), $conn->translate('?', $dt), ); From 28ba63bc2a4dcf359cbe99fa7e4c01b3b762f4cc Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 23 Nov 2024 17:59:08 +0100 Subject: [PATCH 13/15] used attribute Deprecated --- src/Dibi/Drivers/Firebird/Connection.php | 2 +- src/Dibi/Drivers/MySQLi/Connection.php | 6 +++--- src/Dibi/Fluent.php | 2 +- src/Dibi/Type.php | 18 +++++++++--------- src/Dibi/dibi.php | 3 --- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Dibi/Drivers/Firebird/Connection.php b/src/Dibi/Drivers/Firebird/Connection.php index 37f8b30ba..1cdbe0917 100644 --- a/src/Dibi/Drivers/Firebird/Connection.php +++ b/src/Dibi/Drivers/Firebird/Connection.php @@ -30,7 +30,7 @@ class Connection implements Drivers\Connection { public const ErrorExceptionThrown = -836; - /** @deprecated use FirebirdDriver::ErrorExceptionThrown */ + #[\Deprecated('use FirebirdDriver::ErrorExceptionThrown')] public const ERROR_EXCEPTION_THROWN = self::ErrorExceptionThrown; /** @var resource */ diff --git a/src/Dibi/Drivers/MySQLi/Connection.php b/src/Dibi/Drivers/MySQLi/Connection.php index 4e9988e06..3908f016a 100644 --- a/src/Dibi/Drivers/MySQLi/Connection.php +++ b/src/Dibi/Drivers/MySQLi/Connection.php @@ -39,13 +39,13 @@ class Connection implements Drivers\Connection public const ErrorDuplicateEntry = 1062; public const ErrorDataTruncated = 1265; - /** @deprecated use MySqliDriver::ErrorAccessDenied */ + #[\Deprecated('use MySqliDriver::ErrorAccessDenied')] public const ERROR_ACCESS_DENIED = self::ErrorAccessDenied; - /** @deprecated use MySqliDriver::ErrorDuplicateEntry */ + #[\Deprecated('use MySqliDriver::ErrorDuplicateEntry')] public const ERROR_DUPLICATE_ENTRY = self::ErrorDuplicateEntry; - /** @deprecated use MySqliDriver::ErrorDataTruncated */ + #[\Deprecated('use MySqliDriver::ErrorDataTruncated')] public const ERROR_DATA_TRUNCATED = self::ErrorDataTruncated; private \mysqli $connection; diff --git a/src/Dibi/Fluent.php b/src/Dibi/Fluent.php index cfb8192f4..302ef7e68 100644 --- a/src/Dibi/Fluent.php +++ b/src/Dibi/Fluent.php @@ -52,7 +52,7 @@ class Fluent implements IDataSource Identifier = 'n', Remove = false; - /** @deprecated use Fluent::Remove */ + #[\Deprecated('use Fluent::Remove')] public const REMOVE = self::Remove; public static array $masks = [ diff --git a/src/Dibi/Type.php b/src/Dibi/Type.php index da0f49dfe..d98f2d784 100644 --- a/src/Dibi/Type.php +++ b/src/Dibi/Type.php @@ -27,31 +27,31 @@ class Type Time = 't', TimeInterval = 'ti'; - /** @deprecated use Type::Text */ + #[\Deprecated('use Type::Text')] public const TEXT = self::Text; - /** @deprecated use Type::Binary */ + #[\Deprecated('use Type::Binary')] public const BINARY = self::Binary; - /** @deprecated use Type::Bool */ + #[\Deprecated('use Type::Bool')] public const BOOL = self::Bool; - /** @deprecated use Type::Integer */ + #[\Deprecated('use Type::Integer')] public const INTEGER = self::Integer; - /** @deprecated use Type::Float */ + #[\Deprecated('use Type::Float')] public const FLOAT = self::Float; - /** @deprecated use Type::Date */ + #[\Deprecated('use Type::Date')] public const DATE = self::Date; - /** @deprecated use Type::DateTime */ + #[\Deprecated('use Type::DateTime')] public const DATETIME = self::DateTime; - /** @deprecated use Type::Time */ + #[\Deprecated('use Type::Time')] public const TIME = self::Time; - /** @deprecated use Type::TimeInterval */ + #[\Deprecated('use Type::TimeInterval')] public const TIME_INTERVAL = self::TimeInterval; diff --git a/src/Dibi/dibi.php b/src/Dibi/dibi.php index c9c95c4ed..90086ead6 100644 --- a/src/Dibi/dibi.php +++ b/src/Dibi/dibi.php @@ -39,13 +39,10 @@ class dibi { public const Version = '6.0-dev'; - /** @deprecated use dibi::Version */ public const VERSION = self::Version; - /** @deprecated use Dibi\Fluent::AffectedRows */ public const AFFECTED_ROWS = Dibi\Fluent::AffectedRows; - /** @deprecated use Dibi\Fluent::Identifier */ public const IDENTIFIER = Dibi\Fluent::Identifier; /** sorting order */ From 21ebd3500e680e94f05433d34f80b8a44308df75 Mon Sep 17 00:00:00 2001 From: Ciki Date: Wed, 21 Jan 2026 16:32:05 +0100 Subject: [PATCH 14/15] cast $this to array as an argument for ArrayIterator constructor Prior to this change that line of code throws Deprecated ArrayIterator::__construct(): Using an object as a backing array for ArrayIterator is deprecated, as it allows violating class constraints and invariants --- src/Dibi/Row.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dibi/Row.php b/src/Dibi/Row.php index 9ab26fde0..e2b12095a 100644 --- a/src/Dibi/Row.php +++ b/src/Dibi/Row.php @@ -75,7 +75,7 @@ final public function count(): int final public function getIterator(): \ArrayIterator { - return new \ArrayIterator($this); + return new \ArrayIterator($this->toArray()); } From 366b82eca4608731cf592b99fb080bd2f06e8f09 Mon Sep 17 00:00:00 2001 From: Martin Lutonsky Date: Thu, 12 Feb 2026 12:43:17 +0100 Subject: [PATCH 15/15] handle PgSQL FATAL connection error --- src/Dibi/Drivers/PgSQL/Connection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dibi/Drivers/PgSQL/Connection.php b/src/Dibi/Drivers/PgSQL/Connection.php index 42a3ecd47..bbe2fee7f 100644 --- a/src/Dibi/Drivers/PgSQL/Connection.php +++ b/src/Dibi/Drivers/PgSQL/Connection.php @@ -132,8 +132,8 @@ public function query(string $sql): ?Result public static function createException(string $message, $code = null, ?string $sql = null): Dibi\DriverException { - if ($code === null && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) { - $code = $m[1]; + if ($code === null && preg_match('#^(ERROR|FATAL):\s+(\S+):\s*#', $message, $m)) { + $code = $m[2]; $message = substr($message, strlen($m[0])); }