mirror of
https://github.com/dg/dibi.git
synced 2025-08-31 09:41:43 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
86a71dde28 | ||
|
bb1f7d4b93 | ||
|
680026747e | ||
|
7ca47508cb | ||
|
beba7b3592 | ||
|
6cc7ce8e44 | ||
|
92b8e6077e | ||
|
e45638eab4 | ||
|
8564217bc1 |
@@ -17,7 +17,8 @@
|
||||
"tracy/tracy": "^2.9",
|
||||
"nette/tester": "^2.5",
|
||||
"nette/di": "^3.1",
|
||||
"phpstan/phpstan": "^1.0"
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"jetbrains/phpstorm-attributes": "^1.0"
|
||||
},
|
||||
"replace": {
|
||||
"dg/dibi": "*"
|
||||
|
@@ -639,7 +639,7 @@ In the configuration file, we will register the DI extensions and add the `dibi`
|
||||
|
||||
```neon
|
||||
extensions:
|
||||
dibi: Dibi\Bridges\Nette\DibiExtension22
|
||||
dibi: Dibi\Bridges\Nette\DibiExtension3
|
||||
|
||||
dibi:
|
||||
host: localhost
|
||||
|
96
src/Dibi/Bridges/Nette/DibiExtension3.php
Normal file
96
src/Dibi/Bridges/Nette/DibiExtension3.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Dibi, smart database abstraction layer (https://dibiphp.com)
|
||||
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dibi\Bridges\Nette;
|
||||
|
||||
use Dibi;
|
||||
use Nette;
|
||||
use Nette\Schema\Expect;
|
||||
use Tracy;
|
||||
|
||||
|
||||
/**
|
||||
* Dibi extension for Nette Framework 3. Creates 'connection' & 'panel' services.
|
||||
*/
|
||||
class DibiExtension3 extends Nette\DI\CompilerExtension
|
||||
{
|
||||
private ?bool $debugMode;
|
||||
private ?bool $cliMode;
|
||||
|
||||
|
||||
public function __construct(?bool $debugMode = null, ?bool $cliMode = null)
|
||||
{
|
||||
$this->debugMode = $debugMode;
|
||||
$this->cliMode = $cliMode;
|
||||
}
|
||||
|
||||
|
||||
public function getConfigSchema(): Nette\Schema\Schema
|
||||
{
|
||||
return Expect::structure([
|
||||
'autowired' => Expect::bool(true),
|
||||
'flags' => Expect::anyOf(Expect::arrayOf('string'), Expect::type('dynamic')),
|
||||
'profiler' => Expect::bool(),
|
||||
'explain' => Expect::bool(true),
|
||||
'filter' => Expect::bool(true),
|
||||
'driver' => Expect::string()->dynamic(),
|
||||
'name' => Expect::string()->dynamic(),
|
||||
'lazy' => Expect::bool(false)->dynamic(),
|
||||
'onConnect' => Expect::array()->dynamic(),
|
||||
'substitutes' => Expect::arrayOf('string')->dynamic(),
|
||||
'result' => Expect::structure([
|
||||
'normalize' => Expect::bool(true),
|
||||
'formatDateTime' => Expect::string(),
|
||||
'formatTimeInterval' => Expect::string(),
|
||||
'formatJson' => Expect::string(),
|
||||
]),
|
||||
])->otherItems(Expect::type('mixed'))
|
||||
->castTo('array');
|
||||
}
|
||||
|
||||
|
||||
public function loadConfiguration()
|
||||
{
|
||||
$container = $this->getContainerBuilder();
|
||||
$config = $this->getConfig();
|
||||
$this->debugMode ??= $container->parameters['debugMode'];
|
||||
$this->cliMode ??= $container->parameters['consoleMode'];
|
||||
|
||||
$useProfiler = $config['profiler'] ?? (class_exists(Tracy\Debugger::class) && $this->debugMode && !$this->cliMode);
|
||||
unset($config['profiler']);
|
||||
|
||||
if (is_array($config['flags'])) {
|
||||
$flags = 0;
|
||||
foreach ((array) $config['flags'] as $flag) {
|
||||
$flags |= constant($flag);
|
||||
}
|
||||
$config['flags'] = $flags;
|
||||
}
|
||||
|
||||
$connection = $container->addDefinition($this->prefix('connection'))
|
||||
->setCreator(Dibi\Connection::class, [$config])
|
||||
->setAutowired($config['autowired']);
|
||||
|
||||
if (class_exists(Tracy\Debugger::class)) {
|
||||
$connection->addSetup(
|
||||
[new Nette\DI\Definitions\Statement('Tracy\Debugger::getBlueScreen'), 'addPanel'],
|
||||
[[Dibi\Bridges\Tracy\Panel::class, 'renderException']],
|
||||
);
|
||||
}
|
||||
|
||||
if ($useProfiler) {
|
||||
$panel = $container->addDefinition($this->prefix('panel'))
|
||||
->setCreator(Dibi\Bridges\Tracy\Panel::class, [
|
||||
$config['explain'],
|
||||
$config['filter'] ? Dibi\Event::QUERY : Dibi\Event::ALL,
|
||||
]);
|
||||
$connection->addSetup([$panel, 'register'], [$connection]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
# This will create service named 'dibi.connection'.
|
||||
# Requires Nette Framework 2.2 or later
|
||||
# Requires Nette Framework 3 or later
|
||||
|
||||
extensions:
|
||||
dibi: Dibi\Bridges\Nette\DibiExtension22
|
||||
dibi: Dibi\Bridges\Nette\DibiExtension3
|
||||
|
||||
dibi:
|
||||
host: localhost
|
||||
|
@@ -62,7 +62,7 @@ class Panel implements Tracy\IBarPanel
|
||||
if ($e instanceof Dibi\Exception && $e->getSql()) {
|
||||
return [
|
||||
'tab' => 'SQL',
|
||||
'panel' => Helpers::dump($e->getSql(), true),
|
||||
'panel' => Helpers::dump($e->getSql(), return: true),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class Panel implements Tracy\IBarPanel
|
||||
? $this->explain
|
||||
: ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
||||
try {
|
||||
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
|
||||
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), return: true);
|
||||
} catch (Dibi\Exception $e) {
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ class Panel implements Tracy\IBarPanel
|
||||
$s .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>";
|
||||
}
|
||||
|
||||
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, true);
|
||||
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, return: true);
|
||||
if ($explain) {
|
||||
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Dibi;
|
||||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Traversable;
|
||||
|
||||
|
||||
@@ -209,7 +210,7 @@ class Connection implements IConnection
|
||||
* Generates (translates) and executes SQL query.
|
||||
* @throws Exception
|
||||
*/
|
||||
final public function query(mixed ...$args): Result
|
||||
final public function query(#[Language('GenericSQL')] mixed ...$args): Result
|
||||
{
|
||||
return $this->nativeQuery($this->translate(...$args));
|
||||
}
|
||||
@@ -219,7 +220,7 @@ class Connection implements IConnection
|
||||
* Generates SQL query.
|
||||
* @throws Exception
|
||||
*/
|
||||
final public function translate(mixed ...$args): string
|
||||
final public function translate(#[Language('GenericSQL')] mixed ...$args): string
|
||||
{
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
@@ -232,7 +233,7 @@ class Connection implements IConnection
|
||||
/**
|
||||
* Generates and prints SQL query.
|
||||
*/
|
||||
final public function test(mixed ...$args): bool
|
||||
final public function test(#[Language('GenericSQL')] mixed ...$args): bool
|
||||
{
|
||||
try {
|
||||
Helpers::dump($this->translate(...$args));
|
||||
@@ -254,7 +255,7 @@ class Connection implements IConnection
|
||||
* Generates (translates) and returns SQL query as DataSource.
|
||||
* @throws Exception
|
||||
*/
|
||||
final public function dataSource(mixed ...$args): DataSource
|
||||
final public function dataSource(#[Language('GenericSQL')] mixed ...$args): DataSource
|
||||
{
|
||||
return new DataSource($this->translate(...$args), $this);
|
||||
}
|
||||
@@ -264,7 +265,7 @@ class Connection implements IConnection
|
||||
* Executes the SQL query.
|
||||
* @throws Exception
|
||||
*/
|
||||
final public function nativeQuery(string $sql): Result
|
||||
final public function nativeQuery(#[Language('SQL')] string $sql): Result
|
||||
{
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
@@ -593,7 +594,7 @@ class Connection implements IConnection
|
||||
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetch(mixed ...$args): ?Row
|
||||
public function fetch(#[Language('GenericSQL')] mixed ...$args): ?Row
|
||||
{
|
||||
return $this->query($args)->fetch();
|
||||
}
|
||||
@@ -604,7 +605,7 @@ class Connection implements IConnection
|
||||
* @return Row[]|array[]
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetchAll(mixed ...$args): array
|
||||
public function fetchAll(#[Language('GenericSQL')] mixed ...$args): array
|
||||
{
|
||||
return $this->query($args)->fetchAll();
|
||||
}
|
||||
@@ -614,7 +615,7 @@ class Connection implements IConnection
|
||||
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetchSingle(mixed ...$args): mixed
|
||||
public function fetchSingle(#[Language('GenericSQL')] mixed ...$args): mixed
|
||||
{
|
||||
return $this->query($args)->fetchSingle();
|
||||
}
|
||||
@@ -624,7 +625,7 @@ class Connection implements IConnection
|
||||
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetchPairs(mixed ...$args): array
|
||||
public function fetchPairs(#[Language('GenericSQL')] mixed ...$args): array
|
||||
{
|
||||
return $this->query($args)->fetchPairs();
|
||||
}
|
||||
|
@@ -119,7 +119,7 @@ class OdbcDriver implements Dibi\Driver
|
||||
*/
|
||||
public function begin(?string $savepoint = null): void
|
||||
{
|
||||
if (!odbc_autocommit($this->connection, false)) {
|
||||
if (!odbc_autocommit($this->connection)) {
|
||||
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||
}
|
||||
}
|
||||
|
@@ -93,23 +93,15 @@ class PdoDriver implements Dibi\Driver
|
||||
$this->affectedRows = null;
|
||||
|
||||
[$sqlState, $code, $message] = $this->connection->errorInfo();
|
||||
$code ??= 0;
|
||||
$message = "SQLSTATE[$sqlState]: $message";
|
||||
switch ($this->driverName) {
|
||||
case 'mysql':
|
||||
throw MySqliDriver::createException($message, $code, $sql);
|
||||
|
||||
case 'oci':
|
||||
throw OracleDriver::createException($message, $code, $sql);
|
||||
|
||||
case 'pgsql':
|
||||
throw PostgreDriver::createException($message, $sqlState, $sql);
|
||||
|
||||
case 'sqlite':
|
||||
throw SqliteDriver::createException($message, $code, $sql);
|
||||
|
||||
default:
|
||||
throw new Dibi\DriverException($message, $code, $sql);
|
||||
}
|
||||
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),
|
||||
default => new Dibi\DriverException($message, $code, $sql),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +131,7 @@ class PdoDriver implements Dibi\Driver
|
||||
{
|
||||
if (!$this->connection->beginTransaction()) {
|
||||
$err = $this->connection->errorInfo();
|
||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
|
||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +144,7 @@ class PdoDriver implements Dibi\Driver
|
||||
{
|
||||
if (!$this->connection->commit()) {
|
||||
$err = $this->connection->errorInfo();
|
||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
|
||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +157,7 @@ class PdoDriver implements Dibi\Driver
|
||||
{
|
||||
if (!$this->connection->rollBack()) {
|
||||
$err = $this->connection->errorInfo();
|
||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
|
||||
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -72,13 +72,6 @@ class PostgreReflector implements Dibi\Reflector
|
||||
public function getColumns(string $table): array
|
||||
{
|
||||
$_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
|
||||
$res = $this->driver->query("
|
||||
SELECT indkey
|
||||
FROM pg_class
|
||||
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary
|
||||
WHERE pg_class.oid = $_table::regclass
|
||||
");
|
||||
$primary = (int) $res->fetch(true)['indkey'];
|
||||
|
||||
$res = $this->driver->query("
|
||||
SELECT *
|
||||
@@ -98,7 +91,8 @@ class PostgreReflector implements Dibi\Reflector
|
||||
a.atttypmod-4 AS character_maximum_length,
|
||||
NOT a.attnotnull AS is_nullable,
|
||||
a.attnum AS ordinal_position,
|
||||
pg_get_expr(adef.adbin, adef.adrelid) AS column_default
|
||||
pg_get_expr(adef.adbin, adef.adrelid) AS column_default,
|
||||
CASE WHEN a.attidentity IN ('a', 'd') THEN 'YES' ELSE 'NO' END AS is_identity
|
||||
FROM
|
||||
pg_attribute a
|
||||
JOIN pg_type ON a.atttypid = pg_type.oid
|
||||
@@ -123,7 +117,7 @@ class PostgreReflector implements Dibi\Reflector
|
||||
'size' => $size > 0 ? $size : null,
|
||||
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true,
|
||||
'default' => $row['column_default'],
|
||||
'autoincrement' => (int) $row['ordinal_position'] === $primary && str_starts_with($row['column_default'] ?? '', 'nextval'),
|
||||
'autoincrement' => $row['is_identity'] === 'YES' || str_starts_with($row['column_default'] ?? '', 'nextval('),
|
||||
'vendor' => $row,
|
||||
];
|
||||
}
|
||||
|
@@ -57,6 +57,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
|
||||
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
|
||||
$version = SQLite3::version();
|
||||
$this->connection->enableExceptions(false);
|
||||
if ($version['versionNumber'] >= '3006019') {
|
||||
$this->query('PRAGMA foreign_keys = ON');
|
||||
}
|
||||
|
@@ -279,19 +279,17 @@ class Fluent implements IDataSource
|
||||
/**
|
||||
* Generates and executes SQL query.
|
||||
* Returns result set or number of affected rows
|
||||
* @return ($return is \dibi::IDENTIFIER|\dibi::AFFECTED_ROWS ? int : Result)
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execute(?string $return = null): Result|int|null
|
||||
{
|
||||
$res = $this->query($this->_export());
|
||||
switch ($return) {
|
||||
case \dibi::IDENTIFIER:
|
||||
return $this->connection->getInsertId();
|
||||
case \dibi::AFFECTED_ROWS:
|
||||
return $this->connection->getAffectedRows();
|
||||
default:
|
||||
return $res;
|
||||
}
|
||||
return match ($return) {
|
||||
\dibi::IDENTIFIER => $this->connection->getInsertId(),
|
||||
\dibi::AFFECTED_ROWS => $this->connection->getAffectedRows(),
|
||||
default => $res,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@@ -46,7 +46,7 @@ abstract class HashMapBase
|
||||
*/
|
||||
final class HashMap extends HashMapBase
|
||||
{
|
||||
public function __set(string $nm, $val)
|
||||
public function __set(string $nm, mixed $val): void
|
||||
{
|
||||
if ($nm === '') {
|
||||
$nm = "\xFF";
|
||||
@@ -56,7 +56,7 @@ final class HashMap extends HashMapBase
|
||||
}
|
||||
|
||||
|
||||
public function __get(string $nm)
|
||||
public function __get(string $nm): mixed
|
||||
{
|
||||
if ($nm === '') {
|
||||
$nm = "\xFF";
|
||||
|
@@ -48,10 +48,11 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
}
|
||||
|
||||
|
||||
public function __get(string $key)
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
$hint = Helpers::getSuggestion(array_keys((array) $this), $key);
|
||||
trigger_error("Attempt to read missing column '$key'" . ($hint ? ", did you mean '$hint'?" : '.'), E_USER_NOTICE);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -42,7 +42,7 @@ class dibi
|
||||
IDENTIFIER = 'n';
|
||||
|
||||
/** version */
|
||||
public const VERSION = '5.0.0';
|
||||
public const VERSION = '5.0.1';
|
||||
|
||||
/** sorting order */
|
||||
public const
|
||||
|
@@ -24,13 +24,17 @@ Assert::true(isset($row['title']));
|
||||
|
||||
|
||||
// missing
|
||||
Assert::error(function () use ($row) {
|
||||
$x = $row->missing;
|
||||
}, E_USER_NOTICE, "Attempt to read missing column 'missing'.");
|
||||
Assert::error(
|
||||
fn() => $x = $row->missing,
|
||||
E_USER_NOTICE,
|
||||
"Attempt to read missing column 'missing'.",
|
||||
);
|
||||
|
||||
Assert::error(function () use ($row) {
|
||||
$x = $row['missing'];
|
||||
}, E_USER_NOTICE, "Attempt to read missing column 'missing'.");
|
||||
Assert::error(
|
||||
fn() => $x = $row['missing'],
|
||||
E_USER_NOTICE,
|
||||
"Attempt to read missing column 'missing'.",
|
||||
);
|
||||
|
||||
Assert::false(isset($row->missing));
|
||||
Assert::false(isset($row['missing']));
|
||||
@@ -41,13 +45,17 @@ Assert::same(123, $row['missing'] ?? 123);
|
||||
|
||||
|
||||
// suggestions
|
||||
Assert::error(function () use ($row) {
|
||||
$x = $row->tilte;
|
||||
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
|
||||
Assert::error(
|
||||
fn() => $x = $row->tilte,
|
||||
E_USER_NOTICE,
|
||||
"Attempt to read missing column 'tilte', did you mean 'title'?",
|
||||
);
|
||||
|
||||
Assert::error(function () use ($row) {
|
||||
$x = $row['tilte'];
|
||||
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
|
||||
Assert::error(
|
||||
fn() => $x = $row['tilte'],
|
||||
E_USER_NOTICE,
|
||||
"Attempt to read missing column 'tilte', did you mean 'title'?",
|
||||
);
|
||||
|
||||
|
||||
// to array
|
||||
|
@@ -20,7 +20,7 @@ date_default_timezone_set('Europe/Prague');
|
||||
try {
|
||||
$config = Tester\Environment::loadData();
|
||||
} catch (Throwable $e) {
|
||||
$config = parse_ini_file(__DIR__ . '/../databases.ini', true);
|
||||
$config = parse_ini_file(__DIR__ . '/../databases.ini', process_sections: true);
|
||||
$config = reset($config);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user