mirror of
https://github.com/dg/dibi.git
synced 2025-09-04 11:45:27 +02:00
Compare commits
28 Commits
translator
...
v5.0.1
Author | SHA1 | Date | |
---|---|---|---|
|
86a71dde28 | ||
|
bb1f7d4b93 | ||
|
680026747e | ||
|
7ca47508cb | ||
|
beba7b3592 | ||
|
6cc7ce8e44 | ||
|
92b8e6077e | ||
|
e45638eab4 | ||
|
8564217bc1 | ||
|
82c45c3076 | ||
|
df45bd3553 | ||
|
fe22e230ce | ||
|
8257532630 | ||
|
08dfc37492 | ||
|
7acef0c34b | ||
|
b01d97ac86 | ||
|
8915b0343c | ||
|
d1a3362321 | ||
|
b6ead80202 | ||
|
a640ac2a8f | ||
|
87e702d1fc | ||
|
cb0cf4ba2f | ||
|
8e7df8374b | ||
|
848ac76fed | ||
|
a0f2ca2fca | ||
|
cf14987b42 | ||
|
01c7ab63e3 | ||
|
520119740d |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,8 +1,8 @@
|
|||||||
.gitattributes export-ignore
|
.gitattributes export-ignore
|
||||||
.gitignore export-ignore
|
.gitignore export-ignore
|
||||||
.github export-ignore
|
.github export-ignore
|
||||||
.travis.yml export-ignore
|
appveyor.yml export-ignore
|
||||||
ecs.php export-ignore
|
ncs.* export-ignore
|
||||||
phpstan.neon export-ignore
|
phpstan.neon export-ignore
|
||||||
tests/ export-ignore
|
tests/ export-ignore
|
||||||
|
|
||||||
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php: ['8.0', '8.1', '8.2']
|
php: ['8.0', '8.1', '8.2', '8.3']
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
|
@@ -11,13 +11,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0 <8.3"
|
"php": "8.0 - 8.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"tracy/tracy": "^2.8",
|
"tracy/tracy": "^2.9",
|
||||||
"nette/tester": "^2.4",
|
"nette/tester": "^2.5",
|
||||||
"nette/di": "^3.0",
|
"nette/di": "^3.1",
|
||||||
"phpstan/phpstan": "^0.12"
|
"phpstan/phpstan": "^1.0",
|
||||||
|
"jetbrains/phpstorm-attributes": "^1.0"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
"dg/dibi": "*"
|
"dg/dibi": "*"
|
||||||
|
@@ -34,7 +34,7 @@ Install Dibi via Composer:
|
|||||||
composer require dibi/dibi
|
composer require dibi/dibi
|
||||||
```
|
```
|
||||||
|
|
||||||
The Dibi 5.0 requires PHP version 8.0 and supports PHP up to 8.2.
|
The Dibi 5.0 requires PHP version 8.0 and supports PHP up to 8.3.
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
@@ -341,7 +341,7 @@ $database->query('INSERT INTO users', [
|
|||||||
There are three methods for dealing with transactions:
|
There are three methods for dealing with transactions:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$database->beginTransaction();
|
$database->begin();
|
||||||
|
|
||||||
$database->commit();
|
$database->commit();
|
||||||
|
|
||||||
@@ -639,7 +639,7 @@ In the configuration file, we will register the DI extensions and add the `dibi`
|
|||||||
|
|
||||||
```neon
|
```neon
|
||||||
extensions:
|
extensions:
|
||||||
dibi: Dibi\Bridges\Nette\DibiExtension22
|
dibi: Dibi\Bridges\Nette\DibiExtension3
|
||||||
|
|
||||||
dibi:
|
dibi:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
@@ -20,7 +20,6 @@ use Tracy;
|
|||||||
class DibiExtension22 extends Nette\DI\CompilerExtension
|
class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||||
{
|
{
|
||||||
private ?bool $debugMode;
|
private ?bool $debugMode;
|
||||||
|
|
||||||
private ?bool $cliMode;
|
private ?bool $cliMode;
|
||||||
|
|
||||||
|
|
||||||
|
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'.
|
# This will create service named 'dibi.connection'.
|
||||||
# Requires Nette Framework 2.2 or later
|
# Requires Nette Framework 3 or later
|
||||||
|
|
||||||
extensions:
|
extensions:
|
||||||
dibi: Dibi\Bridges\Nette\DibiExtension22
|
dibi: Dibi\Bridges\Nette\DibiExtension3
|
||||||
|
|
||||||
dibi:
|
dibi:
|
||||||
host: localhost
|
host: localhost
|
||||||
|
@@ -20,15 +20,9 @@ use Tracy;
|
|||||||
*/
|
*/
|
||||||
class Panel implements Tracy\IBarPanel
|
class Panel implements Tracy\IBarPanel
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** maximum SQL length */
|
|
||||||
public static int $maxLength = 1000;
|
public static int $maxLength = 1000;
|
||||||
|
|
||||||
public bool|string $explain;
|
public bool|string $explain;
|
||||||
|
|
||||||
public int $filter;
|
public int $filter;
|
||||||
|
|
||||||
private array $events = [];
|
private array $events = [];
|
||||||
|
|
||||||
|
|
||||||
@@ -68,7 +62,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
if ($e instanceof Dibi\Exception && $e->getSql()) {
|
if ($e instanceof Dibi\Exception && $e->getSql()) {
|
||||||
return [
|
return [
|
||||||
'tab' => 'SQL',
|
'tab' => 'SQL',
|
||||||
'panel' => Helpers::dump($e->getSql(), true),
|
'panel' => Helpers::dump($e->getSql(), return: true),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +118,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
? $this->explain
|
? $this->explain
|
||||||
: ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
: ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
|
||||||
try {
|
try {
|
||||||
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
|
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), return: true);
|
||||||
} catch (Dibi\Exception $e) {
|
} catch (Dibi\Exception $e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,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 .= "<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) {
|
if ($explain) {
|
||||||
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
|
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
|
||||||
}
|
}
|
||||||
@@ -171,7 +165,7 @@ class Panel implements Tracy\IBarPanel
|
|||||||
private function getConnectionName(Dibi\Connection $connection): string
|
private function getConnectionName(Dibi\Connection $connection): string
|
||||||
{
|
{
|
||||||
$driver = $connection->getConfig('driver');
|
$driver = $connection->getConfig('driver');
|
||||||
return (is_object($driver) ? $driver::class : $driver)
|
return get_debug_type($driver)
|
||||||
. ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
|
. ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
|
||||||
. ($connection->getConfig('host') ? "\u{202f}@\u{202f}" . $connection->getConfig('host') : '');
|
. ($connection->getConfig('host') ? "\u{202f}@\u{202f}" . $connection->getConfig('host') : '');
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi;
|
namespace Dibi;
|
||||||
|
|
||||||
|
use JetBrains\PhpStorm\Language;
|
||||||
use Traversable;
|
use Traversable;
|
||||||
|
|
||||||
|
|
||||||
@@ -20,26 +21,19 @@ use Traversable;
|
|||||||
*/
|
*/
|
||||||
class Connection implements IConnection
|
class Connection implements IConnection
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
/** function (Event $event); Occurs after query is executed */
|
/** function (Event $event); Occurs after query is executed */
|
||||||
public ?array $onEvent = [];
|
public ?array $onEvent = [];
|
||||||
|
|
||||||
/** Current connection configuration */
|
|
||||||
private array $config;
|
private array $config;
|
||||||
|
|
||||||
/** @var string[] resultset formats */
|
/** @var string[] resultset formats */
|
||||||
private array $formats;
|
private array $formats;
|
||||||
|
|
||||||
private ?Driver $driver = null;
|
private ?Driver $driver = null;
|
||||||
|
|
||||||
private ?Translator $translator = null;
|
private ?Translator $translator = null;
|
||||||
|
|
||||||
/** @var array<string, callable(object): Expression> */
|
/** @var array<string, callable(object): Expression | null> */
|
||||||
private array $translators = [];
|
private array $translators = [];
|
||||||
|
private bool $sortTranslators = false;
|
||||||
private HashMap $substitutes;
|
private HashMap $substitutes;
|
||||||
|
|
||||||
private int $transactionDepth = 0;
|
private int $transactionDepth = 0;
|
||||||
|
|
||||||
|
|
||||||
@@ -216,7 +210,7 @@ class Connection implements IConnection
|
|||||||
* Generates (translates) and executes SQL query.
|
* Generates (translates) and executes SQL query.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final public function query(mixed ...$args): Result
|
final public function query(#[Language('GenericSQL')] mixed ...$args): Result
|
||||||
{
|
{
|
||||||
return $this->nativeQuery($this->translate(...$args));
|
return $this->nativeQuery($this->translate(...$args));
|
||||||
}
|
}
|
||||||
@@ -226,7 +220,7 @@ class Connection implements IConnection
|
|||||||
* Generates SQL query.
|
* Generates SQL query.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final public function translate(mixed ...$args): string
|
final public function translate(#[Language('GenericSQL')] mixed ...$args): string
|
||||||
{
|
{
|
||||||
if (!$this->driver) {
|
if (!$this->driver) {
|
||||||
$this->connect();
|
$this->connect();
|
||||||
@@ -239,7 +233,7 @@ class Connection implements IConnection
|
|||||||
/**
|
/**
|
||||||
* Generates and prints SQL query.
|
* Generates and prints SQL query.
|
||||||
*/
|
*/
|
||||||
final public function test(mixed ...$args): bool
|
final public function test(#[Language('GenericSQL')] mixed ...$args): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Helpers::dump($this->translate(...$args));
|
Helpers::dump($this->translate(...$args));
|
||||||
@@ -261,7 +255,7 @@ class Connection implements IConnection
|
|||||||
* Generates (translates) and returns SQL query as DataSource.
|
* Generates (translates) and returns SQL query as DataSource.
|
||||||
* @throws Exception
|
* @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);
|
return new DataSource($this->translate(...$args), $this);
|
||||||
}
|
}
|
||||||
@@ -271,7 +265,7 @@ class Connection implements IConnection
|
|||||||
* Executes the SQL query.
|
* Executes the SQL query.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final public function nativeQuery(string $sql): Result
|
final public function nativeQuery(#[Language('SQL')] string $sql): Result
|
||||||
{
|
{
|
||||||
if (!$this->driver) {
|
if (!$this->driver) {
|
||||||
$this->connect();
|
$this->connect();
|
||||||
@@ -528,19 +522,68 @@ class Connection implements IConnection
|
|||||||
/********************* value objects translation ****************d*g**/
|
/********************* value objects translation ****************d*g**/
|
||||||
|
|
||||||
|
|
||||||
/** @param callable(object): Expression $translator */
|
/**
|
||||||
public function addObjectTranslator(string $class, callable $translator): self
|
* @param callable(object): Expression $translator
|
||||||
|
*/
|
||||||
|
public function setObjectTranslator(callable $translator): void
|
||||||
{
|
{
|
||||||
$this->translators[$class] = $translator;
|
if (!$translator instanceof \Closure) {
|
||||||
uksort($this->translators, fn($a, $b) => class_exists($a, false) && is_subclass_of($a, $b) ? -1 : 1);
|
$translator = \Closure::fromCallable($translator);
|
||||||
return $this;
|
}
|
||||||
|
|
||||||
|
$param = (new \ReflectionFunction($translator))->getParameters()[0] ?? null;
|
||||||
|
$type = $param?->getType();
|
||||||
|
$types = match (true) {
|
||||||
|
$type instanceof \ReflectionNamedType => [$type],
|
||||||
|
$type instanceof \ReflectionUnionType => $type->getTypes(),
|
||||||
|
default => throw new Exception('Object translator must have exactly one parameter with class typehint.'),
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach ($types as $type) {
|
||||||
|
if ($type->isBuiltin() || $type->allowsNull()) {
|
||||||
|
throw new Exception("Object translator must have exactly one parameter with non-nullable class typehint, got '$type'.");
|
||||||
|
}
|
||||||
|
$this->translators[$type->getName()] = $translator;
|
||||||
|
}
|
||||||
|
$this->sortTranslators = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @return array<string, callable(object): Expression> */
|
public function translateObject(object $object): ?Expression
|
||||||
public function getObjectTranslators(): array
|
|
||||||
{
|
{
|
||||||
return $this->translators;
|
if ($this->sortTranslators) {
|
||||||
|
$this->translators = array_filter($this->translators);
|
||||||
|
uksort($this->translators, fn($a, $b) => is_subclass_of($a, $b) ? -1 : 1);
|
||||||
|
$this->sortTranslators = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!array_key_exists($object::class, $this->translators)) {
|
||||||
|
$translator = null;
|
||||||
|
foreach ($this->translators as $class => $t) {
|
||||||
|
if ($object instanceof $class) {
|
||||||
|
$translator = $t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->translators[$object::class] = $translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
$translator = $this->translators[$object::class];
|
||||||
|
if ($translator === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $translator($object);
|
||||||
|
if (!$result instanceof Expression) {
|
||||||
|
throw new Exception(sprintf(
|
||||||
|
"Object translator for class '%s' returned '%s' but %s expected.",
|
||||||
|
$object::class,
|
||||||
|
get_debug_type($result),
|
||||||
|
Expression::class,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -551,7 +594,7 @@ class Connection implements IConnection
|
|||||||
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
* Executes SQL query and fetch result - shortcut for query() & fetch().
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetch(mixed ...$args): ?Row
|
public function fetch(#[Language('GenericSQL')] mixed ...$args): ?Row
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetch();
|
return $this->query($args)->fetch();
|
||||||
}
|
}
|
||||||
@@ -562,7 +605,7 @@ class Connection implements IConnection
|
|||||||
* @return Row[]|array[]
|
* @return Row[]|array[]
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetchAll(mixed ...$args): array
|
public function fetchAll(#[Language('GenericSQL')] mixed ...$args): array
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetchAll();
|
return $this->query($args)->fetchAll();
|
||||||
}
|
}
|
||||||
@@ -572,7 +615,7 @@ class Connection implements IConnection
|
|||||||
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetchSingle(mixed ...$args): mixed
|
public function fetchSingle(#[Language('GenericSQL')] mixed ...$args): mixed
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetchSingle();
|
return $this->query($args)->fetchSingle();
|
||||||
}
|
}
|
||||||
@@ -582,7 +625,7 @@ class Connection implements IConnection
|
|||||||
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function fetchPairs(mixed ...$args): array
|
public function fetchPairs(#[Language('GenericSQL')] mixed ...$args): array
|
||||||
{
|
{
|
||||||
return $this->query($args)->fetchPairs();
|
return $this->query($args)->fetchPairs();
|
||||||
}
|
}
|
||||||
|
@@ -15,26 +15,15 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class DataSource implements IDataSource
|
class DataSource implements IDataSource
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
private Connection $connection;
|
private Connection $connection;
|
||||||
|
|
||||||
private string $sql;
|
private string $sql;
|
||||||
|
|
||||||
private ?Result $result = null;
|
private ?Result $result = null;
|
||||||
|
|
||||||
private ?int $count = null;
|
private ?int $count = null;
|
||||||
|
|
||||||
private ?int $totalCount = null;
|
private ?int $totalCount = null;
|
||||||
|
|
||||||
private array $cols = [];
|
private array $cols = [];
|
||||||
|
|
||||||
private array $sorting = [];
|
private array $sorting = [];
|
||||||
|
|
||||||
private array $conds = [];
|
private array $conds = [];
|
||||||
|
|
||||||
private ?int $offset = null;
|
private ?int $offset = null;
|
||||||
|
|
||||||
private ?int $limit = null;
|
private ?int $limit = null;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -15,8 +15,6 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class DateTime extends \DateTimeImmutable
|
class DateTime extends \DateTimeImmutable
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
public function __construct(string|int $time = 'now', ?\DateTimeZone $timezone = null)
|
public function __construct(string|int $time = 'now', ?\DateTimeZone $timezone = null)
|
||||||
{
|
{
|
||||||
$timezone = $timezone ?: new \DateTimeZone(date_default_timezone_get());
|
$timezone = $timezone ?: new \DateTimeZone(date_default_timezone_get());
|
||||||
|
@@ -17,8 +17,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class DummyDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
class DummyDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
public function disconnect(): void
|
public function disconnect(): void
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -26,8 +26,6 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class FirebirdDriver implements Dibi\Driver
|
class FirebirdDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
public const ERROR_EXCEPTION_THROWN = -836;
|
public const ERROR_EXCEPTION_THROWN = -836;
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
@@ -35,7 +33,6 @@ class FirebirdDriver implements Dibi\Driver
|
|||||||
|
|
||||||
/** @var ?resource */
|
/** @var ?resource */
|
||||||
private $transaction;
|
private $transaction;
|
||||||
|
|
||||||
private bool $inTransaction = false;
|
private bool $inTransaction = false;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,8 +17,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class FirebirdReflector implements Dibi\Reflector
|
class FirebirdReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Driver $driver;
|
private Dibi\Driver $driver;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -18,13 +18,9 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class FirebirdResult implements Dibi\ResultDriver
|
class FirebirdResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $resultSet;
|
private $resultSet;
|
||||||
|
|
||||||
private bool $autoFree = true;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param resource $resultSet
|
* @param resource $resultSet
|
||||||
@@ -35,17 +31,6 @@ class FirebirdResult implements Dibi\ResultDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically frees the resources allocated for this result set.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->autoFree && $this->getResultResource()) {
|
|
||||||
$this->free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of rows in a result set.
|
* Returns the number of rows in a result set.
|
||||||
*/
|
*/
|
||||||
@@ -104,7 +89,6 @@ class FirebirdResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function getResultResource(): mixed
|
public function getResultResource(): mixed
|
||||||
{
|
{
|
||||||
$this->autoFree = false;
|
|
||||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,8 +18,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class MySqlReflector implements Dibi\Reflector
|
class MySqlReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Driver $driver;
|
private Dibi\Driver $driver;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -32,17 +32,11 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class MySqliDriver implements Dibi\Driver
|
class MySqliDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
public const ERROR_ACCESS_DENIED = 1045;
|
public const ERROR_ACCESS_DENIED = 1045;
|
||||||
|
|
||||||
public const ERROR_DUPLICATE_ENTRY = 1062;
|
public const ERROR_DUPLICATE_ENTRY = 1062;
|
||||||
|
|
||||||
public const ERROR_DATA_TRUNCATED = 1265;
|
public const ERROR_DATA_TRUNCATED = 1265;
|
||||||
|
|
||||||
private \mysqli $connection;
|
private \mysqli $connection;
|
||||||
|
|
||||||
/** Is buffered (seekable and countable)? */
|
|
||||||
private bool $buffered = false;
|
private bool $buffered = false;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,13 +17,7 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class MySqliResult implements Dibi\ResultDriver
|
class MySqliResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private \mysqli_result $resultSet;
|
private \mysqli_result $resultSet;
|
||||||
|
|
||||||
private bool $autoFree = true;
|
|
||||||
|
|
||||||
/** Is buffered (seekable and countable)? */
|
|
||||||
private bool $buffered;
|
private bool $buffered;
|
||||||
|
|
||||||
|
|
||||||
@@ -34,17 +28,6 @@ class MySqliResult implements Dibi\ResultDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically frees the resources allocated for this result set.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->autoFree && $this->getResultResource()) {
|
|
||||||
@$this->free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of rows in a result set.
|
* Returns the number of rows in a result set.
|
||||||
*/
|
*/
|
||||||
@@ -134,7 +117,6 @@ class MySqliResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function getResultResource(): \mysqli_result
|
public function getResultResource(): \mysqli_result
|
||||||
{
|
{
|
||||||
$this->autoFree = false;
|
|
||||||
return $this->resultSet;
|
return $this->resultSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,8 +17,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class NoDataResult implements Dibi\ResultDriver
|
class NoDataResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private int $rows;
|
private int $rows;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -25,13 +25,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OdbcDriver implements Dibi\Driver
|
class OdbcDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $connection;
|
private $connection;
|
||||||
|
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
|
|
||||||
private bool $microseconds = true;
|
private bool $microseconds = true;
|
||||||
|
|
||||||
|
|
||||||
@@ -123,7 +119,7 @@ class OdbcDriver implements Dibi\Driver
|
|||||||
*/
|
*/
|
||||||
public function begin(?string $savepoint = null): void
|
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));
|
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OdbcReflector implements Dibi\Reflector
|
class OdbcReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Driver $driver;
|
private Dibi\Driver $driver;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,13 +17,8 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OdbcResult implements Dibi\ResultDriver
|
class OdbcResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $resultSet;
|
private $resultSet;
|
||||||
|
|
||||||
private bool $autoFree = true;
|
|
||||||
|
|
||||||
private int $row = 0;
|
private int $row = 0;
|
||||||
|
|
||||||
|
|
||||||
@@ -36,17 +31,6 @@ class OdbcResult implements Dibi\ResultDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically frees the resources allocated for this result set.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->autoFree && $this->getResultResource()) {
|
|
||||||
$this->free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of rows in a result set.
|
* Returns the number of rows in a result set.
|
||||||
*/
|
*/
|
||||||
@@ -127,7 +111,6 @@ class OdbcResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function getResultResource(): mixed
|
public function getResultResource(): mixed
|
||||||
{
|
{
|
||||||
$this->autoFree = false;
|
|
||||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,16 +27,10 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OracleDriver implements Dibi\Driver
|
class OracleDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $connection;
|
private $connection;
|
||||||
|
|
||||||
private bool $autocommit = true;
|
private bool $autocommit = true;
|
||||||
|
|
||||||
/** use native datetime format */
|
|
||||||
private bool $nativeDate;
|
private bool $nativeDate;
|
||||||
|
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,8 +17,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OracleReflector implements Dibi\Reflector
|
class OracleReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Driver $driver;
|
private Dibi\Driver $driver;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,13 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class OracleResult implements Dibi\ResultDriver
|
class OracleResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $resultSet;
|
private $resultSet;
|
||||||
|
|
||||||
private bool $autoFree = true;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param resource $resultSet
|
* @param resource $resultSet
|
||||||
@@ -34,17 +30,6 @@ class OracleResult implements Dibi\ResultDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically frees the resources allocated for this result set.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->autoFree && $this->getResultResource()) {
|
|
||||||
$this->free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of rows in a result set.
|
* Returns the number of rows in a result set.
|
||||||
*/
|
*/
|
||||||
@@ -110,7 +95,6 @@ class OracleResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function getResultResource(): mixed
|
public function getResultResource(): mixed
|
||||||
{
|
{
|
||||||
$this->autoFree = false;
|
|
||||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,14 +27,9 @@ use PDO;
|
|||||||
*/
|
*/
|
||||||
class PdoDriver implements Dibi\Driver
|
class PdoDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private ?PDO $connection;
|
private ?PDO $connection;
|
||||||
|
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
|
|
||||||
private string $driverName;
|
private string $driverName;
|
||||||
|
|
||||||
private string $serverVersion = '';
|
private string $serverVersion = '';
|
||||||
|
|
||||||
|
|
||||||
@@ -98,23 +93,15 @@ class PdoDriver implements Dibi\Driver
|
|||||||
$this->affectedRows = null;
|
$this->affectedRows = null;
|
||||||
|
|
||||||
[$sqlState, $code, $message] = $this->connection->errorInfo();
|
[$sqlState, $code, $message] = $this->connection->errorInfo();
|
||||||
|
$code ??= 0;
|
||||||
$message = "SQLSTATE[$sqlState]: $message";
|
$message = "SQLSTATE[$sqlState]: $message";
|
||||||
switch ($this->driverName) {
|
throw match ($this->driverName) {
|
||||||
case 'mysql':
|
'mysql' => MySqliDriver::createException($message, $code, $sql),
|
||||||
throw MySqliDriver::createException($message, $code, $sql);
|
'oci' => OracleDriver::createException($message, $code, $sql),
|
||||||
|
'pgsql' => PostgreDriver::createException($message, $sqlState, $sql),
|
||||||
case 'oci':
|
'sqlite' => SqliteDriver::createException($message, $code, $sql),
|
||||||
throw OracleDriver::createException($message, $code, $sql);
|
default => new Dibi\DriverException($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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -144,7 +131,7 @@ class PdoDriver implements Dibi\Driver
|
|||||||
{
|
{
|
||||||
if (!$this->connection->beginTransaction()) {
|
if (!$this->connection->beginTransaction()) {
|
||||||
$err = $this->connection->errorInfo();
|
$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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +144,7 @@ class PdoDriver implements Dibi\Driver
|
|||||||
{
|
{
|
||||||
if (!$this->connection->commit()) {
|
if (!$this->connection->commit()) {
|
||||||
$err = $this->connection->errorInfo();
|
$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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +157,7 @@ class PdoDriver implements Dibi\Driver
|
|||||||
{
|
{
|
||||||
if (!$this->connection->rollBack()) {
|
if (!$this->connection->rollBack()) {
|
||||||
$err = $this->connection->errorInfo();
|
$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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,10 +19,7 @@ use PDO;
|
|||||||
*/
|
*/
|
||||||
class PdoResult implements Dibi\ResultDriver
|
class PdoResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private ?\PDOStatement $resultSet;
|
private ?\PDOStatement $resultSet;
|
||||||
|
|
||||||
private string $driverName;
|
private string $driverName;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -28,11 +28,8 @@ use PgSql;
|
|||||||
*/
|
*/
|
||||||
class PostgreDriver implements Dibi\Driver
|
class PostgreDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource|PgSql\Connection */
|
/** @var resource|PgSql\Connection */
|
||||||
private $connection;
|
private $connection;
|
||||||
|
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,10 +17,7 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class PostgreReflector implements Dibi\Reflector
|
class PostgreReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Driver $driver;
|
private Dibi\Driver $driver;
|
||||||
|
|
||||||
private string $version;
|
private string $version;
|
||||||
|
|
||||||
|
|
||||||
@@ -75,13 +72,6 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
public function getColumns(string $table): array
|
public function getColumns(string $table): array
|
||||||
{
|
{
|
||||||
$_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
|
$_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("
|
$res = $this->driver->query("
|
||||||
SELECT *
|
SELECT *
|
||||||
@@ -101,7 +91,8 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
a.atttypmod-4 AS character_maximum_length,
|
a.atttypmod-4 AS character_maximum_length,
|
||||||
NOT a.attnotnull AS is_nullable,
|
NOT a.attnotnull AS is_nullable,
|
||||||
a.attnum AS ordinal_position,
|
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
|
FROM
|
||||||
pg_attribute a
|
pg_attribute a
|
||||||
JOIN pg_type ON a.atttypid = pg_type.oid
|
JOIN pg_type ON a.atttypid = pg_type.oid
|
||||||
@@ -126,7 +117,7 @@ class PostgreReflector implements Dibi\Reflector
|
|||||||
'size' => $size > 0 ? $size : null,
|
'size' => $size > 0 ? $size : null,
|
||||||
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true,
|
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true,
|
||||||
'default' => $row['column_default'],
|
'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,
|
'vendor' => $row,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -19,13 +19,9 @@ use PgSql;
|
|||||||
*/
|
*/
|
||||||
class PostgreResult implements Dibi\ResultDriver
|
class PostgreResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource|PgSql\Result */
|
/** @var resource|PgSql\Result */
|
||||||
private $resultSet;
|
private $resultSet;
|
||||||
|
|
||||||
private bool $autoFree = true;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param resource|PgSql\Result $resultSet
|
* @param resource|PgSql\Result $resultSet
|
||||||
@@ -36,17 +32,6 @@ class PostgreResult implements Dibi\ResultDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically frees the resources allocated for this result set.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->autoFree && $this->getResultResource()) {
|
|
||||||
$this->free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of rows in a result set.
|
* Returns the number of rows in a result set.
|
||||||
*/
|
*/
|
||||||
@@ -113,7 +98,6 @@ class PostgreResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function getResultResource(): mixed
|
public function getResultResource(): mixed
|
||||||
{
|
{
|
||||||
$this->autoFree = false;
|
|
||||||
return is_resource($this->resultSet) || $this->resultSet instanceof PgSql\Result
|
return is_resource($this->resultSet) || $this->resultSet instanceof PgSql\Result
|
||||||
? $this->resultSet
|
? $this->resultSet
|
||||||
: null;
|
: null;
|
||||||
|
@@ -25,12 +25,8 @@ use SQLite3;
|
|||||||
*/
|
*/
|
||||||
class SqliteDriver implements Dibi\Driver
|
class SqliteDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private SQLite3 $connection;
|
private SQLite3 $connection;
|
||||||
|
|
||||||
private string $fmtDate;
|
private string $fmtDate;
|
||||||
|
|
||||||
private string $fmtDateTime;
|
private string $fmtDateTime;
|
||||||
|
|
||||||
|
|
||||||
@@ -61,6 +57,7 @@ class SqliteDriver implements Dibi\Driver
|
|||||||
|
|
||||||
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
|
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
|
||||||
$version = SQLite3::version();
|
$version = SQLite3::version();
|
||||||
|
$this->connection->enableExceptions(false);
|
||||||
if ($version['versionNumber'] >= '3006019') {
|
if ($version['versionNumber'] >= '3006019') {
|
||||||
$this->query('PRAGMA foreign_keys = ON');
|
$this->query('PRAGMA foreign_keys = ON');
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class SqliteReflector implements Dibi\Reflector
|
class SqliteReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Driver $driver;
|
private Dibi\Driver $driver;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -18,12 +18,8 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class SqliteResult implements Dibi\ResultDriver
|
class SqliteResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private \SQLite3Result $resultSet;
|
private \SQLite3Result $resultSet;
|
||||||
|
|
||||||
private bool $autoFree = true;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct(\SQLite3Result $resultSet)
|
public function __construct(\SQLite3Result $resultSet)
|
||||||
{
|
{
|
||||||
@@ -31,17 +27,6 @@ class SqliteResult implements Dibi\ResultDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically frees the resources allocated for this result set.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->autoFree && $this->getResultResource()) {
|
|
||||||
@$this->free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of rows in a result set.
|
* Returns the number of rows in a result set.
|
||||||
* @throws Dibi\NotSupportedException
|
* @throws Dibi\NotSupportedException
|
||||||
@@ -107,7 +92,6 @@ class SqliteResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function getResultResource(): \SQLite3Result
|
public function getResultResource(): \SQLite3Result
|
||||||
{
|
{
|
||||||
$this->autoFree = false;
|
|
||||||
return $this->resultSet;
|
return $this->resultSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,13 +27,9 @@ use Dibi\Helpers;
|
|||||||
*/
|
*/
|
||||||
class SqlsrvDriver implements Dibi\Driver
|
class SqlsrvDriver implements Dibi\Driver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $connection;
|
private $connection;
|
||||||
|
|
||||||
private ?int $affectedRows;
|
private ?int $affectedRows;
|
||||||
|
|
||||||
private string $version = '';
|
private string $version = '';
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,8 +17,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class SqlsrvReflector implements Dibi\Reflector
|
class SqlsrvReflector implements Dibi\Reflector
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Driver $driver;
|
private Dibi\Driver $driver;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,13 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class SqlsrvResult implements Dibi\ResultDriver
|
class SqlsrvResult implements Dibi\ResultDriver
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var resource */
|
/** @var resource */
|
||||||
private $resultSet;
|
private $resultSet;
|
||||||
|
|
||||||
private bool $autoFree = true;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param resource $resultSet
|
* @param resource $resultSet
|
||||||
@@ -34,17 +30,6 @@ class SqlsrvResult implements Dibi\ResultDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically frees the resources allocated for this result set.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->autoFree && $this->getResultResource()) {
|
|
||||||
$this->free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of rows in a result set.
|
* Returns the number of rows in a result set.
|
||||||
*/
|
*/
|
||||||
@@ -106,7 +91,6 @@ class SqlsrvResult implements Dibi\ResultDriver
|
|||||||
*/
|
*/
|
||||||
public function getResultResource(): mixed
|
public function getResultResource(): mixed
|
||||||
{
|
{
|
||||||
$this->autoFree = false;
|
|
||||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,8 +15,6 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Event
|
class Event
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
/** event type */
|
/** event type */
|
||||||
public const
|
public const
|
||||||
CONNECT = 1,
|
CONNECT = 1,
|
||||||
@@ -32,17 +30,11 @@ class Event
|
|||||||
ALL = 1023;
|
ALL = 1023;
|
||||||
|
|
||||||
public Connection $connection;
|
public Connection $connection;
|
||||||
|
|
||||||
public int $type;
|
public int $type;
|
||||||
|
|
||||||
public string $sql;
|
public string $sql;
|
||||||
|
|
||||||
public Result|DriverException|null $result;
|
public Result|DriverException|null $result;
|
||||||
|
|
||||||
public float $time;
|
public float $time;
|
||||||
|
|
||||||
public ?int $count = null;
|
public ?int $count = null;
|
||||||
|
|
||||||
public ?array $source = null;
|
public ?array $source = null;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -15,8 +15,6 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Expression
|
class Expression
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
private array $values;
|
private array $values;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -27,6 +27,8 @@ namespace Dibi;
|
|||||||
* @method Fluent innerJoin(...$table)
|
* @method Fluent innerJoin(...$table)
|
||||||
* @method Fluent rightJoin(...$table)
|
* @method Fluent rightJoin(...$table)
|
||||||
* @method Fluent outerJoin(...$table)
|
* @method Fluent outerJoin(...$table)
|
||||||
|
* @method Fluent union(Fluent $fluent)
|
||||||
|
* @method Fluent unionAll(Fluent $fluent)
|
||||||
* @method Fluent as(...$field)
|
* @method Fluent as(...$field)
|
||||||
* @method Fluent on(...$cond)
|
* @method Fluent on(...$cond)
|
||||||
* @method Fluent and(...$cond)
|
* @method Fluent and(...$cond)
|
||||||
@@ -43,8 +45,6 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Fluent implements IDataSource
|
class Fluent implements IDataSource
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
public const REMOVE = false;
|
public const REMOVE = false;
|
||||||
|
|
||||||
public static array $masks = [
|
public static array $masks = [
|
||||||
@@ -92,15 +92,10 @@ class Fluent implements IDataSource
|
|||||||
];
|
];
|
||||||
|
|
||||||
private Connection $connection;
|
private Connection $connection;
|
||||||
|
|
||||||
private array $setups = [];
|
private array $setups = [];
|
||||||
|
|
||||||
private ?string $command = null;
|
private ?string $command = null;
|
||||||
|
|
||||||
private array $clauses = [];
|
private array $clauses = [];
|
||||||
|
|
||||||
private array $flags = [];
|
private array $flags = [];
|
||||||
|
|
||||||
private $cursor;
|
private $cursor;
|
||||||
|
|
||||||
/** normalized clauses */
|
/** normalized clauses */
|
||||||
@@ -284,19 +279,17 @@ class Fluent implements IDataSource
|
|||||||
/**
|
/**
|
||||||
* Generates and executes SQL query.
|
* Generates and executes SQL query.
|
||||||
* Returns result set or number of affected rows
|
* Returns result set or number of affected rows
|
||||||
|
* @return ($return is \dibi::IDENTIFIER|\dibi::AFFECTED_ROWS ? int : Result)
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function execute(?string $return = null): Result|int|null
|
public function execute(?string $return = null): Result|int|null
|
||||||
{
|
{
|
||||||
$res = $this->query($this->_export());
|
$res = $this->query($this->_export());
|
||||||
switch ($return) {
|
return match ($return) {
|
||||||
case \dibi::IDENTIFIER:
|
\dibi::IDENTIFIER => $this->connection->getInsertId(),
|
||||||
return $this->connection->getInsertId();
|
\dibi::AFFECTED_ROWS => $this->connection->getAffectedRows(),
|
||||||
case \dibi::AFFECTED_ROWS:
|
default => $res,
|
||||||
return $this->connection->getAffectedRows();
|
};
|
||||||
default:
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -46,7 +46,7 @@ abstract class HashMapBase
|
|||||||
*/
|
*/
|
||||||
final class HashMap extends HashMapBase
|
final class HashMap extends HashMapBase
|
||||||
{
|
{
|
||||||
public function __set(string $nm, $val)
|
public function __set(string $nm, mixed $val): void
|
||||||
{
|
{
|
||||||
if ($nm === '') {
|
if ($nm === '') {
|
||||||
$nm = "\xFF";
|
$nm = "\xFF";
|
||||||
@@ -56,7 +56,7 @@ final class HashMap extends HashMapBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function __get(string $nm)
|
public function __get(string $nm): mixed
|
||||||
{
|
{
|
||||||
if ($nm === '') {
|
if ($nm === '') {
|
||||||
$nm = "\xFF";
|
$nm = "\xFF";
|
||||||
|
@@ -12,8 +12,6 @@ namespace Dibi;
|
|||||||
|
|
||||||
class Helpers
|
class Helpers
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
private static HashMap $types;
|
private static HashMap $types;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -15,8 +15,6 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Literal
|
class Literal
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
private string $value;
|
private string $value;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,13 +17,9 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class FileLogger
|
class FileLogger
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** Name of the file where SQL errors should be logged */
|
/** Name of the file where SQL errors should be logged */
|
||||||
public string $file;
|
public string $file;
|
||||||
|
|
||||||
public int $filter;
|
public int $filter;
|
||||||
|
|
||||||
private bool $errorsOnly;
|
private bool $errorsOnly;
|
||||||
|
|
||||||
|
|
||||||
@@ -74,7 +70,7 @@ class FileLogger
|
|||||||
{
|
{
|
||||||
$driver = $event->connection->getConfig('driver');
|
$driver = $event->connection->getConfig('driver');
|
||||||
$message .=
|
$message .=
|
||||||
"\n-- driver: " . (is_object($driver) ? $driver::class : $driver) . '/' . $event->connection->getConfig('name')
|
"\n-- driver: " . get_debug_type($driver) . '/' . $event->connection->getConfig('name')
|
||||||
. "\n-- " . date('Y-m-d H:i:s')
|
. "\n-- " . date('Y-m-d H:i:s')
|
||||||
. "\n\n";
|
. "\n\n";
|
||||||
file_put_contents($this->file, $message, FILE_APPEND | LOCK_EX);
|
file_put_contents($this->file, $message, FILE_APPEND | LOCK_EX);
|
||||||
|
@@ -27,8 +27,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Column
|
class Column
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** when created by Result */
|
/** when created by Result */
|
||||||
private ?Dibi\Reflector $reflector;
|
private ?Dibi\Reflector $reflector;
|
||||||
|
|
||||||
|
@@ -21,10 +21,7 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Database
|
class Database
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Reflector $reflector;
|
private Dibi\Reflector $reflector;
|
||||||
|
|
||||||
private ?string $name;
|
private ?string $name;
|
||||||
|
|
||||||
/** @var Table[] */
|
/** @var Table[] */
|
||||||
|
@@ -9,7 +9,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi\Reflection;
|
namespace Dibi\Reflection;
|
||||||
|
|
||||||
use Dibi;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,8 +19,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class ForeignKey
|
class ForeignKey
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private string $name;
|
private string $name;
|
||||||
|
|
||||||
/** @var array of [local, foreign, onDelete, onUpdate] */
|
/** @var array of [local, foreign, onDelete, onUpdate] */
|
||||||
|
@@ -9,7 +9,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Dibi\Reflection;
|
namespace Dibi\Reflection;
|
||||||
|
|
||||||
use Dibi;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,8 +21,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Index
|
class Index
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
/** @var array (name, columns, [unique], [primary]) */
|
/** @var array (name, columns, [unique], [primary]) */
|
||||||
private array $info;
|
private array $info;
|
||||||
|
|
||||||
|
@@ -20,8 +20,6 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Result
|
class Result
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\ResultDriver $driver;
|
private Dibi\ResultDriver $driver;
|
||||||
|
|
||||||
/** @var Column[]|null */
|
/** @var Column[]|null */
|
||||||
|
@@ -25,12 +25,8 @@ use Dibi;
|
|||||||
*/
|
*/
|
||||||
class Table
|
class Table
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
private Dibi\Reflector $reflector;
|
private Dibi\Reflector $reflector;
|
||||||
|
|
||||||
private string $name;
|
private string $name;
|
||||||
|
|
||||||
private bool $view;
|
private bool $view;
|
||||||
|
|
||||||
/** @var Column[] */
|
/** @var Column[] */
|
||||||
@@ -41,7 +37,6 @@ class Table
|
|||||||
|
|
||||||
/** @var Index[] */
|
/** @var Index[] */
|
||||||
private array $indexes;
|
private array $indexes;
|
||||||
|
|
||||||
private ?Index $primaryKey;
|
private ?Index $primaryKey;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,13 +17,10 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class Result implements IDataSource
|
class Result implements IDataSource
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
private ?ResultDriver $driver;
|
private ?ResultDriver $driver;
|
||||||
|
|
||||||
/** Translate table */
|
/** Translate table */
|
||||||
private array $types = [];
|
private array $types = [];
|
||||||
|
|
||||||
private ?Reflection\Result $meta;
|
private ?Reflection\Result $meta;
|
||||||
|
|
||||||
/** Already fetched? Used for allowance for first seek(0) */
|
/** Already fetched? Used for allowance for first seek(0) */
|
||||||
@@ -34,7 +31,6 @@ class Result implements IDataSource
|
|||||||
|
|
||||||
/** @var callable|null returned object factory */
|
/** @var callable|null returned object factory */
|
||||||
private $rowFactory;
|
private $rowFactory;
|
||||||
|
|
||||||
private array $formats = [];
|
private array $formats = [];
|
||||||
|
|
||||||
|
|
||||||
|
@@ -15,12 +15,8 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
class ResultIterator implements \Iterator, \Countable
|
class ResultIterator implements \Iterator, \Countable
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
private Result $result;
|
private Result $result;
|
||||||
|
|
||||||
private mixed $row;
|
private mixed $row;
|
||||||
|
|
||||||
private int $pointer = 0;
|
private int $pointer = 0;
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +40,7 @@ class ResultIterator implements \Iterator, \Countable
|
|||||||
/**
|
/**
|
||||||
* Returns the key of the current element.
|
* Returns the key of the current element.
|
||||||
*/
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
public function key(): mixed
|
public function key(): mixed
|
||||||
{
|
{
|
||||||
return $this->pointer;
|
return $this->pointer;
|
||||||
@@ -53,6 +50,7 @@ class ResultIterator implements \Iterator, \Countable
|
|||||||
/**
|
/**
|
||||||
* Returns the current element.
|
* Returns the current element.
|
||||||
*/
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
public function current(): mixed
|
public function current(): mixed
|
||||||
{
|
{
|
||||||
return $this->row;
|
return $this->row;
|
||||||
|
@@ -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);
|
$hint = Helpers::getSuggestion(array_keys((array) $this), $key);
|
||||||
trigger_error("Attempt to read missing column '$key'" . ($hint ? ", did you mean '$hint'?" : '.'), E_USER_NOTICE);
|
trigger_error("Attempt to read missing column '$key'" . ($hint ? ", did you mean '$hint'?" : '.'), E_USER_NOTICE);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,112 +0,0 @@
|
|||||||
<?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;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Better OOP experience.
|
|
||||||
*/
|
|
||||||
trait Strict
|
|
||||||
{
|
|
||||||
/** @var array [method => [type => callback]] */
|
|
||||||
private static array $extMethods;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call to undefined method.
|
|
||||||
* @throws \LogicException
|
|
||||||
*/
|
|
||||||
public function __call(string $name, array $args)
|
|
||||||
{
|
|
||||||
$class = method_exists($this, $name) ? 'parent' : static::class;
|
|
||||||
$items = (new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC);
|
|
||||||
$items = array_map(fn($item) => $item->getName(), $items);
|
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $t()?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Call to undefined method $class::$name()$hint");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call to undefined static method.
|
|
||||||
* @throws \LogicException
|
|
||||||
*/
|
|
||||||
public static function __callStatic(string $name, array $args)
|
|
||||||
{
|
|
||||||
$rc = new ReflectionClass(static::class);
|
|
||||||
$items = array_filter($rc->getMethods(\ReflectionMethod::IS_STATIC), fn($m) => $m->isPublic());
|
|
||||||
$items = array_map(fn($item) => $item->getName(), $items);
|
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $t()?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Call to undefined static method {$rc->getName()}::$name()$hint");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access to undeclared property.
|
|
||||||
* @throws \LogicException
|
|
||||||
*/
|
|
||||||
public function &__get(string $name)
|
|
||||||
{
|
|
||||||
if ((method_exists($this, $m = 'get' . $name) || method_exists($this, $m = 'is' . $name))
|
|
||||||
&& (new ReflectionMethod($this, $m))->isPublic()
|
|
||||||
) { // back compatiblity
|
|
||||||
$ret = $this->$m();
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rc = new ReflectionClass($this);
|
|
||||||
$items = array_filter($rc->getProperties(ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic());
|
|
||||||
$items = array_map(fn($item) => $item->getName(), $items);
|
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $$t?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Attempt to read undeclared property {$rc->getName()}::$$name$hint");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access to undeclared property.
|
|
||||||
* @throws \LogicException
|
|
||||||
*/
|
|
||||||
public function __set(string $name, $value)
|
|
||||||
{
|
|
||||||
$rc = new ReflectionClass($this);
|
|
||||||
$items = array_filter($rc->getProperties(ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic());
|
|
||||||
$items = array_map(fn($item) => $item->getName(), $items);
|
|
||||||
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
||||||
? ", did you mean $$t?"
|
|
||||||
: '.';
|
|
||||||
throw new \LogicException("Attempt to write to undeclared property {$rc->getName()}::$$name$hint");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function __isset(string $name): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access to undeclared property.
|
|
||||||
* @throws \LogicException
|
|
||||||
*/
|
|
||||||
public function __unset(string $name)
|
|
||||||
{
|
|
||||||
$class = static::class;
|
|
||||||
throw new \LogicException("Attempt to unset undeclared property $class::$$name.");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -15,29 +15,18 @@ namespace Dibi;
|
|||||||
*/
|
*/
|
||||||
final class Translator
|
final class Translator
|
||||||
{
|
{
|
||||||
use Strict;
|
|
||||||
|
|
||||||
private Connection $connection;
|
private Connection $connection;
|
||||||
|
|
||||||
private Driver $driver;
|
private Driver $driver;
|
||||||
|
|
||||||
private int $cursor = 0;
|
private int $cursor = 0;
|
||||||
|
|
||||||
private array $args;
|
private array $args;
|
||||||
|
|
||||||
/** @var string[] */
|
/** @var string[] */
|
||||||
private array $errors;
|
private array $errors;
|
||||||
|
|
||||||
private bool $comment = false;
|
private bool $comment = false;
|
||||||
|
|
||||||
private int $ifLevel = 0;
|
private int $ifLevel = 0;
|
||||||
|
|
||||||
private int $ifLevelStart = 0;
|
private int $ifLevelStart = 0;
|
||||||
|
|
||||||
private ?int $limit = null;
|
private ?int $limit = null;
|
||||||
|
|
||||||
private ?int $offset = null;
|
private ?int $offset = null;
|
||||||
|
|
||||||
private HashMap $identifiers;
|
private HashMap $identifiers;
|
||||||
|
|
||||||
|
|
||||||
@@ -268,7 +257,7 @@ final class Translator
|
|||||||
$proto = array_keys($v);
|
$proto = array_keys($v);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return $this->errors[] = '**Unexpected type ' . (is_object($v) ? $v::class : gettype($v)) . '**';
|
return $this->errors[] = '**Unexpected type ' . get_debug_type($v) . '**';
|
||||||
}
|
}
|
||||||
|
|
||||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
$pair = explode('%', $k, 2); // split into identifier & modifier
|
||||||
@@ -318,13 +307,9 @@ final class Translator
|
|||||||
&& $modifier === null
|
&& $modifier === null
|
||||||
&& !$value instanceof Literal
|
&& !$value instanceof Literal
|
||||||
&& !$value instanceof Expression
|
&& !$value instanceof Expression
|
||||||
|
&& $result = $this->connection->translateObject($value)
|
||||||
) {
|
) {
|
||||||
foreach ($this->connection->getObjectTranslators() as $class => $translator) {
|
return $this->connection->translate(...$result->getValues());
|
||||||
if ($value instanceof $class) {
|
|
||||||
$value = $translator($value);
|
|
||||||
return $this->connection->translate(...$value->getValues());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// object-to-scalar procession
|
// object-to-scalar procession
|
||||||
@@ -350,7 +335,7 @@ final class Translator
|
|||||||
) {
|
) {
|
||||||
// continue
|
// continue
|
||||||
} else {
|
} else {
|
||||||
$type = is_object($value) ? $value::class : gettype($value);
|
$type = get_debug_type($value);
|
||||||
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
|
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,18 +423,17 @@ final class Translator
|
|||||||
$value = substr($value, 0, $toSkip)
|
$value = substr($value, 0, $toSkip)
|
||||||
. preg_replace_callback(
|
. preg_replace_callback(
|
||||||
<<<'XX'
|
<<<'XX'
|
||||||
/
|
/
|
||||||
(?=[`['":])
|
(?=[`['":])
|
||||||
(?:
|
(?:
|
||||||
`(.+?)`|
|
`(.+?)`|
|
||||||
\[(.+?)\]|
|
\[(.+?)]|
|
||||||
(')((?:''|[^'])*)'|
|
(')((?:''|[^'])*)'|
|
||||||
(")((?:""|[^"])*)"|
|
(")((?:""|[^"])*)"|
|
||||||
('|")|
|
(['"])|
|
||||||
:(\S*?:)([a-zA-Z0-9._]?)
|
:(\S*?:)([a-zA-Z0-9._]?)
|
||||||
)/sx
|
)/sx
|
||||||
XX
|
XX,
|
||||||
,
|
|
||||||
[$this, 'cb'],
|
[$this, 'cb'],
|
||||||
substr($value, $toSkip),
|
substr($value, $toSkip),
|
||||||
);
|
);
|
||||||
@@ -517,7 +501,7 @@ final class Translator
|
|||||||
return $this->connection->translate(...$value->getValues());
|
return $this->connection->translate(...$value->getValues());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$type = is_object($value) ? $value::class : gettype($value);
|
$type = get_debug_type($value);
|
||||||
return $this->errors[] = "**Unexpected $type**";
|
return $this->errors[] = "**Unexpected $type**";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,14 +37,12 @@ declare(strict_types=1);
|
|||||||
*/
|
*/
|
||||||
class dibi
|
class dibi
|
||||||
{
|
{
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
public const
|
public const
|
||||||
AFFECTED_ROWS = 'a',
|
AFFECTED_ROWS = 'a',
|
||||||
IDENTIFIER = 'n';
|
IDENTIFIER = 'n';
|
||||||
|
|
||||||
/** version */
|
/** version */
|
||||||
public const VERSION = '5.0-dev';
|
public const VERSION = '5.0.1';
|
||||||
|
|
||||||
/** sorting order */
|
/** sorting order */
|
||||||
public const
|
public const
|
||||||
|
@@ -73,24 +73,29 @@ test('', function () use ($config) {
|
|||||||
|
|
||||||
|
|
||||||
test('', function () use ($config) {
|
test('', function () use ($config) {
|
||||||
Assert::exception(function () use ($config) {
|
Assert::exception(
|
||||||
new Connection($config + ['onConnect' => '']);
|
fn() => new Connection($config + ['onConnect' => '']),
|
||||||
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");
|
InvalidArgumentException::class,
|
||||||
|
"Configuration option 'onConnect' must be array.",
|
||||||
|
);
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($config) {
|
$e = Assert::exception(
|
||||||
new Connection($config + ['onConnect' => ['STOP']]);
|
fn() => new Connection($config + ['onConnect' => ['STOP']]),
|
||||||
}, Dibi\DriverException::class);
|
Dibi\DriverException::class,
|
||||||
|
);
|
||||||
Assert::same('STOP', $e->getSql());
|
Assert::same('STOP', $e->getSql());
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($config) {
|
$e = Assert::exception(
|
||||||
new Connection($config + ['onConnect' => [['STOP %i', 123]]]);
|
fn() => new Connection($config + ['onConnect' => [['STOP %i', 123]]]),
|
||||||
}, Dibi\DriverException::class);
|
Dibi\DriverException::class,
|
||||||
|
);
|
||||||
Assert::same('STOP 123', $e->getSql());
|
Assert::same('STOP 123', $e->getSql());
|
||||||
|
|
||||||
// lazy
|
// lazy
|
||||||
$conn = new Connection($config + ['lazy' => true, 'onConnect' => ['STOP']]);
|
$conn = new Connection($config + ['lazy' => true, 'onConnect' => ['STOP']]);
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('SELECT 1');
|
fn() => $conn->query('SELECT 1'),
|
||||||
}, Dibi\DriverException::class);
|
Dibi\DriverException::class,
|
||||||
|
);
|
||||||
Assert::same('STOP', $e->getSql());
|
Assert::same('STOP', $e->getSql());
|
||||||
});
|
});
|
||||||
|
@@ -24,14 +24,16 @@ class Time extends DateTimeImmutable
|
|||||||
|
|
||||||
|
|
||||||
test('Without object translator', function () use ($conn) {
|
test('Without object translator', function () use ($conn) {
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->translate('?', new Email);
|
fn() => $conn->translate('?', new Email),
|
||||||
}, Dibi\Exception::class, 'SQL translate error: Unexpected Email');
|
Dibi\Exception::class,
|
||||||
|
'SQL translate error: Unexpected Email',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('Basics', function () use ($conn) {
|
test('Basics', function () use ($conn) {
|
||||||
$conn->addObjectTranslator(Email::class, fn($object) => new Dibi\Expression('?', $object->address));
|
$conn->setObjectTranslator(fn(Email $email) => new Dibi\Expression('?', $email->address));
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat([
|
reformat([
|
||||||
'sqlsrv' => "N'address@example.com'",
|
'sqlsrv' => "N'address@example.com'",
|
||||||
@@ -53,7 +55,7 @@ test('DateTime', function () use ($conn) {
|
|||||||
|
|
||||||
|
|
||||||
// With object translator
|
// With object translator
|
||||||
$conn->addObjectTranslator(Time::class, fn($object) => new Dibi\Expression('OwnTime(?)', $object->format('H:i:s')));
|
$conn->setObjectTranslator(fn(Time $time) => new Dibi\Expression('OwnTime(?)', $time->format('H:i:s')));
|
||||||
Assert::same(
|
Assert::same(
|
||||||
reformat([
|
reformat([
|
||||||
'sqlsrv' => "OwnTime(N'12:13:14')",
|
'sqlsrv' => "OwnTime(N'12:13:14')",
|
||||||
@@ -86,7 +88,7 @@ test('DateTime', function () use ($conn) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// But DateTime translation can be overloaded
|
// But DateTime translation can be overloaded
|
||||||
$conn->addObjectTranslator(DateTimeInterface::class, fn() => new Dibi\Expression('OwnDateTime'));
|
$conn->setObjectTranslator(fn(DateTimeInterface $dt) => new Dibi\Expression('OwnDateTime'));
|
||||||
Assert::same(
|
Assert::same(
|
||||||
'OwnDateTime',
|
'OwnDateTime',
|
||||||
$conn->translate('?', $dt),
|
$conn->translate('?', $dt),
|
||||||
@@ -95,9 +97,9 @@ test('DateTime', function () use ($conn) {
|
|||||||
|
|
||||||
|
|
||||||
test('Complex structures', function () use ($conn) {
|
test('Complex structures', function () use ($conn) {
|
||||||
$conn->addObjectTranslator(Email::class, fn($object) => new Dibi\Expression('?', $object->address));
|
$conn->setObjectTranslator(fn(Email $email) => new Dibi\Expression('?', $email->address));
|
||||||
$conn->addObjectTranslator(Time::class, fn($object) => new Dibi\Expression('OwnTime(?)', $object->format('H:i:s')));
|
$conn->setObjectTranslator(fn(Time $time) => new Dibi\Expression('OwnTime(?)', $time->format('H:i:s')));
|
||||||
$conn->addObjectTranslator(Time::class, fn($object) => new Dibi\Expression('OwnTime(?)', $object->format('H:i:s')));
|
$conn->setObjectTranslator(fn(DateTimeInterface $dt) => new Dibi\Expression('OwnDateTime'));
|
||||||
|
|
||||||
$time = Time::createFromFormat('Y-m-d H:i:s', '2022-11-22 12:13:14');
|
$time = Time::createFromFormat('Y-m-d H:i:s', '2022-11-22 12:13:14');
|
||||||
Assert::same(
|
Assert::same(
|
||||||
@@ -117,3 +119,37 @@ test('Complex structures', function () use ($conn) {
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('Invalid translator', function () use ($conn) {
|
||||||
|
Assert::exception(
|
||||||
|
fn() => $conn->setObjectTranslator(fn($email) => 'foo'),
|
||||||
|
Dibi\Exception::class,
|
||||||
|
'Object translator must have exactly one parameter with class typehint.',
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert::exception(
|
||||||
|
fn() => $conn->setObjectTranslator(fn(string $email) => 'foo'),
|
||||||
|
Dibi\Exception::class,
|
||||||
|
"Object translator must have exactly one parameter with non-nullable class typehint, got 'string'.",
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert::exception(
|
||||||
|
fn() => $conn->setObjectTranslator(fn(Email|bool $email) => 'foo'),
|
||||||
|
Dibi\Exception::class,
|
||||||
|
"Object translator must have exactly one parameter with non-nullable class typehint, got 'bool'.",
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert::exception(
|
||||||
|
fn() => $conn->setObjectTranslator(fn(Email|null $email) => 'foo'),
|
||||||
|
Dibi\Exception::class,
|
||||||
|
"Object translator must have exactly one parameter with non-nullable class typehint, got '?Email'.",
|
||||||
|
);
|
||||||
|
|
||||||
|
$conn->setObjectTranslator(fn(Email $email) => 'foo');
|
||||||
|
Assert::exception(
|
||||||
|
fn() => $conn->translate('?', new Email),
|
||||||
|
Dibi\Exception::class,
|
||||||
|
"Object translator for class 'Email' returned 'string' but Dibi\\Expression expected.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
@@ -15,18 +15,22 @@ $conn = new Dibi\Connection($config);
|
|||||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||||
|
|
||||||
|
|
||||||
/*Assert::exception(function () use ($conn) {
|
/*
|
||||||
$conn->rollback();
|
Assert::exception(
|
||||||
}, Dibi\Exception::class);
|
fn() => $conn->rollback(),
|
||||||
|
Dibi\Exception::class,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->commit();
|
fn() => $conn->commit(),
|
||||||
}, Dibi\Exception::class);
|
Dibi\Exception::class,
|
||||||
|
);
|
||||||
|
|
||||||
$conn->begin();
|
$conn->begin();
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->begin();
|
fn() => $conn->begin(),
|
||||||
}, Dibi\Exception::class);
|
Dibi\Exception::class,
|
||||||
|
);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -53,14 +57,16 @@ test('begin() & commit()', function () use ($conn) {
|
|||||||
|
|
||||||
|
|
||||||
test('transaction() fail', function () use ($conn) {
|
test('transaction() fail', function () use ($conn) {
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||||
$connection->query('INSERT INTO [products]', [
|
$connection->query('INSERT INTO [products]', [
|
||||||
'title' => 'Test product',
|
'title' => 'Test product',
|
||||||
]);
|
]);
|
||||||
throw new Exception('my exception');
|
throw new Exception('my exception');
|
||||||
});
|
}),
|
||||||
}, Throwable::class, 'my exception');
|
Throwable::class,
|
||||||
|
'my exception',
|
||||||
|
);
|
||||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,8 +82,8 @@ test('transaction() success', function () use ($conn) {
|
|||||||
|
|
||||||
|
|
||||||
test('nested transaction() call fail', function () use ($conn) {
|
test('nested transaction() call fail', function () use ($conn) {
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||||
$connection->query('INSERT INTO [products]', [
|
$connection->query('INSERT INTO [products]', [
|
||||||
'title' => 'Test product',
|
'title' => 'Test product',
|
||||||
]);
|
]);
|
||||||
@@ -88,8 +94,10 @@ test('nested transaction() call fail', function () use ($conn) {
|
|||||||
]);
|
]);
|
||||||
throw new Exception('my exception');
|
throw new Exception('my exception');
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
}, Throwable::class, 'my exception');
|
Throwable::class,
|
||||||
|
'my exception',
|
||||||
|
);
|
||||||
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -111,21 +119,27 @@ test('nested transaction() call success', function () use ($conn) {
|
|||||||
|
|
||||||
|
|
||||||
test('begin(), commit() & rollback() calls are forbidden in transaction()', function () use ($conn) {
|
test('begin(), commit() & rollback() calls are forbidden in transaction()', function () use ($conn) {
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||||
$connection->begin();
|
$connection->begin();
|
||||||
});
|
}),
|
||||||
}, LogicException::class, Dibi\Connection::class . '::begin() call is forbidden inside a transaction() callback');
|
LogicException::class,
|
||||||
|
Dibi\Connection::class . '::begin() call is forbidden inside a transaction() callback',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||||
$connection->commit();
|
$connection->commit();
|
||||||
});
|
}),
|
||||||
}, LogicException::class, Dibi\Connection::class . '::commit() call is forbidden inside a transaction() callback');
|
LogicException::class,
|
||||||
|
Dibi\Connection::class . '::commit() call is forbidden inside a transaction() callback',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->transaction(function (Dibi\Connection $connection) {
|
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||||
$connection->rollback();
|
$connection->rollback();
|
||||||
});
|
}),
|
||||||
}, LogicException::class, Dibi\Connection::class . '::rollback() call is forbidden inside a transaction() callback');
|
LogicException::class,
|
||||||
|
Dibi\Connection::class . '::rollback() call is forbidden inside a transaction() callback',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@@ -147,9 +147,11 @@ if ($config['system'] === 'mysql') {
|
|||||||
->limit(' 1; DROP TABLE users')
|
->limit(' 1; DROP TABLE users')
|
||||||
->offset(' 1; DROP TABLE users');
|
->offset(' 1; DROP TABLE users');
|
||||||
|
|
||||||
Assert::exception(function () use ($fluent) {
|
Assert::exception(
|
||||||
(string) $fluent;
|
fn() => (string) $fluent,
|
||||||
}, Dibi\Exception::class, "Expected number, ' 1; DROP TABLE users' given.");
|
Dibi\Exception::class,
|
||||||
|
"Expected number, ' 1; DROP TABLE users' given.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -12,22 +12,32 @@ Assert::same(0, Helpers::intVal(0));
|
|||||||
Assert::same(0, Helpers::intVal('0'));
|
Assert::same(0, Helpers::intVal('0'));
|
||||||
Assert::same(-10, Helpers::intVal('-10'));
|
Assert::same(-10, Helpers::intVal('-10'));
|
||||||
|
|
||||||
Assert::exception(function () {
|
Assert::exception(
|
||||||
Helpers::intVal('12345678901234567890123456879');
|
fn() => Helpers::intVal('12345678901234567890123456879'),
|
||||||
}, Dibi\Exception::class, 'Number 12345678901234567890123456879 is greater than integer.');
|
Dibi\Exception::class,
|
||||||
|
'Number 12345678901234567890123456879 is greater than integer.',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () {
|
Assert::exception(
|
||||||
Helpers::intVal('-12345678901234567890123456879');
|
fn() => Helpers::intVal('-12345678901234567890123456879'),
|
||||||
}, Dibi\Exception::class, 'Number -12345678901234567890123456879 is greater than integer.');
|
Dibi\Exception::class,
|
||||||
|
'Number -12345678901234567890123456879 is greater than integer.',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () {
|
Assert::exception(
|
||||||
Helpers::intVal('');
|
fn() => Helpers::intVal(''),
|
||||||
}, Dibi\Exception::class, "Expected number, '' given.");
|
Dibi\Exception::class,
|
||||||
|
"Expected number, '' given.",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () {
|
Assert::exception(
|
||||||
Helpers::intVal('not number');
|
fn() => Helpers::intVal('not number'),
|
||||||
}, Dibi\Exception::class, "Expected number, 'not number' given.");
|
Dibi\Exception::class,
|
||||||
|
"Expected number, 'not number' given.",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () {
|
Assert::exception(
|
||||||
Helpers::intVal(null);
|
fn() => Helpers::intVal(null),
|
||||||
}, Dibi\Exception::class, "Expected number, '' given.");
|
Dibi\Exception::class,
|
||||||
|
"Expected number, '' given.",
|
||||||
|
);
|
||||||
|
@@ -19,17 +19,22 @@ function buildPdoDriver(?int $errorMode)
|
|||||||
|
|
||||||
|
|
||||||
// PDO error mode: exception
|
// PDO error mode: exception
|
||||||
Assert::exception(function () {
|
Assert::exception(
|
||||||
buildPdoDriver(PDO::ERRMODE_EXCEPTION);
|
fn() => buildPdoDriver(PDO::ERRMODE_EXCEPTION),
|
||||||
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
|
Dibi\DriverException::class,
|
||||||
|
'PDO connection in exception or warning error mode is not supported.',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// PDO error mode: warning
|
// PDO error mode: warning
|
||||||
Assert::exception(function () {
|
Assert::exception(
|
||||||
buildPdoDriver(PDO::ERRMODE_WARNING);
|
fn() => buildPdoDriver(PDO::ERRMODE_WARNING),
|
||||||
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
|
Dibi\DriverException::class,
|
||||||
|
'PDO connection in exception or warning error mode is not supported.',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
test('PDO error mode: explicitly set silent', function () {
|
test(
|
||||||
buildPdoDriver(PDO::ERRMODE_SILENT);
|
'PDO error mode: explicitly set silent',
|
||||||
});
|
fn() => buildPdoDriver(PDO::ERRMODE_SILENT)
|
||||||
|
);
|
||||||
|
@@ -162,9 +162,10 @@ test('', function () {
|
|||||||
if (PHP_VERSION_ID < 80000) {
|
if (PHP_VERSION_ID < 80000) {
|
||||||
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning since PHP 7.1
|
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning since PHP 7.1
|
||||||
} else {
|
} else {
|
||||||
Assert::exception(function () use ($result) {
|
Assert::exception(
|
||||||
Assert::same(['col' => 0], $result->test(['col' => '']));
|
fn() => Assert::same(['col' => 0], $result->test(['col' => ''])),
|
||||||
}, TypeError::class);
|
TypeError::class,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert::same(['col' => 0], $result->test(['col' => '0']));
|
Assert::same(['col' => 0], $result->test(['col' => '0']));
|
||||||
@@ -189,9 +190,10 @@ test('', function () {
|
|||||||
$result->setType('col', Type::DATETIME);
|
$result->setType('col', Type::DATETIME);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(function () use ($result) {
|
Assert::exception(
|
||||||
$result->test(['col' => true]);
|
fn() => $result->test(['col' => true]),
|
||||||
}, TypeError::class);
|
TypeError::class,
|
||||||
|
);
|
||||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||||
@@ -208,9 +210,10 @@ test('', function () {
|
|||||||
$result->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
|
$result->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(function () use ($result) {
|
Assert::exception(
|
||||||
$result->test(['col' => true]);
|
fn() => $result->test(['col' => true]),
|
||||||
}, TypeError::class);
|
TypeError::class,
|
||||||
|
);
|
||||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||||
@@ -226,9 +229,10 @@ test('', function () {
|
|||||||
$result->setType('col', Type::DATE);
|
$result->setType('col', Type::DATE);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(function () use ($result) {
|
Assert::exception(
|
||||||
$result->test(['col' => true]);
|
fn() => $result->test(['col' => true]),
|
||||||
}, TypeError::class);
|
TypeError::class,
|
||||||
|
);
|
||||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||||
@@ -242,9 +246,10 @@ test('', function () {
|
|||||||
$result->setType('col', Type::TIME);
|
$result->setType('col', Type::TIME);
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
Assert::exception(function () use ($result) {
|
Assert::exception(
|
||||||
$result->test(['col' => true]);
|
fn() => $result->test(['col' => true]),
|
||||||
}, TypeError::class);
|
TypeError::class,
|
||||||
|
);
|
||||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||||
|
|
||||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||||
|
@@ -24,13 +24,17 @@ Assert::true(isset($row['title']));
|
|||||||
|
|
||||||
|
|
||||||
// missing
|
// missing
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row->missing;
|
fn() => $x = $row->missing,
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'missing'.");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'missing'.",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row['missing'];
|
fn() => $x = $row['missing'],
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'missing'.");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'missing'.",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::false(isset($row->missing));
|
Assert::false(isset($row->missing));
|
||||||
Assert::false(isset($row['missing']));
|
Assert::false(isset($row['missing']));
|
||||||
@@ -41,13 +45,17 @@ Assert::same(123, $row['missing'] ?? 123);
|
|||||||
|
|
||||||
|
|
||||||
// suggestions
|
// suggestions
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row->tilte;
|
fn() => $x = $row->tilte,
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'tilte', did you mean 'title'?",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::error(function () use ($row) {
|
Assert::error(
|
||||||
$x = $row['tilte'];
|
fn() => $x = $row['tilte'],
|
||||||
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
|
E_USER_NOTICE,
|
||||||
|
"Attempt to read missing column 'tilte', did you mean 'title'?",
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// to array
|
// to array
|
||||||
|
@@ -83,7 +83,7 @@ $tests = function ($conn) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Assert::exception(
|
Assert::exception(
|
||||||
$conn->translate('SELECT 1 %ofs %lmt', 10, 10),
|
fn() => $conn->translate('SELECT 1 %ofs %lmt', 10, 10),
|
||||||
Dibi\NotSupportedException::class,
|
Dibi\NotSupportedException::class,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,151 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
use Tester\Assert;
|
|
||||||
|
|
||||||
require __DIR__ . '/bootstrap.php';
|
|
||||||
|
|
||||||
|
|
||||||
class TestClass
|
|
||||||
{
|
|
||||||
use Dibi\Strict;
|
|
||||||
|
|
||||||
public $public;
|
|
||||||
|
|
||||||
public static $publicStatic;
|
|
||||||
|
|
||||||
protected $protected;
|
|
||||||
|
|
||||||
|
|
||||||
public function publicMethod()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function publicMethodStatic()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function protectedMethod()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected static function protectedMethodS()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function getBar()
|
|
||||||
{
|
|
||||||
return 123;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function isFoo()
|
|
||||||
{
|
|
||||||
return 456;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestChild extends TestClass
|
|
||||||
{
|
|
||||||
public function callParent()
|
|
||||||
{
|
|
||||||
parent::callParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// calling
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->undeclared();
|
|
||||||
}, LogicException::class, 'Call to undefined method TestClass::undeclared().');
|
|
||||||
|
|
||||||
Assert::exception(function () {
|
|
||||||
TestClass::undeclared();
|
|
||||||
}, LogicException::class, 'Call to undefined static method TestClass::undeclared().');
|
|
||||||
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestChild;
|
|
||||||
$obj->callParent();
|
|
||||||
}, LogicException::class, 'Call to undefined method parent::callParent().');
|
|
||||||
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->publicMethodX();
|
|
||||||
}, LogicException::class, 'Call to undefined method TestClass::publicMethodX(), did you mean publicMethod()?');
|
|
||||||
|
|
||||||
Assert::exception(function () { // suggest static method
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->publicMethodStaticX();
|
|
||||||
}, LogicException::class, 'Call to undefined method TestClass::publicMethodStaticX(), did you mean publicMethodStatic()?');
|
|
||||||
|
|
||||||
Assert::exception(function () { // suggest only public method
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->protectedMethodX();
|
|
||||||
}, LogicException::class, 'Call to undefined method TestClass::protectedMethodX().');
|
|
||||||
|
|
||||||
|
|
||||||
// writing
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->undeclared = 'value';
|
|
||||||
}, LogicException::class, 'Attempt to write to undeclared property TestClass::$undeclared.');
|
|
||||||
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->publicX = 'value';
|
|
||||||
}, LogicException::class, 'Attempt to write to undeclared property TestClass::$publicX, did you mean $public?');
|
|
||||||
|
|
||||||
Assert::exception(function () { // suggest only non-static property
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->publicStaticX = 'value';
|
|
||||||
}, LogicException::class, 'Attempt to write to undeclared property TestClass::$publicStaticX.');
|
|
||||||
|
|
||||||
Assert::exception(function () { // suggest only public property
|
|
||||||
$obj = new TestClass;
|
|
||||||
$obj->protectedX = 'value';
|
|
||||||
}, LogicException::class, 'Attempt to write to undeclared property TestClass::$protectedX.');
|
|
||||||
|
|
||||||
|
|
||||||
// property getter
|
|
||||||
$obj = new TestClass;
|
|
||||||
Assert::false(isset($obj->bar));
|
|
||||||
Assert::same(123, $obj->bar);
|
|
||||||
Assert::false(isset($obj->foo));
|
|
||||||
Assert::same(456, $obj->foo);
|
|
||||||
|
|
||||||
|
|
||||||
// reading
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestClass;
|
|
||||||
$val = $obj->undeclared;
|
|
||||||
}, LogicException::class, 'Attempt to read undeclared property TestClass::$undeclared.');
|
|
||||||
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestClass;
|
|
||||||
$val = $obj->publicX;
|
|
||||||
}, LogicException::class, 'Attempt to read undeclared property TestClass::$publicX, did you mean $public?');
|
|
||||||
|
|
||||||
Assert::exception(function () { // suggest only non-static property
|
|
||||||
$obj = new TestClass;
|
|
||||||
$val = $obj->publicStaticX;
|
|
||||||
}, LogicException::class, 'Attempt to read undeclared property TestClass::$publicStaticX.');
|
|
||||||
|
|
||||||
Assert::exception(function () { // suggest only public property
|
|
||||||
$obj = new TestClass;
|
|
||||||
$val = $obj->protectedX;
|
|
||||||
}, LogicException::class, 'Attempt to read undeclared property TestClass::$protectedX.');
|
|
||||||
|
|
||||||
|
|
||||||
// unset/isset
|
|
||||||
Assert::exception(function () {
|
|
||||||
$obj = new TestClass;
|
|
||||||
unset($obj->undeclared);
|
|
||||||
}, LogicException::class, 'Attempt to unset undeclared property TestClass::$undeclared.');
|
|
||||||
|
|
||||||
Assert::false(isset($obj->undeclared));
|
|
@@ -13,13 +13,16 @@ switch ($config['system']) {
|
|||||||
case 'mysql':
|
case 'mysql':
|
||||||
Assert::equal('10:20:30.0', $translator->formatValue(new DateInterval('PT10H20M30S'), null));
|
Assert::equal('10:20:30.0', $translator->formatValue(new DateInterval('PT10H20M30S'), null));
|
||||||
Assert::equal('-1:00:00.0', $translator->formatValue(DateInterval::createFromDateString('-1 hour'), null));
|
Assert::equal('-1:00:00.0', $translator->formatValue(DateInterval::createFromDateString('-1 hour'), null));
|
||||||
Assert::exception(function () use ($translator) {
|
Assert::exception(
|
||||||
$translator->formatValue(new DateInterval('P2Y4DT6H8M'), null);
|
fn() => $translator->formatValue(new DateInterval('P2Y4DT6H8M'), null),
|
||||||
}, Dibi\NotSupportedException::class, 'Only time interval is supported.');
|
Dibi\NotSupportedException::class,
|
||||||
|
'Only time interval is supported.',
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Assert::exception(function () use ($translator) {
|
Assert::exception(
|
||||||
$translator->formatValue(new DateInterval('PT10H20M30S'), null);
|
fn() => $translator->formatValue(new DateInterval('PT10H20M30S'), null),
|
||||||
}, Dibi\Exception::class);
|
Dibi\Exception::class,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@@ -91,9 +91,11 @@ Assert::same(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// invalid input
|
// invalid input
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->translate('SELECT %s', (object) [123], ', %m', 123);
|
fn() => $conn->translate('SELECT %s', (object) [123], ', %m', 123),
|
||||||
}, Dibi\Exception::class, 'SQL translate error: Invalid combination of type stdClass and modifier %s');
|
Dibi\Exception::class,
|
||||||
|
'SQL translate error: Invalid combination of type stdClass and modifier %s',
|
||||||
|
);
|
||||||
Assert::same('SELECT **Invalid combination of type stdClass and modifier %s** , **Unknown or unexpected modifier %m**', $e->getSql());
|
Assert::same('SELECT **Invalid combination of type stdClass and modifier %s** , **Unknown or unexpected modifier %m**', $e->getSql());
|
||||||
|
|
||||||
Assert::same(
|
Assert::same(
|
||||||
@@ -176,9 +178,10 @@ Assert::same(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ($config['system'] === 'odbc') {
|
if ($config['system'] === 'odbc') {
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1);
|
fn() => $conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1),
|
||||||
}, Dibi\Exception::class);
|
Dibi\Exception::class,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// with limit = 2, offset = 1
|
// with limit = 2, offset = 1
|
||||||
Assert::same(
|
Assert::same(
|
||||||
@@ -226,9 +229,11 @@ Assert::same(
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->translate('SELECT %s', new DateTime('1212-09-26'));
|
fn() => $conn->translate('SELECT %s', new DateTime('1212-09-26')),
|
||||||
}, Dibi\Exception::class, 'SQL translate error: Invalid combination of type Dibi\DateTime and modifier %s');
|
Dibi\Exception::class,
|
||||||
|
'SQL translate error: Invalid combination of type Dibi\DateTime and modifier %s',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -268,9 +273,11 @@ if ($config['system'] === 'postgre') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->translate("SELECT '");
|
fn() => $conn->translate("SELECT '"),
|
||||||
}, Dibi\Exception::class, 'SQL translate error: Alone quote');
|
Dibi\Exception::class,
|
||||||
|
'SQL translate error: Alone quote',
|
||||||
|
);
|
||||||
Assert::same('SELECT **Alone quote**', $e->getSql());
|
Assert::same('SELECT **Alone quote**', $e->getSql());
|
||||||
|
|
||||||
Assert::match(
|
Assert::match(
|
||||||
@@ -647,13 +654,17 @@ Assert::same(
|
|||||||
$conn->translate('INSERT INTO [test.*]'),
|
$conn->translate('INSERT INTO [test.*]'),
|
||||||
);
|
);
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->translate('INSERT INTO %i', 'ahoj');
|
fn() => $conn->translate('INSERT INTO %i', 'ahoj'),
|
||||||
}, Dibi\Exception::class, "Expected number, 'ahoj' given.");
|
Dibi\Exception::class,
|
||||||
|
"Expected number, 'ahoj' given.",
|
||||||
|
);
|
||||||
|
|
||||||
Assert::exception(function () use ($conn) {
|
Assert::exception(
|
||||||
$conn->translate('INSERT INTO %f', 'ahoj');
|
fn() => $conn->translate('INSERT INTO %f', 'ahoj'),
|
||||||
}, Dibi\Exception::class, "Expected number, 'ahoj' given.");
|
Dibi\Exception::class,
|
||||||
|
"Expected number, 'ahoj' given.",
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
Assert::same(
|
Assert::same(
|
||||||
|
@@ -20,7 +20,7 @@ date_default_timezone_set('Europe/Prague');
|
|||||||
try {
|
try {
|
||||||
$config = Tester\Environment::loadData();
|
$config = Tester\Environment::loadData();
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$config = parse_ini_file(__DIR__ . '/../databases.ini', true);
|
$config = parse_ini_file(__DIR__ . '/../databases.ini', process_sections: true);
|
||||||
$config = reset($config);
|
$config = reset($config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,41 +15,54 @@ $conn = new Dibi\Connection($config);
|
|||||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
new Dibi\Connection([
|
fn() => new Dibi\Connection([
|
||||||
'driver' => 'mysqli',
|
'driver' => 'mysqli',
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
'username' => 'unknown',
|
'username' => 'unknown',
|
||||||
'password' => 'unknown',
|
'password' => 'unknown',
|
||||||
]);
|
]),
|
||||||
}, Dibi\DriverException::class);
|
Dibi\DriverException::class,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::null($e->getSql());
|
Assert::null($e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('SELECT');
|
fn() => $conn->query('SELECT'),
|
||||||
}, Dibi\DriverException::class, '%a% error in your SQL syntax;%a%', 1064);
|
Dibi\DriverException::class,
|
||||||
|
'%a% error in your SQL syntax;%a%',
|
||||||
|
1064,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('SELECT', $e->getSql());
|
Assert::same('SELECT', $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
|
fn() => $conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")'),
|
||||||
}, Dibi\UniqueConstraintViolationException::class, "%a?%Duplicate entry '1' for key '%a?%PRIMARY'", 1062);
|
Dibi\UniqueConstraintViolationException::class,
|
||||||
|
"%a?%Duplicate entry '1' for key '%a?%PRIMARY'",
|
||||||
|
1062,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO products (title) VALUES (NULL)');
|
fn() => $conn->query('INSERT INTO products (title) VALUES (NULL)'),
|
||||||
}, Dibi\NotNullConstraintViolationException::class, "%a?%Column 'title' cannot be null", 1048);
|
Dibi\NotNullConstraintViolationException::class,
|
||||||
|
"%a?%Column 'title' cannot be null",
|
||||||
|
1048,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)');
|
fn() => $conn->query('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)'),
|
||||||
}, Dibi\ForeignKeyConstraintViolationException::class, '%a% a foreign key constraint fails %a%', 1452);
|
Dibi\ForeignKeyConstraintViolationException::class,
|
||||||
|
'%a% a foreign key constraint fails %a%',
|
||||||
|
1452,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)', $e->getSql());
|
Assert::same('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)', $e->getSql());
|
||||||
|
@@ -15,29 +15,40 @@ $conn = new Dibi\Connection($config);
|
|||||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('SELECT INTO');
|
fn() => $conn->query('SELECT INTO'),
|
||||||
}, Dibi\DriverException::class, '%a?%syntax error %A%');
|
Dibi\DriverException::class,
|
||||||
|
'%a?%syntax error %A%',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('SELECT INTO', $e->getSql());
|
Assert::same('SELECT INTO', $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
|
fn() => $conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")'),
|
||||||
}, Dibi\UniqueConstraintViolationException::class, '%a% violates unique constraint %A%', '23505');
|
Dibi\UniqueConstraintViolationException::class,
|
||||||
|
'%a% violates unique constraint %A%',
|
||||||
|
'23505',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO products (title) VALUES (NULL)');
|
fn() => $conn->query('INSERT INTO products (title) VALUES (NULL)'),
|
||||||
}, Dibi\NotNullConstraintViolationException::class, '%a?%null value in column "title"%a%violates not-null constraint%A?%', '23502');
|
Dibi\NotNullConstraintViolationException::class,
|
||||||
|
'%a?%null value in column "title"%a%violates not-null constraint%A?%',
|
||||||
|
'23502',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)');
|
fn() => $conn->query('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)'),
|
||||||
}, Dibi\ForeignKeyConstraintViolationException::class, '%a% violates foreign key constraint %A%', '23503');
|
Dibi\ForeignKeyConstraintViolationException::class,
|
||||||
|
'%a% violates foreign key constraint %A%',
|
||||||
|
'23503',
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)', $e->getSql());
|
Assert::same('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)', $e->getSql());
|
||||||
|
@@ -15,23 +15,32 @@ $conn = new Dibi\Connection($config);
|
|||||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('SELECT');
|
fn() => $conn->query('SELECT'),
|
||||||
}, Dibi\DriverException::class, '%a%', 1);
|
Dibi\DriverException::class,
|
||||||
|
'%a%',
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('SELECT', $e->getSql());
|
Assert::same('SELECT', $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
|
fn() => $conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")'),
|
||||||
}, Dibi\UniqueConstraintViolationException::class, null, 19);
|
Dibi\UniqueConstraintViolationException::class,
|
||||||
|
null,
|
||||||
|
19,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
||||||
|
|
||||||
|
|
||||||
$e = Assert::exception(function () use ($conn) {
|
$e = Assert::exception(
|
||||||
$conn->query('INSERT INTO products (title) VALUES (NULL)');
|
fn() => $conn->query('INSERT INTO products (title) VALUES (NULL)'),
|
||||||
}, Dibi\NotNullConstraintViolationException::class, null, 19);
|
Dibi\NotNullConstraintViolationException::class,
|
||||||
|
null,
|
||||||
|
19,
|
||||||
|
);
|
||||||
|
|
||||||
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user