mirror of
https://github.com/dg/dibi.git
synced 2025-09-03 19:12:33 +02:00
Compare commits
11 Commits
v5.0.0
...
translator
Author | SHA1 | Date | |
---|---|---|---|
|
09a974ed7a | ||
|
124d52139c | ||
|
78646e1790 | ||
|
cab1c5b5e6 | ||
|
94df6db03d | ||
|
0575f9ea17 | ||
|
76b8ed2108 | ||
|
4b1a2faa76 | ||
|
b931dbe13b | ||
|
a55e2a0cf8 | ||
|
6356f9f7a4 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,8 +1,8 @@
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.github export-ignore
|
||||
appveyor.yml export-ignore
|
||||
ncs.* export-ignore
|
||||
.travis.yml export-ignore
|
||||
ecs.php export-ignore
|
||||
phpstan.neon 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
|
||||
strategy:
|
||||
matrix:
|
||||
php: ['8.0', '8.1', '8.2', '8.3']
|
||||
php: ['8.0', '8.1', '8.2']
|
||||
|
||||
fail-fast: false
|
||||
|
||||
|
@@ -11,13 +11,13 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "8.0 - 8.3"
|
||||
"php": ">=8.0 <8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"tracy/tracy": "^2.9",
|
||||
"nette/tester": "^2.5",
|
||||
"nette/di": "^3.1",
|
||||
"phpstan/phpstan": "^1.0"
|
||||
"tracy/tracy": "^2.8",
|
||||
"nette/tester": "^2.4",
|
||||
"nette/di": "^3.0",
|
||||
"phpstan/phpstan": "^0.12"
|
||||
},
|
||||
"replace": {
|
||||
"dg/dibi": "*"
|
||||
|
@@ -34,7 +34,7 @@ Install Dibi via Composer:
|
||||
composer require dibi/dibi
|
||||
```
|
||||
|
||||
The Dibi 5.0 requires PHP version 8.0 and supports PHP up to 8.3.
|
||||
The Dibi 5.0 requires PHP version 8.0 and supports PHP up to 8.2.
|
||||
|
||||
|
||||
Usage
|
||||
@@ -341,7 +341,7 @@ $database->query('INSERT INTO users', [
|
||||
There are three methods for dealing with transactions:
|
||||
|
||||
```php
|
||||
$database->begin();
|
||||
$database->beginTransaction();
|
||||
|
||||
$database->commit();
|
||||
|
||||
|
@@ -20,6 +20,7 @@ use Tracy;
|
||||
class DibiExtension22 extends Nette\DI\CompilerExtension
|
||||
{
|
||||
private ?bool $debugMode;
|
||||
|
||||
private ?bool $cliMode;
|
||||
|
||||
|
||||
|
@@ -20,9 +20,15 @@ use Tracy;
|
||||
*/
|
||||
class Panel implements Tracy\IBarPanel
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** maximum SQL length */
|
||||
public static int $maxLength = 1000;
|
||||
|
||||
public bool|string $explain;
|
||||
|
||||
public int $filter;
|
||||
|
||||
private array $events = [];
|
||||
|
||||
|
||||
@@ -165,7 +171,7 @@ class Panel implements Tracy\IBarPanel
|
||||
private function getConnectionName(Dibi\Connection $connection): string
|
||||
{
|
||||
$driver = $connection->getConfig('driver');
|
||||
return get_debug_type($driver)
|
||||
return (is_object($driver) ? $driver::class : $driver)
|
||||
. ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
|
||||
. ($connection->getConfig('host') ? "\u{202f}@\u{202f}" . $connection->getConfig('host') : '');
|
||||
}
|
||||
|
@@ -20,19 +20,26 @@ use Traversable;
|
||||
*/
|
||||
class Connection implements IConnection
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** function (Event $event); Occurs after query is executed */
|
||||
public ?array $onEvent = [];
|
||||
|
||||
/** Current connection configuration */
|
||||
private array $config;
|
||||
|
||||
/** @var string[] resultset formats */
|
||||
private array $formats;
|
||||
|
||||
private ?Driver $driver = null;
|
||||
|
||||
private ?Translator $translator = null;
|
||||
|
||||
/** @var array<string, callable(object): Expression | null> */
|
||||
/** @var array<string, callable(object): Expression> */
|
||||
private array $translators = [];
|
||||
private bool $sortTranslators = false;
|
||||
|
||||
private HashMap $substitutes;
|
||||
|
||||
private int $transactionDepth = 0;
|
||||
|
||||
|
||||
@@ -521,68 +528,19 @@ class Connection implements IConnection
|
||||
/********************* value objects translation ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* @param callable(object): Expression $translator
|
||||
*/
|
||||
public function setObjectTranslator(callable $translator): void
|
||||
/** @param callable(object): Expression $translator */
|
||||
public function addObjectTranslator(string $class, callable $translator): self
|
||||
{
|
||||
if (!$translator instanceof \Closure) {
|
||||
$translator = \Closure::fromCallable($translator);
|
||||
}
|
||||
|
||||
$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;
|
||||
$this->translators[$class] = $translator;
|
||||
uksort($this->translators, fn($a, $b) => class_exists($a, false) && is_subclass_of($a, $b) ? -1 : 1);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function translateObject(object $object): ?Expression
|
||||
/** @return array<string, callable(object): Expression> */
|
||||
public function getObjectTranslators(): array
|
||||
{
|
||||
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;
|
||||
return $this->translators;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -15,15 +15,26 @@ namespace Dibi;
|
||||
*/
|
||||
class DataSource implements IDataSource
|
||||
{
|
||||
use Strict;
|
||||
|
||||
private Connection $connection;
|
||||
|
||||
private string $sql;
|
||||
|
||||
private ?Result $result = null;
|
||||
|
||||
private ?int $count = null;
|
||||
|
||||
private ?int $totalCount = null;
|
||||
|
||||
private array $cols = [];
|
||||
|
||||
private array $sorting = [];
|
||||
|
||||
private array $conds = [];
|
||||
|
||||
private ?int $offset = null;
|
||||
|
||||
private ?int $limit = null;
|
||||
|
||||
|
||||
|
@@ -15,6 +15,8 @@ namespace Dibi;
|
||||
*/
|
||||
class DateTime extends \DateTimeImmutable
|
||||
{
|
||||
use Strict;
|
||||
|
||||
public function __construct(string|int $time = 'now', ?\DateTimeZone $timezone = null)
|
||||
{
|
||||
$timezone = $timezone ?: new \DateTimeZone(date_default_timezone_get());
|
||||
|
@@ -17,6 +17,8 @@ use Dibi;
|
||||
*/
|
||||
class DummyDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
public function disconnect(): void
|
||||
{
|
||||
}
|
||||
|
@@ -26,6 +26,8 @@ use Dibi\Helpers;
|
||||
*/
|
||||
class FirebirdDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
public const ERROR_EXCEPTION_THROWN = -836;
|
||||
|
||||
/** @var resource */
|
||||
@@ -33,6 +35,7 @@ class FirebirdDriver implements Dibi\Driver
|
||||
|
||||
/** @var ?resource */
|
||||
private $transaction;
|
||||
|
||||
private bool $inTransaction = false;
|
||||
|
||||
|
||||
|
@@ -17,6 +17,8 @@ use Dibi;
|
||||
*/
|
||||
class FirebirdReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Driver $driver;
|
||||
|
||||
|
||||
|
@@ -18,9 +18,13 @@ use Dibi\Helpers;
|
||||
*/
|
||||
class FirebirdResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
private $resultSet;
|
||||
|
||||
private bool $autoFree = true;
|
||||
|
||||
|
||||
/**
|
||||
* @param resource $resultSet
|
||||
@@ -31,6 +35,17 @@ 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.
|
||||
*/
|
||||
@@ -89,6 +104,7 @@ class FirebirdResult implements Dibi\ResultDriver
|
||||
*/
|
||||
public function getResultResource(): mixed
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,8 @@ use Dibi;
|
||||
*/
|
||||
class MySqlReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Driver $driver;
|
||||
|
||||
|
||||
|
@@ -32,11 +32,17 @@ use Dibi;
|
||||
*/
|
||||
class MySqliDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
public const ERROR_ACCESS_DENIED = 1045;
|
||||
|
||||
public const ERROR_DUPLICATE_ENTRY = 1062;
|
||||
|
||||
public const ERROR_DATA_TRUNCATED = 1265;
|
||||
|
||||
private \mysqli $connection;
|
||||
|
||||
/** Is buffered (seekable and countable)? */
|
||||
private bool $buffered = false;
|
||||
|
||||
|
||||
|
@@ -17,7 +17,13 @@ use Dibi;
|
||||
*/
|
||||
class MySqliResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private \mysqli_result $resultSet;
|
||||
|
||||
private bool $autoFree = true;
|
||||
|
||||
/** Is buffered (seekable and countable)? */
|
||||
private bool $buffered;
|
||||
|
||||
|
||||
@@ -28,6 +34,17 @@ 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.
|
||||
*/
|
||||
@@ -117,6 +134,7 @@ class MySqliResult implements Dibi\ResultDriver
|
||||
*/
|
||||
public function getResultResource(): \mysqli_result
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return $this->resultSet;
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,8 @@ use Dibi;
|
||||
*/
|
||||
class NoDataResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private int $rows;
|
||||
|
||||
|
||||
|
@@ -25,9 +25,13 @@ use Dibi;
|
||||
*/
|
||||
class OdbcDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
private $connection;
|
||||
|
||||
private ?int $affectedRows;
|
||||
|
||||
private bool $microseconds = true;
|
||||
|
||||
|
||||
|
@@ -17,6 +17,8 @@ use Dibi;
|
||||
*/
|
||||
class OdbcReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Driver $driver;
|
||||
|
||||
|
||||
|
@@ -17,8 +17,13 @@ use Dibi;
|
||||
*/
|
||||
class OdbcResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
private $resultSet;
|
||||
|
||||
private bool $autoFree = true;
|
||||
|
||||
private int $row = 0;
|
||||
|
||||
|
||||
@@ -31,6 +36,17 @@ 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.
|
||||
*/
|
||||
@@ -111,6 +127,7 @@ class OdbcResult implements Dibi\ResultDriver
|
||||
*/
|
||||
public function getResultResource(): mixed
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||
}
|
||||
|
||||
|
@@ -27,10 +27,16 @@ use Dibi;
|
||||
*/
|
||||
class OracleDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
private $connection;
|
||||
|
||||
private bool $autocommit = true;
|
||||
|
||||
/** use native datetime format */
|
||||
private bool $nativeDate;
|
||||
|
||||
private ?int $affectedRows;
|
||||
|
||||
|
||||
|
@@ -17,6 +17,8 @@ use Dibi;
|
||||
*/
|
||||
class OracleReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Driver $driver;
|
||||
|
||||
|
||||
|
@@ -17,9 +17,13 @@ use Dibi;
|
||||
*/
|
||||
class OracleResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
private $resultSet;
|
||||
|
||||
private bool $autoFree = true;
|
||||
|
||||
|
||||
/**
|
||||
* @param resource $resultSet
|
||||
@@ -30,6 +34,17 @@ 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.
|
||||
*/
|
||||
@@ -95,6 +110,7 @@ class OracleResult implements Dibi\ResultDriver
|
||||
*/
|
||||
public function getResultResource(): mixed
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||
}
|
||||
|
||||
|
@@ -27,9 +27,14 @@ use PDO;
|
||||
*/
|
||||
class PdoDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private ?PDO $connection;
|
||||
|
||||
private ?int $affectedRows;
|
||||
|
||||
private string $driverName;
|
||||
|
||||
private string $serverVersion = '';
|
||||
|
||||
|
||||
|
@@ -19,7 +19,10 @@ use PDO;
|
||||
*/
|
||||
class PdoResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private ?\PDOStatement $resultSet;
|
||||
|
||||
private string $driverName;
|
||||
|
||||
|
||||
|
@@ -28,8 +28,11 @@ use PgSql;
|
||||
*/
|
||||
class PostgreDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource|PgSql\Connection */
|
||||
private $connection;
|
||||
|
||||
private ?int $affectedRows;
|
||||
|
||||
|
||||
|
@@ -17,7 +17,10 @@ use Dibi;
|
||||
*/
|
||||
class PostgreReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Driver $driver;
|
||||
|
||||
private string $version;
|
||||
|
||||
|
||||
|
@@ -19,9 +19,13 @@ use PgSql;
|
||||
*/
|
||||
class PostgreResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource|PgSql\Result */
|
||||
private $resultSet;
|
||||
|
||||
private bool $autoFree = true;
|
||||
|
||||
|
||||
/**
|
||||
* @param resource|PgSql\Result $resultSet
|
||||
@@ -32,6 +36,17 @@ 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.
|
||||
*/
|
||||
@@ -98,6 +113,7 @@ class PostgreResult implements Dibi\ResultDriver
|
||||
*/
|
||||
public function getResultResource(): mixed
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return is_resource($this->resultSet) || $this->resultSet instanceof PgSql\Result
|
||||
? $this->resultSet
|
||||
: null;
|
||||
|
@@ -25,8 +25,12 @@ use SQLite3;
|
||||
*/
|
||||
class SqliteDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private SQLite3 $connection;
|
||||
|
||||
private string $fmtDate;
|
||||
|
||||
private string $fmtDateTime;
|
||||
|
||||
|
||||
|
@@ -17,6 +17,8 @@ use Dibi;
|
||||
*/
|
||||
class SqliteReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Driver $driver;
|
||||
|
||||
|
||||
|
@@ -18,8 +18,12 @@ use Dibi\Helpers;
|
||||
*/
|
||||
class SqliteResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private \SQLite3Result $resultSet;
|
||||
|
||||
private bool $autoFree = true;
|
||||
|
||||
|
||||
public function __construct(\SQLite3Result $resultSet)
|
||||
{
|
||||
@@ -27,6 +31,17 @@ 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.
|
||||
* @throws Dibi\NotSupportedException
|
||||
@@ -92,6 +107,7 @@ class SqliteResult implements Dibi\ResultDriver
|
||||
*/
|
||||
public function getResultResource(): \SQLite3Result
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return $this->resultSet;
|
||||
}
|
||||
|
||||
|
@@ -27,9 +27,13 @@ use Dibi\Helpers;
|
||||
*/
|
||||
class SqlsrvDriver implements Dibi\Driver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
private $connection;
|
||||
|
||||
private ?int $affectedRows;
|
||||
|
||||
private string $version = '';
|
||||
|
||||
|
||||
|
@@ -17,6 +17,8 @@ use Dibi;
|
||||
*/
|
||||
class SqlsrvReflector implements Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Driver $driver;
|
||||
|
||||
|
||||
|
@@ -17,9 +17,13 @@ use Dibi;
|
||||
*/
|
||||
class SqlsrvResult implements Dibi\ResultDriver
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var resource */
|
||||
private $resultSet;
|
||||
|
||||
private bool $autoFree = true;
|
||||
|
||||
|
||||
/**
|
||||
* @param resource $resultSet
|
||||
@@ -30,6 +34,17 @@ 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.
|
||||
*/
|
||||
@@ -91,6 +106,7 @@ class SqlsrvResult implements Dibi\ResultDriver
|
||||
*/
|
||||
public function getResultResource(): mixed
|
||||
{
|
||||
$this->autoFree = false;
|
||||
return is_resource($this->resultSet) ? $this->resultSet : null;
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,8 @@ namespace Dibi;
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** event type */
|
||||
public const
|
||||
CONNECT = 1,
|
||||
@@ -30,11 +32,17 @@ class Event
|
||||
ALL = 1023;
|
||||
|
||||
public Connection $connection;
|
||||
|
||||
public int $type;
|
||||
|
||||
public string $sql;
|
||||
|
||||
public Result|DriverException|null $result;
|
||||
|
||||
public float $time;
|
||||
|
||||
public ?int $count = null;
|
||||
|
||||
public ?array $source = null;
|
||||
|
||||
|
||||
|
@@ -15,6 +15,8 @@ namespace Dibi;
|
||||
*/
|
||||
class Expression
|
||||
{
|
||||
use Strict;
|
||||
|
||||
private array $values;
|
||||
|
||||
|
||||
|
@@ -27,8 +27,6 @@ namespace Dibi;
|
||||
* @method Fluent innerJoin(...$table)
|
||||
* @method Fluent rightJoin(...$table)
|
||||
* @method Fluent outerJoin(...$table)
|
||||
* @method Fluent union(Fluent $fluent)
|
||||
* @method Fluent unionAll(Fluent $fluent)
|
||||
* @method Fluent as(...$field)
|
||||
* @method Fluent on(...$cond)
|
||||
* @method Fluent and(...$cond)
|
||||
@@ -45,6 +43,8 @@ namespace Dibi;
|
||||
*/
|
||||
class Fluent implements IDataSource
|
||||
{
|
||||
use Strict;
|
||||
|
||||
public const REMOVE = false;
|
||||
|
||||
public static array $masks = [
|
||||
@@ -92,10 +92,15 @@ class Fluent implements IDataSource
|
||||
];
|
||||
|
||||
private Connection $connection;
|
||||
|
||||
private array $setups = [];
|
||||
|
||||
private ?string $command = null;
|
||||
|
||||
private array $clauses = [];
|
||||
|
||||
private array $flags = [];
|
||||
|
||||
private $cursor;
|
||||
|
||||
/** normalized clauses */
|
||||
|
@@ -12,6 +12,8 @@ namespace Dibi;
|
||||
|
||||
class Helpers
|
||||
{
|
||||
use Strict;
|
||||
|
||||
private static HashMap $types;
|
||||
|
||||
|
||||
|
@@ -15,6 +15,8 @@ namespace Dibi;
|
||||
*/
|
||||
class Literal
|
||||
{
|
||||
use Strict;
|
||||
|
||||
private string $value;
|
||||
|
||||
|
||||
|
@@ -17,9 +17,13 @@ use Dibi;
|
||||
*/
|
||||
class FileLogger
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** Name of the file where SQL errors should be logged */
|
||||
public string $file;
|
||||
|
||||
public int $filter;
|
||||
|
||||
private bool $errorsOnly;
|
||||
|
||||
|
||||
@@ -70,7 +74,7 @@ class FileLogger
|
||||
{
|
||||
$driver = $event->connection->getConfig('driver');
|
||||
$message .=
|
||||
"\n-- driver: " . get_debug_type($driver) . '/' . $event->connection->getConfig('name')
|
||||
"\n-- driver: " . (is_object($driver) ? $driver::class : $driver) . '/' . $event->connection->getConfig('name')
|
||||
. "\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n";
|
||||
file_put_contents($this->file, $message, FILE_APPEND | LOCK_EX);
|
||||
|
@@ -27,6 +27,8 @@ use Dibi;
|
||||
*/
|
||||
class Column
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** when created by Result */
|
||||
private ?Dibi\Reflector $reflector;
|
||||
|
||||
|
@@ -21,7 +21,10 @@ use Dibi;
|
||||
*/
|
||||
class Database
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Reflector $reflector;
|
||||
|
||||
private ?string $name;
|
||||
|
||||
/** @var Table[] */
|
||||
|
@@ -9,6 +9,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Dibi\Reflection;
|
||||
|
||||
use Dibi;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,6 +20,8 @@ namespace Dibi\Reflection;
|
||||
*/
|
||||
class ForeignKey
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private string $name;
|
||||
|
||||
/** @var array of [local, foreign, onDelete, onUpdate] */
|
||||
|
@@ -9,6 +9,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Dibi\Reflection;
|
||||
|
||||
use Dibi;
|
||||
|
||||
|
||||
/**
|
||||
@@ -21,6 +22,8 @@ namespace Dibi\Reflection;
|
||||
*/
|
||||
class Index
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
/** @var array (name, columns, [unique], [primary]) */
|
||||
private array $info;
|
||||
|
||||
|
@@ -20,6 +20,8 @@ use Dibi;
|
||||
*/
|
||||
class Result
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\ResultDriver $driver;
|
||||
|
||||
/** @var Column[]|null */
|
||||
|
@@ -25,8 +25,12 @@ use Dibi;
|
||||
*/
|
||||
class Table
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
private Dibi\Reflector $reflector;
|
||||
|
||||
private string $name;
|
||||
|
||||
private bool $view;
|
||||
|
||||
/** @var Column[] */
|
||||
@@ -37,6 +41,7 @@ class Table
|
||||
|
||||
/** @var Index[] */
|
||||
private array $indexes;
|
||||
|
||||
private ?Index $primaryKey;
|
||||
|
||||
|
||||
|
@@ -17,10 +17,13 @@ namespace Dibi;
|
||||
*/
|
||||
class Result implements IDataSource
|
||||
{
|
||||
use Strict;
|
||||
|
||||
private ?ResultDriver $driver;
|
||||
|
||||
/** Translate table */
|
||||
private array $types = [];
|
||||
|
||||
private ?Reflection\Result $meta;
|
||||
|
||||
/** Already fetched? Used for allowance for first seek(0) */
|
||||
@@ -31,6 +34,7 @@ class Result implements IDataSource
|
||||
|
||||
/** @var callable|null returned object factory */
|
||||
private $rowFactory;
|
||||
|
||||
private array $formats = [];
|
||||
|
||||
|
||||
|
@@ -15,8 +15,12 @@ namespace Dibi;
|
||||
*/
|
||||
class ResultIterator implements \Iterator, \Countable
|
||||
{
|
||||
use Strict;
|
||||
|
||||
private Result $result;
|
||||
|
||||
private mixed $row;
|
||||
|
||||
private int $pointer = 0;
|
||||
|
||||
|
||||
@@ -40,7 +44,6 @@ class ResultIterator implements \Iterator, \Countable
|
||||
/**
|
||||
* Returns the key of the current element.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key(): mixed
|
||||
{
|
||||
return $this->pointer;
|
||||
@@ -50,7 +53,6 @@ class ResultIterator implements \Iterator, \Countable
|
||||
/**
|
||||
* Returns the current element.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current(): mixed
|
||||
{
|
||||
return $this->row;
|
||||
|
112
src/Dibi/Strict.php
Normal file
112
src/Dibi/Strict.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?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,18 +15,29 @@ namespace Dibi;
|
||||
*/
|
||||
final class Translator
|
||||
{
|
||||
use Strict;
|
||||
|
||||
private Connection $connection;
|
||||
|
||||
private Driver $driver;
|
||||
|
||||
private int $cursor = 0;
|
||||
|
||||
private array $args;
|
||||
|
||||
/** @var string[] */
|
||||
private array $errors;
|
||||
|
||||
private bool $comment = false;
|
||||
|
||||
private int $ifLevel = 0;
|
||||
|
||||
private int $ifLevelStart = 0;
|
||||
|
||||
private ?int $limit = null;
|
||||
|
||||
private ?int $offset = null;
|
||||
|
||||
private HashMap $identifiers;
|
||||
|
||||
|
||||
@@ -257,7 +268,7 @@ final class Translator
|
||||
$proto = array_keys($v);
|
||||
}
|
||||
} else {
|
||||
return $this->errors[] = '**Unexpected type ' . get_debug_type($v) . '**';
|
||||
return $this->errors[] = '**Unexpected type ' . (is_object($v) ? $v::class : gettype($v)) . '**';
|
||||
}
|
||||
|
||||
$pair = explode('%', $k, 2); // split into identifier & modifier
|
||||
@@ -307,9 +318,13 @@ final class Translator
|
||||
&& $modifier === null
|
||||
&& !$value instanceof Literal
|
||||
&& !$value instanceof Expression
|
||||
&& $result = $this->connection->translateObject($value)
|
||||
) {
|
||||
return $this->connection->translate(...$result->getValues());
|
||||
foreach ($this->connection->getObjectTranslators() as $class => $translator) {
|
||||
if ($value instanceof $class) {
|
||||
$value = $translator($value);
|
||||
return $this->connection->translate(...$value->getValues());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// object-to-scalar procession
|
||||
@@ -335,7 +350,7 @@ final class Translator
|
||||
) {
|
||||
// continue
|
||||
} else {
|
||||
$type = get_debug_type($value);
|
||||
$type = is_object($value) ? $value::class : gettype($value);
|
||||
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
|
||||
}
|
||||
}
|
||||
@@ -423,17 +438,18 @@ final class Translator
|
||||
$value = substr($value, 0, $toSkip)
|
||||
. preg_replace_callback(
|
||||
<<<'XX'
|
||||
/
|
||||
(?=[`['":])
|
||||
(?:
|
||||
`(.+?)`|
|
||||
\[(.+?)]|
|
||||
(')((?:''|[^'])*)'|
|
||||
(")((?:""|[^"])*)"|
|
||||
(['"])|
|
||||
:(\S*?:)([a-zA-Z0-9._]?)
|
||||
)/sx
|
||||
XX,
|
||||
/
|
||||
(?=[`['":])
|
||||
(?:
|
||||
`(.+?)`|
|
||||
\[(.+?)\]|
|
||||
(')((?:''|[^'])*)'|
|
||||
(")((?:""|[^"])*)"|
|
||||
('|")|
|
||||
:(\S*?:)([a-zA-Z0-9._]?)
|
||||
)/sx
|
||||
XX
|
||||
,
|
||||
[$this, 'cb'],
|
||||
substr($value, $toSkip),
|
||||
);
|
||||
@@ -501,7 +517,7 @@ final class Translator
|
||||
return $this->connection->translate(...$value->getValues());
|
||||
|
||||
} else {
|
||||
$type = get_debug_type($value);
|
||||
$type = is_object($value) ? $value::class : gettype($value);
|
||||
return $this->errors[] = "**Unexpected $type**";
|
||||
}
|
||||
}
|
||||
|
@@ -37,12 +37,14 @@ declare(strict_types=1);
|
||||
*/
|
||||
class dibi
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
public const
|
||||
AFFECTED_ROWS = 'a',
|
||||
IDENTIFIER = 'n';
|
||||
|
||||
/** version */
|
||||
public const VERSION = '5.0.0';
|
||||
public const VERSION = '5.0-dev';
|
||||
|
||||
/** sorting order */
|
||||
public const
|
||||
|
@@ -73,29 +73,24 @@ test('', function () use ($config) {
|
||||
|
||||
|
||||
test('', function () use ($config) {
|
||||
Assert::exception(
|
||||
fn() => new Connection($config + ['onConnect' => '']),
|
||||
InvalidArgumentException::class,
|
||||
"Configuration option 'onConnect' must be array.",
|
||||
);
|
||||
Assert::exception(function () use ($config) {
|
||||
new Connection($config + ['onConnect' => '']);
|
||||
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => new Connection($config + ['onConnect' => ['STOP']]),
|
||||
Dibi\DriverException::class,
|
||||
);
|
||||
$e = Assert::exception(function () use ($config) {
|
||||
new Connection($config + ['onConnect' => ['STOP']]);
|
||||
}, Dibi\DriverException::class);
|
||||
Assert::same('STOP', $e->getSql());
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => new Connection($config + ['onConnect' => [['STOP %i', 123]]]),
|
||||
Dibi\DriverException::class,
|
||||
);
|
||||
$e = Assert::exception(function () use ($config) {
|
||||
new Connection($config + ['onConnect' => [['STOP %i', 123]]]);
|
||||
}, Dibi\DriverException::class);
|
||||
Assert::same('STOP 123', $e->getSql());
|
||||
|
||||
// lazy
|
||||
$conn = new Connection($config + ['lazy' => true, 'onConnect' => ['STOP']]);
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('SELECT 1'),
|
||||
Dibi\DriverException::class,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('SELECT 1');
|
||||
}, Dibi\DriverException::class);
|
||||
Assert::same('STOP', $e->getSql());
|
||||
});
|
||||
|
@@ -24,16 +24,14 @@ class Time extends DateTimeImmutable
|
||||
|
||||
|
||||
test('Without object translator', function () use ($conn) {
|
||||
Assert::exception(
|
||||
fn() => $conn->translate('?', new Email),
|
||||
Dibi\Exception::class,
|
||||
'SQL translate error: Unexpected Email',
|
||||
);
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->translate('?', new Email);
|
||||
}, Dibi\Exception::class, 'SQL translate error: Unexpected Email');
|
||||
});
|
||||
|
||||
|
||||
test('Basics', function () use ($conn) {
|
||||
$conn->setObjectTranslator(fn(Email $email) => new Dibi\Expression('?', $email->address));
|
||||
$conn->addObjectTranslator(Email::class, fn($object) => new Dibi\Expression('?', $object->address));
|
||||
Assert::same(
|
||||
reformat([
|
||||
'sqlsrv' => "N'address@example.com'",
|
||||
@@ -55,7 +53,7 @@ test('DateTime', function () use ($conn) {
|
||||
|
||||
|
||||
// With object translator
|
||||
$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')));
|
||||
Assert::same(
|
||||
reformat([
|
||||
'sqlsrv' => "OwnTime(N'12:13:14')",
|
||||
@@ -88,7 +86,7 @@ test('DateTime', function () use ($conn) {
|
||||
);
|
||||
|
||||
// But DateTime translation can be overloaded
|
||||
$conn->setObjectTranslator(fn(DateTimeInterface $dt) => new Dibi\Expression('OwnDateTime'));
|
||||
$conn->addObjectTranslator(DateTimeInterface::class, fn() => new Dibi\Expression('OwnDateTime'));
|
||||
Assert::same(
|
||||
'OwnDateTime',
|
||||
$conn->translate('?', $dt),
|
||||
@@ -97,9 +95,9 @@ test('DateTime', function () use ($conn) {
|
||||
|
||||
|
||||
test('Complex structures', function () use ($conn) {
|
||||
$conn->setObjectTranslator(fn(Email $email) => new Dibi\Expression('?', $email->address));
|
||||
$conn->setObjectTranslator(fn(Time $time) => new Dibi\Expression('OwnTime(?)', $time->format('H:i:s')));
|
||||
$conn->setObjectTranslator(fn(DateTimeInterface $dt) => new Dibi\Expression('OwnDateTime'));
|
||||
$conn->addObjectTranslator(Email::class, fn($object) => new Dibi\Expression('?', $object->address));
|
||||
$conn->addObjectTranslator(Time::class, fn($object) => new Dibi\Expression('OwnTime(?)', $object->format('H:i:s')));
|
||||
$conn->addObjectTranslator(Time::class, fn($object) => new Dibi\Expression('OwnTime(?)', $object->format('H:i:s')));
|
||||
|
||||
$time = Time::createFromFormat('Y-m-d H:i:s', '2022-11-22 12:13:14');
|
||||
Assert::same(
|
||||
@@ -119,37 +117,3 @@ 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,22 +15,18 @@ $conn = new Dibi\Connection($config);
|
||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||
|
||||
|
||||
/*
|
||||
Assert::exception(
|
||||
fn() => $conn->rollback(),
|
||||
Dibi\Exception::class,
|
||||
);
|
||||
/*Assert::exception(function () use ($conn) {
|
||||
$conn->rollback();
|
||||
}, Dibi\Exception::class);
|
||||
|
||||
Assert::exception(
|
||||
fn() => $conn->commit(),
|
||||
Dibi\Exception::class,
|
||||
);
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->commit();
|
||||
}, Dibi\Exception::class);
|
||||
|
||||
$conn->begin();
|
||||
Assert::exception(
|
||||
fn() => $conn->begin(),
|
||||
Dibi\Exception::class,
|
||||
);
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->begin();
|
||||
}, Dibi\Exception::class);
|
||||
*/
|
||||
|
||||
|
||||
@@ -57,16 +53,14 @@ test('begin() & commit()', function () use ($conn) {
|
||||
|
||||
|
||||
test('transaction() fail', function () use ($conn) {
|
||||
Assert::exception(
|
||||
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
throw new Exception('my exception');
|
||||
}),
|
||||
Throwable::class,
|
||||
'my exception',
|
||||
);
|
||||
});
|
||||
}, Throwable::class, 'my exception');
|
||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
@@ -82,8 +76,8 @@ test('transaction() success', function () use ($conn) {
|
||||
|
||||
|
||||
test('nested transaction() call fail', function () use ($conn) {
|
||||
Assert::exception(
|
||||
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$connection->query('INSERT INTO [products]', [
|
||||
'title' => 'Test product',
|
||||
]);
|
||||
@@ -94,10 +88,8 @@ test('nested transaction() call fail', function () use ($conn) {
|
||||
]);
|
||||
throw new Exception('my exception');
|
||||
});
|
||||
}),
|
||||
Throwable::class,
|
||||
'my exception',
|
||||
);
|
||||
});
|
||||
}, Throwable::class, 'my exception');
|
||||
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||
});
|
||||
|
||||
@@ -119,27 +111,21 @@ test('nested transaction() call success', function () use ($conn) {
|
||||
|
||||
|
||||
test('begin(), commit() & rollback() calls are forbidden in transaction()', function () use ($conn) {
|
||||
Assert::exception(
|
||||
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$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(
|
||||
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$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(
|
||||
fn() => $conn->transaction(function (Dibi\Connection $connection) {
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->transaction(function (Dibi\Connection $connection) {
|
||||
$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,11 +147,9 @@ if ($config['system'] === 'mysql') {
|
||||
->limit(' 1; DROP TABLE users')
|
||||
->offset(' 1; DROP TABLE users');
|
||||
|
||||
Assert::exception(
|
||||
fn() => (string) $fluent,
|
||||
Dibi\Exception::class,
|
||||
"Expected number, ' 1; DROP TABLE users' given.",
|
||||
);
|
||||
Assert::exception(function () use ($fluent) {
|
||||
(string) $fluent;
|
||||
}, Dibi\Exception::class, "Expected number, ' 1; DROP TABLE users' given.");
|
||||
}
|
||||
|
||||
|
||||
|
@@ -12,32 +12,22 @@ Assert::same(0, Helpers::intVal(0));
|
||||
Assert::same(0, Helpers::intVal('0'));
|
||||
Assert::same(-10, Helpers::intVal('-10'));
|
||||
|
||||
Assert::exception(
|
||||
fn() => Helpers::intVal('12345678901234567890123456879'),
|
||||
Dibi\Exception::class,
|
||||
'Number 12345678901234567890123456879 is greater than integer.',
|
||||
);
|
||||
Assert::exception(function () {
|
||||
Helpers::intVal('12345678901234567890123456879');
|
||||
}, Dibi\Exception::class, 'Number 12345678901234567890123456879 is greater than integer.');
|
||||
|
||||
Assert::exception(
|
||||
fn() => Helpers::intVal('-12345678901234567890123456879'),
|
||||
Dibi\Exception::class,
|
||||
'Number -12345678901234567890123456879 is greater than integer.',
|
||||
);
|
||||
Assert::exception(function () {
|
||||
Helpers::intVal('-12345678901234567890123456879');
|
||||
}, Dibi\Exception::class, 'Number -12345678901234567890123456879 is greater than integer.');
|
||||
|
||||
Assert::exception(
|
||||
fn() => Helpers::intVal(''),
|
||||
Dibi\Exception::class,
|
||||
"Expected number, '' given.",
|
||||
);
|
||||
Assert::exception(function () {
|
||||
Helpers::intVal('');
|
||||
}, Dibi\Exception::class, "Expected number, '' given.");
|
||||
|
||||
Assert::exception(
|
||||
fn() => Helpers::intVal('not number'),
|
||||
Dibi\Exception::class,
|
||||
"Expected number, 'not number' given.",
|
||||
);
|
||||
Assert::exception(function () {
|
||||
Helpers::intVal('not number');
|
||||
}, Dibi\Exception::class, "Expected number, 'not number' given.");
|
||||
|
||||
Assert::exception(
|
||||
fn() => Helpers::intVal(null),
|
||||
Dibi\Exception::class,
|
||||
"Expected number, '' given.",
|
||||
);
|
||||
Assert::exception(function () {
|
||||
Helpers::intVal(null);
|
||||
}, Dibi\Exception::class, "Expected number, '' given.");
|
||||
|
@@ -19,22 +19,17 @@ function buildPdoDriver(?int $errorMode)
|
||||
|
||||
|
||||
// PDO error mode: exception
|
||||
Assert::exception(
|
||||
fn() => buildPdoDriver(PDO::ERRMODE_EXCEPTION),
|
||||
Dibi\DriverException::class,
|
||||
'PDO connection in exception or warning error mode is not supported.',
|
||||
);
|
||||
Assert::exception(function () {
|
||||
buildPdoDriver(PDO::ERRMODE_EXCEPTION);
|
||||
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
|
||||
|
||||
|
||||
// PDO error mode: warning
|
||||
Assert::exception(
|
||||
fn() => buildPdoDriver(PDO::ERRMODE_WARNING),
|
||||
Dibi\DriverException::class,
|
||||
'PDO connection in exception or warning error mode is not supported.',
|
||||
);
|
||||
Assert::exception(function () {
|
||||
buildPdoDriver(PDO::ERRMODE_WARNING);
|
||||
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
|
||||
|
||||
|
||||
test(
|
||||
'PDO error mode: explicitly set silent',
|
||||
fn() => buildPdoDriver(PDO::ERRMODE_SILENT)
|
||||
);
|
||||
test('PDO error mode: explicitly set silent', function () {
|
||||
buildPdoDriver(PDO::ERRMODE_SILENT);
|
||||
});
|
||||
|
@@ -162,10 +162,9 @@ test('', function () {
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning since PHP 7.1
|
||||
} else {
|
||||
Assert::exception(
|
||||
fn() => Assert::same(['col' => 0], $result->test(['col' => ''])),
|
||||
TypeError::class,
|
||||
);
|
||||
Assert::exception(function () use ($result) {
|
||||
Assert::same(['col' => 0], $result->test(['col' => '']));
|
||||
}, TypeError::class);
|
||||
}
|
||||
|
||||
Assert::same(['col' => 0], $result->test(['col' => '0']));
|
||||
@@ -190,10 +189,9 @@ test('', function () {
|
||||
$result->setType('col', Type::DATETIME);
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||
Assert::exception(
|
||||
fn() => $result->test(['col' => true]),
|
||||
TypeError::class,
|
||||
);
|
||||
Assert::exception(function () use ($result) {
|
||||
$result->test(['col' => true]);
|
||||
}, TypeError::class);
|
||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||
@@ -210,10 +208,9 @@ test('', function () {
|
||||
$result->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||
Assert::exception(
|
||||
fn() => $result->test(['col' => true]),
|
||||
TypeError::class,
|
||||
);
|
||||
Assert::exception(function () use ($result) {
|
||||
$result->test(['col' => true]);
|
||||
}, TypeError::class);
|
||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||
@@ -229,10 +226,9 @@ test('', function () {
|
||||
$result->setType('col', Type::DATE);
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||
Assert::exception(
|
||||
fn() => $result->test(['col' => true]),
|
||||
TypeError::class,
|
||||
);
|
||||
Assert::exception(function () use ($result) {
|
||||
$result->test(['col' => true]);
|
||||
}, TypeError::class);
|
||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||
@@ -246,10 +242,9 @@ test('', function () {
|
||||
$result->setType('col', Type::TIME);
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||
Assert::exception(
|
||||
fn() => $result->test(['col' => true]),
|
||||
TypeError::class,
|
||||
);
|
||||
Assert::exception(function () use ($result) {
|
||||
$result->test(['col' => true]);
|
||||
}, TypeError::class);
|
||||
Assert::same(['col' => null], $result->test(['col' => false]));
|
||||
|
||||
Assert::same(['col' => null], $result->test(['col' => '']));
|
||||
|
@@ -83,7 +83,7 @@ $tests = function ($conn) {
|
||||
);
|
||||
|
||||
Assert::exception(
|
||||
fn() => $conn->translate('SELECT 1 %ofs %lmt', 10, 10),
|
||||
$conn->translate('SELECT 1 %ofs %lmt', 10, 10),
|
||||
Dibi\NotSupportedException::class,
|
||||
);
|
||||
}
|
||||
|
151
tests/dibi/Strict.phpt
Normal file
151
tests/dibi/Strict.phpt
Normal file
@@ -0,0 +1,151 @@
|
||||
<?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,16 +13,13 @@ switch ($config['system']) {
|
||||
case 'mysql':
|
||||
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::exception(
|
||||
fn() => $translator->formatValue(new DateInterval('P2Y4DT6H8M'), null),
|
||||
Dibi\NotSupportedException::class,
|
||||
'Only time interval is supported.',
|
||||
);
|
||||
Assert::exception(function () use ($translator) {
|
||||
$translator->formatValue(new DateInterval('P2Y4DT6H8M'), null);
|
||||
}, Dibi\NotSupportedException::class, 'Only time interval is supported.');
|
||||
break;
|
||||
|
||||
default:
|
||||
Assert::exception(
|
||||
fn() => $translator->formatValue(new DateInterval('PT10H20M30S'), null),
|
||||
Dibi\Exception::class,
|
||||
);
|
||||
Assert::exception(function () use ($translator) {
|
||||
$translator->formatValue(new DateInterval('PT10H20M30S'), null);
|
||||
}, Dibi\Exception::class);
|
||||
}
|
||||
|
@@ -91,11 +91,9 @@ Assert::same(
|
||||
);
|
||||
|
||||
// invalid input
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->translate('SELECT %s', (object) [123], ', %m', 123),
|
||||
Dibi\Exception::class,
|
||||
'SQL translate error: Invalid combination of type stdClass and modifier %s',
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->translate('SELECT %s', (object) [123], ', %m', 123);
|
||||
}, 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(
|
||||
@@ -178,10 +176,9 @@ Assert::same(
|
||||
);
|
||||
|
||||
if ($config['system'] === 'odbc') {
|
||||
Assert::exception(
|
||||
fn() => $conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1),
|
||||
Dibi\Exception::class,
|
||||
);
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->translate('SELECT * FROM [products] %lmt %ofs', 2, 1);
|
||||
}, Dibi\Exception::class);
|
||||
} else {
|
||||
// with limit = 2, offset = 1
|
||||
Assert::same(
|
||||
@@ -229,11 +226,9 @@ Assert::same(
|
||||
]),
|
||||
);
|
||||
|
||||
Assert::exception(
|
||||
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',
|
||||
);
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->translate('SELECT %s', new DateTime('1212-09-26'));
|
||||
}, Dibi\Exception::class, 'SQL translate error: Invalid combination of type Dibi\DateTime and modifier %s');
|
||||
|
||||
|
||||
|
||||
@@ -273,11 +268,9 @@ if ($config['system'] === 'postgre') {
|
||||
}
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->translate("SELECT '"),
|
||||
Dibi\Exception::class,
|
||||
'SQL translate error: Alone quote',
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->translate("SELECT '");
|
||||
}, Dibi\Exception::class, 'SQL translate error: Alone quote');
|
||||
Assert::same('SELECT **Alone quote**', $e->getSql());
|
||||
|
||||
Assert::match(
|
||||
@@ -654,17 +647,13 @@ Assert::same(
|
||||
$conn->translate('INSERT INTO [test.*]'),
|
||||
);
|
||||
|
||||
Assert::exception(
|
||||
fn() => $conn->translate('INSERT INTO %i', 'ahoj'),
|
||||
Dibi\Exception::class,
|
||||
"Expected number, 'ahoj' given.",
|
||||
);
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->translate('INSERT INTO %i', 'ahoj');
|
||||
}, Dibi\Exception::class, "Expected number, 'ahoj' given.");
|
||||
|
||||
Assert::exception(
|
||||
fn() => $conn->translate('INSERT INTO %f', 'ahoj'),
|
||||
Dibi\Exception::class,
|
||||
"Expected number, 'ahoj' given.",
|
||||
);
|
||||
Assert::exception(function () use ($conn) {
|
||||
$conn->translate('INSERT INTO %f', 'ahoj');
|
||||
}, Dibi\Exception::class, "Expected number, 'ahoj' given.");
|
||||
|
||||
|
||||
Assert::same(
|
||||
|
@@ -15,54 +15,41 @@ $conn = new Dibi\Connection($config);
|
||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => new Dibi\Connection([
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
new Dibi\Connection([
|
||||
'driver' => 'mysqli',
|
||||
'host' => 'localhost',
|
||||
'username' => 'unknown',
|
||||
'password' => 'unknown',
|
||||
]),
|
||||
Dibi\DriverException::class,
|
||||
);
|
||||
]);
|
||||
}, Dibi\DriverException::class);
|
||||
|
||||
Assert::null($e->getSql());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('SELECT'),
|
||||
Dibi\DriverException::class,
|
||||
'%a% error in your SQL syntax;%a%',
|
||||
1064,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('SELECT');
|
||||
}, Dibi\DriverException::class, '%a% error in your SQL syntax;%a%', 1064);
|
||||
|
||||
Assert::same('SELECT', $e->getSql());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")'),
|
||||
Dibi\UniqueConstraintViolationException::class,
|
||||
"%a?%Duplicate entry '1' for key '%a?%PRIMARY'",
|
||||
1062,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
|
||||
}, 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());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('INSERT INTO products (title) VALUES (NULL)'),
|
||||
Dibi\NotNullConstraintViolationException::class,
|
||||
"%a?%Column 'title' cannot be null",
|
||||
1048,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (title) VALUES (NULL)');
|
||||
}, Dibi\NotNullConstraintViolationException::class, "%a?%Column 'title' cannot be null", 1048);
|
||||
|
||||
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
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,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$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);
|
||||
|
||||
Assert::same('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)', $e->getSql());
|
||||
|
@@ -15,40 +15,29 @@ $conn = new Dibi\Connection($config);
|
||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('SELECT INTO'),
|
||||
Dibi\DriverException::class,
|
||||
'%a?%syntax error %A%',
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('SELECT INTO');
|
||||
}, Dibi\DriverException::class, '%a?%syntax error %A%');
|
||||
|
||||
Assert::same('SELECT INTO', $e->getSql());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")'),
|
||||
Dibi\UniqueConstraintViolationException::class,
|
||||
'%a% violates unique constraint %A%',
|
||||
'23505',
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
|
||||
}, Dibi\UniqueConstraintViolationException::class, '%a% violates unique constraint %A%', '23505');
|
||||
|
||||
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
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',
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (title) VALUES (NULL)');
|
||||
}, 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());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
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',
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO orders (customer_id, product_id, amount) VALUES (100, 1, 1)');
|
||||
}, 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());
|
||||
|
@@ -15,32 +15,23 @@ $conn = new Dibi\Connection($config);
|
||||
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('SELECT'),
|
||||
Dibi\DriverException::class,
|
||||
'%a%',
|
||||
1,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('SELECT');
|
||||
}, Dibi\DriverException::class, '%a%', 1);
|
||||
|
||||
Assert::same('SELECT', $e->getSql());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")'),
|
||||
Dibi\UniqueConstraintViolationException::class,
|
||||
null,
|
||||
19,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (product_id, title) VALUES (1, "New")');
|
||||
}, Dibi\UniqueConstraintViolationException::class, null, 19);
|
||||
|
||||
Assert::same("INSERT INTO products (product_id, title) VALUES (1, 'New')", $e->getSql());
|
||||
|
||||
|
||||
$e = Assert::exception(
|
||||
fn() => $conn->query('INSERT INTO products (title) VALUES (NULL)'),
|
||||
Dibi\NotNullConstraintViolationException::class,
|
||||
null,
|
||||
19,
|
||||
);
|
||||
$e = Assert::exception(function () use ($conn) {
|
||||
$conn->query('INSERT INTO products (title) VALUES (NULL)');
|
||||
}, Dibi\NotNullConstraintViolationException::class, null, 19);
|
||||
|
||||
Assert::same('INSERT INTO products (title) VALUES (NULL)', $e->getSql());
|
||||
|
||||
|
Reference in New Issue
Block a user