mirror of
https://github.com/dg/dibi.git
synced 2025-09-08 21:30:46 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ca74488636 | ||
|
07f994a0b5 | ||
|
5fa5acb724 | ||
|
98563d8165 | ||
|
c464960239 | ||
|
a9e90d0b22 | ||
|
decb30de1e | ||
|
f4e71e8855 | ||
|
39f59e0f08 |
@@ -1,6 +1,5 @@
|
|||||||
language: php
|
language: php
|
||||||
php:
|
php:
|
||||||
- 7.1
|
|
||||||
- 7.2
|
- 7.2
|
||||||
- 7.3
|
- 7.3
|
||||||
- 7.4
|
- 7.4
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1"
|
"php": ">=7.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"tracy/tracy": "~2.2",
|
"tracy/tracy": "~2.2",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "4.1-dev"
|
"dev-master": "4.2-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,7 @@ Install Dibi via Composer:
|
|||||||
composer require dibi/dibi
|
composer require dibi/dibi
|
||||||
```
|
```
|
||||||
|
|
||||||
The Dibi 4.1 requires PHP version 7.1 and supports PHP up to 8.0.
|
The Dibi 4.2 requires PHP version 7.2 and supports PHP up to 8.0.
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
|
@@ -28,6 +28,9 @@ class Connection implements IConnection
|
|||||||
/** @var array Current connection configuration */
|
/** @var array Current connection configuration */
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
|
/** @var string[] resultset formats */
|
||||||
|
private $formats;
|
||||||
|
|
||||||
/** @var Driver|null */
|
/** @var Driver|null */
|
||||||
private $driver;
|
private $driver;
|
||||||
|
|
||||||
@@ -42,12 +45,19 @@ class Connection implements IConnection
|
|||||||
* Connection options: (see driver-specific options too)
|
* Connection options: (see driver-specific options too)
|
||||||
* - lazy (bool) => if true, connection will be established only when required
|
* - lazy (bool) => if true, connection will be established only when required
|
||||||
* - result (array) => result set options
|
* - result (array) => result set options
|
||||||
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
|
* - normalize => normalizes result fields (default: true)
|
||||||
* - formatJson => json format (
|
* - formatDateTime => date-time format
|
||||||
* "string" for leaving value as is,
|
* empty for decoding as Dibi\DateTime (default)
|
||||||
* "object" for decoding json as \stdClass,
|
* "..." formatted according to given format, see https://www.php.net/manual/en/datetime.format.php
|
||||||
* "array" for decoding json as an array - default
|
* "native" for leaving value as is
|
||||||
* )
|
* - formatTimeInterval => time-interval format
|
||||||
|
* empty for decoding as DateInterval (default)
|
||||||
|
* "..." formatted according to given format, see https://www.php.net/manual/en/dateinterval.format.php
|
||||||
|
* "native" for leaving value as is
|
||||||
|
* - formatJson => json format
|
||||||
|
* "array" for decoding json as an array (default)
|
||||||
|
* "object" for decoding json as \stdClass
|
||||||
|
* "native" for leaving value as is
|
||||||
* - profiler (array)
|
* - profiler (array)
|
||||||
* - run (bool) => enable profiler?
|
* - run (bool) => enable profiler?
|
||||||
* - file => file to log
|
* - file => file to log
|
||||||
@@ -65,9 +75,15 @@ class Connection implements IConnection
|
|||||||
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
|
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
|
||||||
$config['driver'] = $config['driver'] ?? 'mysqli';
|
$config['driver'] = $config['driver'] ?? 'mysqli';
|
||||||
$config['name'] = $name;
|
$config['name'] = $name;
|
||||||
$config['result']['formatJson'] = $config['result']['formatJson'] ?? 'array';
|
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
|
||||||
|
$this->formats = [
|
||||||
|
Type::DATE => $this->config['result']['formatDate'],
|
||||||
|
Type::DATETIME => $this->config['result']['formatDateTime'],
|
||||||
|
Type::JSON => $this->config['result']['formatJson'] ?? 'array',
|
||||||
|
Type::TIME_INTERVAL => $this->config['result']['formatTimeInterval'] ?? null,
|
||||||
|
];
|
||||||
|
|
||||||
// profiler
|
// profiler
|
||||||
if (isset($config['profiler']['file']) && (!isset($config['profiler']['run']) || $config['profiler']['run'])) {
|
if (isset($config['profiler']['file']) && (!isset($config['profiler']['run']) || $config['profiler']['run'])) {
|
||||||
$filter = $config['profiler']['filter'] ?? Event::QUERY;
|
$filter = $config['profiler']['filter'] ?? Event::QUERY;
|
||||||
@@ -200,7 +216,7 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
final public function query(...$args): Result
|
final public function query(...$args): Result
|
||||||
{
|
{
|
||||||
return $this->nativeQuery($this->translateArgs($args));
|
return $this->nativeQuery($this->translate(...$args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -211,7 +227,10 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
final public function translate(...$args): string
|
final public function translate(...$args): string
|
||||||
{
|
{
|
||||||
return $this->translateArgs($args);
|
if (!$this->driver) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
return (clone $this->translator)->translate($args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -222,7 +241,7 @@ class Connection implements IConnection
|
|||||||
final public function test(...$args): bool
|
final public function test(...$args): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Helpers::dump($this->translateArgs($args));
|
Helpers::dump($this->translate(...$args));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@@ -243,19 +262,7 @@ class Connection implements IConnection
|
|||||||
*/
|
*/
|
||||||
final public function dataSource(...$args): DataSource
|
final public function dataSource(...$args): DataSource
|
||||||
{
|
{
|
||||||
return new DataSource($this->translateArgs($args), $this);
|
return new DataSource($this->translate(...$args), $this);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates SQL query.
|
|
||||||
*/
|
|
||||||
protected function translateArgs(array $args): string
|
|
||||||
{
|
|
||||||
if (!$this->driver) {
|
|
||||||
$this->connect();
|
|
||||||
}
|
|
||||||
return (clone $this->translator)->translate($args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -395,15 +402,30 @@ class Connection implements IConnection
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function transaction(callable $callback)
|
||||||
|
{
|
||||||
|
$this->begin();
|
||||||
|
try {
|
||||||
|
$res = $callback();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->rollback();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
$this->commit();
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result set factory.
|
* Result set factory.
|
||||||
*/
|
*/
|
||||||
public function createResultSet(ResultDriver $resultDriver): Result
|
public function createResultSet(ResultDriver $resultDriver): Result
|
||||||
{
|
{
|
||||||
$res = new Result($resultDriver);
|
return (new Result($resultDriver, $this->config['result']['normalize'] ?? true))
|
||||||
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
|
->setFormats($this->formats);
|
||||||
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime'])
|
|
||||||
->setFormat(Type::JSON, $this->config['result']['formatJson']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -63,13 +63,11 @@ class SqlsrvDriver implements Dibi\Driver
|
|||||||
$options['UID'] = (string) $options['UID'];
|
$options['UID'] = (string) $options['UID'];
|
||||||
$options['Database'] = (string) $options['Database'];
|
$options['Database'] = (string) $options['Database'];
|
||||||
|
|
||||||
sqlsrv_configure('WarningsReturnAsErrors', 0);
|
|
||||||
$this->connection = sqlsrv_connect($config['host'], $options);
|
$this->connection = sqlsrv_connect($config['host'], $options);
|
||||||
sqlsrv_configure('WarningsReturnAsErrors', 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_resource($this->connection)) {
|
if (!is_resource($this->connection)) {
|
||||||
$info = sqlsrv_errors(SQLSRV_ERR_ERRORS);
|
$info = sqlsrv_errors();
|
||||||
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
|
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
|
||||||
}
|
}
|
||||||
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
|
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
|
||||||
|
@@ -41,10 +41,12 @@ class Result implements IDataSource
|
|||||||
private $formats = [];
|
private $formats = [];
|
||||||
|
|
||||||
|
|
||||||
public function __construct(ResultDriver $driver)
|
public function __construct(ResultDriver $driver, bool $normalize = true)
|
||||||
{
|
{
|
||||||
$this->driver = $driver;
|
$this->driver = $driver;
|
||||||
$this->detectTypes();
|
if ($normalize) {
|
||||||
|
$this->detectTypes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -454,8 +456,12 @@ class Result implements IDataSource
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$value = $row[$key];
|
$value = $row[$key];
|
||||||
|
$format = $this->formats[$type] ?? null;
|
||||||
|
|
||||||
if ($type === Type::TEXT) {
|
if ($type === null || $format === 'native') {
|
||||||
|
$row[$key] = $value;
|
||||||
|
|
||||||
|
} elseif ($type === Type::TEXT) {
|
||||||
$row[$key] = (string) $value;
|
$row[$key] = (string) $value;
|
||||||
|
|
||||||
} elseif ($type === Type::INTEGER) {
|
} elseif ($type === Type::INTEGER) {
|
||||||
@@ -485,17 +491,16 @@ class Result implements IDataSource
|
|||||||
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
|
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
|
||||||
if ($value && substr((string) $value, 0, 3) !== '000') { // '', null, false, '0000-00-00', ...
|
if ($value && substr((string) $value, 0, 3) !== '000') { // '', null, false, '0000-00-00', ...
|
||||||
$value = new DateTime($value);
|
$value = new DateTime($value);
|
||||||
$row[$key] = empty($this->formats[$type])
|
$row[$key] = $format ? $value->format($format) : $value;
|
||||||
? $value
|
|
||||||
: $value->format($this->formats[$type]);
|
|
||||||
} else {
|
} else {
|
||||||
$row[$key] = null;
|
$row[$key] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} elseif ($type === Type::TIME_INTERVAL) {
|
} elseif ($type === Type::TIME_INTERVAL) {
|
||||||
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
|
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
|
||||||
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
$value = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
|
||||||
$row[$key]->invert = (int) (bool) $m[1];
|
$value->invert = (int) (bool) $m[1];
|
||||||
|
$row[$key] = $format ? $value->format($format) : $value;
|
||||||
|
|
||||||
} elseif ($type === Type::BINARY) {
|
} elseif ($type === Type::BINARY) {
|
||||||
$row[$key] = is_string($value)
|
$row[$key] = is_string($value)
|
||||||
@@ -503,15 +508,12 @@ class Result implements IDataSource
|
|||||||
: $value;
|
: $value;
|
||||||
|
|
||||||
} elseif ($type === Type::JSON) {
|
} elseif ($type === Type::JSON) {
|
||||||
if ($this->formats[$type] === 'string') {
|
if ($format === 'string') { // back compatibility with 'native'
|
||||||
$row[$key] = $value;
|
$row[$key] = $value;
|
||||||
} else {
|
} else {
|
||||||
$row[$key] = json_decode($value, $this->formats[$type] === 'array');
|
$row[$key] = json_decode($value, $format === 'array');
|
||||||
}
|
}
|
||||||
|
|
||||||
} elseif ($type === null) {
|
|
||||||
$row[$key] = $value;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException('Unexpected type ' . $type);
|
throw new \RuntimeException('Unexpected type ' . $type);
|
||||||
}
|
}
|
||||||
@@ -549,7 +551,7 @@ class Result implements IDataSource
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets date format.
|
* Sets type format.
|
||||||
*/
|
*/
|
||||||
final public function setFormat(string $type, ?string $format): self
|
final public function setFormat(string $type, ?string $format): self
|
||||||
{
|
{
|
||||||
@@ -558,6 +560,16 @@ class Result implements IDataSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets type formats.
|
||||||
|
*/
|
||||||
|
final public function setFormats(array $formats): self
|
||||||
|
{
|
||||||
|
$this->formats = $formats;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns data format.
|
* Returns data format.
|
||||||
*/
|
*/
|
||||||
|
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
|||||||
* @method static void begin(string $savepoint = null)
|
* @method static void begin(string $savepoint = null)
|
||||||
* @method static void commit(string $savepoint = null)
|
* @method static void commit(string $savepoint = null)
|
||||||
* @method static void rollback(string $savepoint = null)
|
* @method static void rollback(string $savepoint = null)
|
||||||
|
* @method static mixed transaction(callable $callback)
|
||||||
* @method static Dibi\Reflection\Database getDatabaseInfo()
|
* @method static Dibi\Reflection\Database getDatabaseInfo()
|
||||||
* @method static Dibi\Fluent command()
|
* @method static Dibi\Fluent command()
|
||||||
* @method static Dibi\Fluent select(...$args)
|
* @method static Dibi\Fluent select(...$args)
|
||||||
@@ -44,7 +45,7 @@ class dibi
|
|||||||
|
|
||||||
/** version */
|
/** version */
|
||||||
public const
|
public const
|
||||||
VERSION = '4.1.3';
|
VERSION = '4.2.0';
|
||||||
|
|
||||||
/** sorting order */
|
/** sorting order */
|
||||||
public const
|
public const
|
||||||
|
@@ -48,3 +48,24 @@ $conn->query('INSERT INTO [products]', [
|
|||||||
]);
|
]);
|
||||||
$conn->commit();
|
$conn->commit();
|
||||||
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Assert::exception(function () use ($conn) {
|
||||||
|
$conn->transaction(function () use ($conn) {
|
||||||
|
$conn->query('INSERT INTO [products]', [
|
||||||
|
'title' => 'Test product',
|
||||||
|
]);
|
||||||
|
throw new Exception('my exception');
|
||||||
|
});
|
||||||
|
}, \Throwable::class, 'my exception');
|
||||||
|
|
||||||
|
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
|
|
||||||
|
$conn->transaction(function () use ($conn) {
|
||||||
|
$conn->query('INSERT INTO [products]', [
|
||||||
|
'title' => 'Test product',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert::same(5, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
|
||||||
|
@@ -24,6 +24,17 @@ class MockResult extends Dibi\Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test('', function () {
|
||||||
|
$result = new MockResult;
|
||||||
|
$result->setType('col', Type::TEXT);
|
||||||
|
$result->setFormat(Type::TEXT, 'native');
|
||||||
|
|
||||||
|
Assert::same(['col' => null], $result->test(['col' => null]));
|
||||||
|
Assert::same(['col' => true], $result->test(['col' => true]));
|
||||||
|
Assert::same(['col' => false], $result->test(['col' => false]));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('', function () {
|
test('', function () {
|
||||||
$result = new MockResult;
|
$result = new MockResult;
|
||||||
$result->setType('col', Type::BOOL);
|
$result->setType('col', Type::BOOL);
|
||||||
|
Reference in New Issue
Block a user