1
0
mirror of https://github.com/dg/dibi.git synced 2025-08-30 17:29:53 +02:00

Compare commits

..

23 Commits

Author SHA1 Message Date
David Grudl
0d2f643795 strict comparison 2020-11-02 15:27:54 +01:00
David Grudl
70d4246866 Tracy\Panel: table is sortable 2020-11-02 15:05:55 +01:00
David Grudl
34a1665915 coding style 2020-11-02 15:05:55 +01:00
David Grudl
b27db4a9aa updated gitattributes 2020-11-02 15:05:55 +01:00
David Grudl
212dd1ae55 updated phpstan 2020-11-02 15:05:55 +01:00
David Grudl
b9683f8a3c updated nette/coding-standard 2020-11-02 15:05:55 +01:00
David Grudl
177a800bff PdoDriver: changes error mode do ERRMODE_SILENT (for PHP 8.0) 2020-11-02 15:05:55 +01:00
David Grudl
fa6a1203a9 fixed compatibility with PHP 8 [Closes #379] 2020-11-02 15:05:55 +01:00
David Grudl
3f7171c7a4 tested on PHP 8.0 2020-10-30 13:50:54 +01:00
David Grudl
e4b6e769ee Dibi\Helpers::getSuggestion(): item may be an int, type cast fix (#378) 2020-10-14 12:00:23 +02:00
David Grudl
24d0b069d8 Helpers::getSuggestion(): support for reflection objects moved to Strict 2020-10-14 12:00:04 +02:00
David Grudl
34bc742245 refactoring 2020-10-08 16:49:47 +02:00
Miloslav Hůla
f5fa2255ff FileLogger: fixed object to string conversion with custom driver (#376) (#376)
Co-authored-by: Miloslav Hůla <miloslav.hula@fsv.cvut.cz>
2020-10-08 16:38:35 +02:00
Miloslav Hůla
7b02296f3e Tracy\Panel: fixed object to string converion error with custom driver (#373)
Co-authored-by: Miloslav Hůla <miloslav.hula@fsv.cvut.cz>
2020-10-08 16:38:35 +02:00
David Grudl
df4cddac1f Tracy\Panel: supports multiple connections [Closes #365]
Partially reverts "Tracy\Panel: one panel is used per connection"

This reverts commit ae6c8756b6.
2020-10-08 16:38:35 +02:00
groupnet
cc37121390 Postgre driver - add connect_type config parameter (#370)
- adds the option to pass the connect_type as config parameter
 - PHP default is 0, but to make this change non-breaking defauls to PGSQL_CONNECT_FORCE_NEW
 - see PHP docs: https://www.php.net/manual/en/function.pg-connect.php
2020-10-08 15:55:11 +02:00
LHavlicek
a95b409231 MySqliDriver: fixed DateInterval encoding (#371) 2020-10-08 15:55:11 +02:00
Jakub Bouček
3b057c2e35 MySqliDriver: added support for ping (#372) 2020-10-08 15:55:11 +02:00
David Grudl
f444b5d993 typo 2020-10-08 15:55:11 +02:00
David Grudl
6e41c4223b tests: test() with description 2020-10-08 15:55:11 +02:00
David Grudl
0ee4628712 SqlsrvDriver: improved escapeBinary [Closes #287] 2020-10-07 03:19:03 +02:00
David Grudl
ab3677203c added funding.yml 2020-10-07 03:19:03 +02:00
Jan Barášek
1bdf6e93d0 PhpStan fixes (#363) 2020-05-07 21:41:28 +02:00
43 changed files with 416 additions and 261 deletions

6
.gitattributes vendored
View File

@@ -2,5 +2,9 @@
.gitignore export-ignore
.github export-ignore
.travis.yml export-ignore
appveyor.yml export-ignore
ecs.php export-ignore
phpstan.neon export-ignore
tests/ export-ignore
*.sh eol=lf
*.php* diff=php linguist-language=PHP

1
.github/funding.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: dg

View File

@@ -4,6 +4,7 @@ php:
- 7.2
- 7.3
- 7.4
- 8.0snapshot
services:
- mysql
@@ -32,7 +33,6 @@ after_failure:
jobs:
include:
- name: Nette Code Checker
php: 7.4
install:
- travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress
script:
@@ -42,9 +42,9 @@ jobs:
- name: Nette Coding Standard
php: 7.4
install:
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress
script:
- php temp/coding-standard/ecs check src tests examples --config tests/coding-standard.yml
- php temp/coding-standard/ecs check src tests
- stage: Static Analysis (informative)
@@ -67,7 +67,7 @@ jobs:
- stage: Code Coverage
sudo: false
dist: xenial
cache:
directories:

View File

@@ -16,6 +16,7 @@
"require-dev": {
"tracy/tracy": "~2.2",
"nette/tester": "~2.0",
"nette/di": "^3.0",
"phpstan/phpstan": "^0.12"
},
"replace": {
@@ -25,7 +26,7 @@
"classmap": ["src/"]
},
"scripts": {
"phpstan": "phpstan analyse --autoload-file vendor/autoload.php --level 5 --configuration tests/phpstan.neon src",
"phpstan": "phpstan analyse",
"tester": "tester tests -s"
},
"extra": {

21
ecs.php Normal file
View File

@@ -0,0 +1,21 @@
<?php
/**
* Rules for Nette Coding Standard
* https://github.com/nette/coding-standard
*/
declare(strict_types=1);
return function (Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->import(PRESET_DIR . '/php71.php');
$parameters = $containerConfigurator->parameters();
$parameters->set('skip', [
// issue #260
PhpCsFixer\Fixer\Operator\TernaryToNullCoalescingFixer::class => ['src/Dibi/HashMap.php'],
SlevomatCodingStandard\Sniffs\ControlStructures\RequireNullCoalesceOperatorSniff::class => ['src/Dibi/HashMap.php'],
]);
};

View File

@@ -1,4 +1,9 @@
parameters:
level: 5
paths:
- src
ignoreErrors:
# The namespace is referenced, not the class.
- '#Class dibi referenced with incorrect case: Dibi#'

View File

@@ -14,7 +14,13 @@ Introduction
Database access functions in PHP are not standardised. This library
hides the differences between them, and above all, it gives you a very handy interface.
If you like Dibi, **[please make a donation now](https://nette.org/make-donation?to=dibi)**. Thank you!
Support Project
---------------
Do you like Dibi? Are you looking forward to the new features?
[![Donate](https://files.nette.org/icons/donation-1.svg?)](https://nette.org/make-donation?to=dibi)
Installation
@@ -26,7 +32,7 @@ Install Dibi via Composer:
composer require dibi/dibi
```
The Dibi 4.1 requires PHP version 7.1 and supports PHP up to 7.4.
The Dibi 4.1 requires PHP version 7.1 and supports PHP up to 8.0.
Usage
@@ -42,11 +48,11 @@ The database connection is represented by the object `Dibi\Connection`:
```php
$database = new Dibi\Connection([
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'table',
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'table',
]);
$result = $database->query('SELECT * FROM users');
@@ -56,12 +62,12 @@ Alternatively, you can use the `dibi` static register, which maintains a connect
```php
dibi::connect([
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'test',
'charset' => 'utf8',
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
'database' => 'test',
'charset' => 'utf8',
]);
$result = dibi::query('SELECT * FROM users');
@@ -229,8 +235,8 @@ Example:
```php
$arr = [
'a' => 'hello',
'b' => true,
'a' => 'hello',
'b' => true,
];
$database->query('INSERT INTO table %v', $arr);
@@ -502,7 +508,7 @@ $all = $result->fetchAssoc('customer_id|order_id');
// we will iterate like this:
foreach ($all as $customerId => $orders) {
foreach ($orders as $orderId => $order) {
...
...
}
}
```
@@ -534,7 +540,7 @@ $all = $result->fetchAssoc('name[]order_id');
// we get all the Arnolds in the results
foreach ($all['Arnold Rimmer'] as $arnoldOrders) {
foreach ($arnoldOrders as $orderId => $order) {
...
...
}
}
```
@@ -548,8 +554,8 @@ foreach ($all as $customerId => $orders) {
echo "Customer $customerId":
foreach ($orders as $orderId => $order) {
echo "ID number: $order->number";
// customer name is in $order->name
echo "ID number: $order->number";
// customer name is in $order->name
}
}
```
@@ -571,7 +577,7 @@ foreach ($all as $customerId => $row) {
echo "Customer $row->name":
foreach ($row->order_id as $orderId => $order) {
echo "ID number: $order->number";
echo "ID number: $order->number";
}
}
```

View File

@@ -45,7 +45,7 @@ class Panel implements Tracy\IBarPanel
public function register(Dibi\Connection $connection): void
{
Tracy\Debugger::getBar()->addPanel($this);
Tracy\Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
Tracy\Debugger::getBlueScreen()->addPanel([self::class, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
@@ -105,6 +105,15 @@ class Panel implements Tracy\IBarPanel
}
$totalTime = $s = null;
$singleConnection = reset($this->events)->connection;
foreach ($this->events as $event) {
if ($event->connection !== $singleConnection) {
$singleConnection = null;
break;
}
}
foreach ($this->events as $event) {
$totalTime += $event->time;
$connection = $event->connection;
@@ -112,7 +121,9 @@ class Panel implements Tracy\IBarPanel
if ($this->explain && $event->type === Event::SELECT) {
$backup = [$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$connection->onEvent = null;
$cmd = is_string($this->explain) ? $this->explain : ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$cmd = is_string($this->explain)
? $this->explain
: ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
try {
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), true);
} catch (Dibi\Exception $e) {
@@ -120,7 +131,7 @@ class Panel implements Tracy\IBarPanel
[$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime] = $backup;
}
$s .= '<tr><td>' . number_format($event->time * 1000, 3, '.', '');
$s .= '<tr><td data-order="' . $event->time . '">' . number_format($event->time * 1000, 3, '.', '');
if ($explain) {
static $counter;
$counter++;
@@ -132,10 +143,13 @@ class Panel implements Tracy\IBarPanel
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
}
if ($event->source) {
$s .= Tracy\Helpers::editorLink($event->source[0], $event->source[1]);//->class('tracy-DibiProfiler-source');
$s .= Tracy\Helpers::editorLink($event->source[0], $event->source[1]); //->class('tracy-DibiProfiler-source');
}
$s .= "</td><td>{$event->count}</td></tr>";
$s .= "</td><td>{$event->count}</td>";
if (!$singleConnection) {
$s .= '<td>' . htmlspecialchars($this->getConnectionName($connection)) . '</td></tr>';
}
}
return '<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
@@ -143,12 +157,21 @@ class Panel implements Tracy\IBarPanel
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
<h1>Queries: ' . count($this->events)
. ($totalTime === null ? '' : ', time: ' . number_format($totalTime * 1000, 1, '.', '') . 'ms') . ', '
. htmlspecialchars($connection->getConfig('driver') . ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
. ($connection->getConfig('host') ? '@' . $connection->getConfig('host') : '')) . '</h1>
. htmlspecialchars($this->getConnectionName($singleConnection)) . '</h1>
<div class="tracy-inner tracy-DibiProfiler">
<table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th></tr>' . $s . '
<table class="tracy-sortable">
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th>' . (!$singleConnection ? '<th>Connection</th>' : '') . '</tr>
' . $s . '
</table>
</div>';
}
private function getConnectionName(Dibi\Connection $connection): string
{
$driver = $connection->getConfig('driver');
return (is_object($driver) ? get_class($driver) : $driver)
. ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
. ($connection->getConfig('host') ? '@' . $connection->getConfig('host') : '');
}
}

View File

@@ -562,7 +562,7 @@ class Connection implements IConnection
*/
public function __wakeup()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
}
@@ -571,7 +571,7 @@ class Connection implements IConnection
*/
public function __sleep()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
throw new NotSupportedException('You cannot serialize or unserialize ' . static::class . ' instances.');
}

View File

@@ -53,11 +53,9 @@ class DataSource implements IDataSource
*/
public function __construct(string $sql, Connection $connection)
{
if (strpbrk($sql, " \t\r\n") === false) {
$this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name
} else {
$this->sql = '(' . $sql . ') t'; // SQL command
}
$this->sql = strpbrk($sql, " \t\r\n") === false
? $connection->getDriver()->escapeIdentifier($sql) // table name
: '(' . $sql . ') t'; // SQL command
$this->connection = $connection;
}
@@ -84,12 +82,9 @@ class DataSource implements IDataSource
*/
public function where($cond): self
{
if (is_array($cond)) {
// TODO: not consistent with select and orderBy
$this->conds[] = $cond;
} else {
$this->conds[] = func_get_args();
}
$this->conds[] = is_array($cond)
? $cond // TODO: not consistent with select and orderBy
: func_get_args();
$this->result = $this->count = null;
return $this;
}
@@ -232,12 +227,18 @@ class DataSource implements IDataSource
public function __toString(): string
{
try {
return $this->connection->translate('
SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
FROM %SQL', $this->sql, '
%ex', $this->conds ? ['WHERE %and', $this->conds] : null, '
%ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : null, '
%ofs %lmt', $this->offset, $this->limit
return $this->connection->translate(
"\nSELECT %n",
(empty($this->cols) ? '*' : $this->cols),
"\nFROM %SQL",
$this->sql,
"\n%ex",
$this->conds ? ['WHERE %and', $this->conds] : null,
"\n%ex",
$this->sorting ? ['ORDER BY %by', $this->sorting] : null,
"\n%ofs %lmt",
$this->offset,
$this->limit
);
} catch (\Throwable $e) {
trigger_error($e->getMessage(), E_USER_ERROR);

View File

@@ -62,11 +62,9 @@ class FirebirdDriver implements Dibi\Driver
'buffers' => 0,
];
if (empty($config['persistent'])) {
$this->connection = @ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else {
$this->connection = @ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
}
$this->connection = empty($config['persistent'])
? @ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']) // intentionally @
: @ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
@@ -90,11 +88,13 @@ class FirebirdDriver implements Dibi\Driver
*/
public function query(string $sql): ?Dibi\ResultDriver
{
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$resource = $this->inTransaction
? $this->transaction
: $this->connection;
$res = ibase_query($resource, $sql);
if ($res === false) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
if (ibase_errcode() === self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);

View File

@@ -38,8 +38,8 @@ class FirebirdReflector implements Dibi\Reflector
SELECT TRIM(RDB\$RELATION_NAME),
CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
WHERE RDB\$SYSTEM_FLAG = 0;
");
$tables = [];
while ($row = $res->fetch(false)) {
$tables[] = [
@@ -84,9 +84,8 @@ class FirebirdReflector implements Dibi\Reflector
FROM RDB\$RELATION_FIELDS r
LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME
WHERE r.RDB\$RELATION_NAME = '$table'
ORDER BY r.RDB\$FIELD_POSITION;"
);
ORDER BY r.RDB\$FIELD_POSITION;
");
$columns = [];
while ($row = $res->fetch(true)) {
$key = $row['FIELD_NAME'];
@@ -121,8 +120,8 @@ class FirebirdReflector implements Dibi\Reflector
LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
ORDER BY s.RDB\$FIELD_POSITION
");
$indexes = [];
while ($row = $res->fetch(true)) {
$key = $row['INDEX_NAME'];
@@ -149,8 +148,8 @@ class FirebirdReflector implements Dibi\Reflector
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
ORDER BY s.RDB\$FIELD_POSITION
");
$keys = [];
while ($row = $res->fetch(true)) {
$key = $row['INDEX_NAME'];
@@ -174,8 +173,8 @@ class FirebirdReflector implements Dibi\Reflector
FROM RDB\$INDICES
WHERE RDB\$RELATION_NAME = UPPER('$table')
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
AND RDB\$FOREIGN_KEY IS NULL;
");
$indices = [];
while ($row = $res->fetch(false)) {
$indices[] = $row[0];
@@ -196,8 +195,8 @@ class FirebirdReflector implements Dibi\Reflector
AND (
RDB\$UNIQUE_FLAG IS NOT NULL
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
);
");
$constraints = [];
while ($row = $res->fetch(false)) {
$constraints[] = $row[0];
@@ -212,7 +211,8 @@ class FirebirdReflector implements Dibi\Reflector
*/
public function getTriggersMeta(string $table = null): array
{
$res = $this->driver->query("
$res = $this->driver->query(
"
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
TRIM(RDB\$RELATION_NAME) AS TABLE_NAME,
CASE RDB\$TRIGGER_TYPE
@@ -261,7 +261,9 @@ class FirebirdReflector implements Dibi\Reflector
$q = 'SELECT TRIM(RDB$TRIGGER_NAME)
FROM RDB$TRIGGERS
WHERE RDB$SYSTEM_FLAG = 0';
$q .= $table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$q .= $table === null
? ';'
: " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->driver->query($q);
$triggers = [];
@@ -307,8 +309,8 @@ class FirebirdReflector implements Dibi\Reflector
p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER
FROM RDB\$PROCEDURE_PARAMETERS p
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;
");
$procedures = [];
while ($row = $res->fetch(true)) {
$key = $row['PROCEDURE_NAME'];
@@ -330,8 +332,8 @@ class FirebirdReflector implements Dibi\Reflector
{
$res = $this->driver->query('
SELECT TRIM(RDB$PROCEDURE_NAME)
FROM RDB$PROCEDURES;'
);
FROM RDB$PROCEDURES;
');
$procedures = [];
while ($row = $res->fetch(false)) {
$procedures[] = $row[0];
@@ -348,8 +350,8 @@ class FirebirdReflector implements Dibi\Reflector
$res = $this->driver->query('
SELECT TRIM(RDB$GENERATOR_NAME)
FROM RDB$GENERATORS
WHERE RDB$SYSTEM_FLAG = 0;'
);
WHERE RDB$SYSTEM_FLAG = 0;
');
$generators = [];
while ($row = $res->fetch(false)) {
$generators[] = $row[0];
@@ -366,8 +368,8 @@ class FirebirdReflector implements Dibi\Reflector
$res = $this->driver->query('
SELECT TRIM(RDB$FUNCTION_NAME)
FROM RDB$FUNCTIONS
WHERE RDB$SYSTEM_FLAG = 0;'
);
WHERE RDB$SYSTEM_FLAG = 0;
');
$functions = [];
while ($row = $res->fetch(false)) {
$functions[] = $row[0];

View File

@@ -62,10 +62,12 @@ class FirebirdResult implements Dibi\ResultDriver
*/
public function fetch(bool $assoc): ?array
{
$result = $assoc ? @ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
$result = $assoc
? @ibase_fetch_assoc($this->resultSet, IBASE_TEXT)
: @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (ibase_errcode()) {
if (ibase_errcode() == FirebirdDriver::ERROR_EXCEPTION_THROWN) {
if (ibase_errcode() === FirebirdDriver::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);

View File

@@ -130,6 +130,15 @@ class MySqliDriver implements Dibi\Driver
}
/**
* Pings a server connection, or tries to reconnect if the connection has gone down.
*/
public function ping(): bool
{
return $this->connection->ping();
}
/**
* Executes the SQL query.
* @throws Dibi\DriverException
@@ -148,6 +157,9 @@ class MySqliDriver implements Dibi\Driver
}
/**
* @param int|string $code
*/
public static function createException(string $message, $code, string $sql): Dibi\DriverException
{
if (in_array($code, [1216, 1217, 1451, 1452, 1701], true)) {
@@ -188,7 +200,9 @@ class MySqliDriver implements Dibi\Driver
*/
public function getAffectedRows(): ?int
{
return $this->connection->affected_rows === -1 ? null : $this->connection->affected_rows;
return $this->connection->affected_rows === -1
? null
: $this->connection->affected_rows;
}
@@ -305,7 +319,7 @@ class MySqliDriver implements Dibi\Driver
if ($value->y || $value->m || $value->d) {
throw new Dibi\NotSupportedException('Only time interval is supported.');
}
return $value->format('%r%H:%I:%S.%f');
return $value->format("'%r%H:%I:%S.%f'");
}

View File

@@ -54,11 +54,9 @@ class OdbcDriver implements Dibi\Driver
'dsn' => ini_get('odbc.default_db'),
];
if (empty($config['persistent'])) {
$this->connection = @odbc_connect($config['dsn'], $config['username'] ?? '', $config['password'] ?? ''); // intentionally @
} else {
$this->connection = @odbc_pconnect($config['dsn'], $config['username'] ?? '', $config['password'] ?? ''); // intentionally @
}
$this->connection = empty($config['persistent'])
? @odbc_connect($config['dsn'], $config['username'] ?? '', $config['password'] ?? '') // intentionally @
: @odbc_pconnect($config['dsn'], $config['username'] ?? '', $config['password'] ?? ''); // intentionally @
}
if (!is_resource($this->connection)) {
@@ -94,7 +92,9 @@ class OdbcDriver implements Dibi\Driver
} elseif (is_resource($res)) {
$this->affectedRows = Dibi\Helpers::false2Null(odbc_num_rows($res));
return odbc_num_fields($res) ? $this->createResultDriver($res) : null;
return odbc_num_fields($res)
? $this->createResultDriver($res)
: null;
}
return null;
}
@@ -124,7 +124,7 @@ class OdbcDriver implements Dibi\Driver
*/
public function begin(string $savepoint = null): void
{
if (!odbc_autocommit($this->connection, 0/*false*/)) {
if (!odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 0 : false)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
}
@@ -139,7 +139,7 @@ class OdbcDriver implements Dibi\Driver
if (!odbc_commit($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, 1/*true*/);
odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 1 : true);
}
@@ -152,7 +152,7 @@ class OdbcDriver implements Dibi\Driver
if (!odbc_rollback($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, 1/*true*/);
odbc_autocommit($this->connection, PHP_VERSION_ID < 80000 ? 1 : true);
}

View File

@@ -96,7 +96,9 @@ class OracleDriver implements Dibi\Driver
} elseif (is_resource($res)) {
$this->affectedRows = Dibi\Helpers::false2Null(oci_num_rows($res));
return oci_num_fields($res) ? $this->createResultDriver($res) : null;
return oci_num_fields($res)
? $this->createResultDriver($res)
: null;
}
} else {
$err = oci_error($this->connection);

View File

@@ -56,9 +56,15 @@ class PdoDriver implements Dibi\Driver
if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource'];
unset($config['resource'], $config['pdo']);
if ($this->connection->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_SILENT) {
throw new Dibi\DriverException('PDO connection in exception or warning error mode is not supported.');
}
} else {
try {
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
} catch (\PDOException $e) {
if ($e->getMessage() === 'could not find driver') {
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
@@ -67,10 +73,6 @@ class PdoDriver implements Dibi\Driver
}
}
if ($this->connection->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_SILENT) {
throw new Dibi\DriverException('PDO connection in exception or warning error mode is not supported.');
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
$this->serverVersion = (string) ($config['version'] ?? @$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION)); // @ - may be not supported
}
@@ -232,21 +234,17 @@ class PdoDriver implements Dibi\Driver
*/
public function escapeText(string $value): string
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_STR);
}
return $this->driverName === 'odbc'
? "'" . str_replace("'", "''", $value) . "'"
: $this->connection->quote($value, PDO::PARAM_STR);
}
public function escapeBinary(string $value): string
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_LOB);
}
return $this->driverName === 'odbc'
? "'" . str_replace("'", "''", $value) . "'"
: $this->connection->quote($value, PDO::PARAM_LOB);
}

View File

@@ -85,7 +85,7 @@ class PdoResult implements Dibi\ResultDriver
if ($row === false) {
throw new Dibi\NotSupportedException('Driver does not support meta data.');
}
$row = $row + [
$row += [
'table' => null,
'native_type' => 'VAR_STRING',
];

View File

@@ -23,6 +23,7 @@ use Dibi\Helpers;
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - connect_type (int) => see pg_connect()
*/
class PostgreDriver implements Dibi\Driver
{
@@ -62,15 +63,14 @@ class PostgreDriver implements Dibi\Driver
}
}
}
$connectType = $config['connect_type'] ?? PGSQL_CONNECT_FORCE_NEW;
set_error_handler(function (int $severity, string $message) use (&$error) {
$error = $message;
});
if (empty($config['persistent'])) {
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->connection = pg_pconnect($string);
}
$this->connection = empty($config['persistent'])
? pg_connect($string, $connectType)
: pg_pconnect($string, $connectType);
restore_error_handler();
}
@@ -169,12 +169,9 @@ class PostgreDriver implements Dibi\Driver
*/
public function getInsertId(?string $sequence): ?int
{
if ($sequence === null) {
// PostgreSQL 8.1 is needed
$res = $this->query('SELECT LASTVAL()');
} else {
$res = $this->query("SELECT CURRVAL('$sequence')");
}
$res = $sequence === null
? $this->query('SELECT LASTVAL()') // PostgreSQL 8.1 is needed
: $this->query("SELECT CURRVAL('$sequence')");
if (!$res) {
return null;

View File

@@ -251,7 +251,10 @@ class PostgreReflector implements Dibi\Reflector
$references[$row['name']] = array_combine($l, $f);
}
if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) {
if (
isset($references[$row['name']][$row['lnum']])
&& $references[$row['name']][$row['lnum']] === $row['fnum']
) {
$fKeys[$row['name']]['local'][] = $row['local'];
$fKeys[$row['name']]['foreign'][] = $row['foreign'];
}

View File

@@ -97,7 +97,9 @@ class PostgreResult implements Dibi\ResultDriver
'table' => pg_field_table($this->resultSet, $i),
'nativetype' => pg_field_type($this->resultSet, $i),
];
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
$row['fullname'] = $row['table']
? $row['table'] . '.' . $row['name']
: $row['name'];
$columns[] = $row;
}
return $columns;

View File

@@ -286,8 +286,12 @@ class SqliteDriver implements Dibi\Driver
/**
* Registers an aggregating user defined function for use in SQL statements.
*/
public function registerAggregateFunction(string $name, callable $rowCallback, callable $agrCallback, int $numArgs = -1): void
{
public function registerAggregateFunction(
string $name,
callable $rowCallback,
callable $agrCallback,
int $numArgs = -1
): void {
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -64,7 +64,7 @@ class SqliteReflector implements Dibi\Reflector
'fullname' => "$table.$column",
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : null,
'nullable' => $row['notnull'] == '0',
'nullable' => $row['notnull'] === 0,
'default' => $row['dflt_value'],
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
'vendor' => $row,
@@ -98,7 +98,7 @@ class SqliteReflector implements Dibi\Reflector
$column = $indexes[$index]['columns'][0];
$primary = false;
foreach ($columns as $info) {
if ($column == $info['name']) {
if ($column === $info['name']) {
$primary = $info['vendor']['pk'];
break;
}

View File

@@ -98,7 +98,9 @@ class SqlsrvDriver implements Dibi\Driver
} elseif (is_resource($res)) {
$this->affectedRows = Helpers::false2Null(sqlsrv_rows_affected($res));
return sqlsrv_num_fields($res) ? $this->createResultDriver($res) : null;
return sqlsrv_num_fields($res)
? $this->createResultDriver($res)
: null;
}
return null;
}
@@ -200,7 +202,7 @@ class SqlsrvDriver implements Dibi\Driver
public function escapeBinary(string $value): string
{
return "'" . str_replace("'", "''", $value) . "'";
return '0x' . bin2hex($value);
}

View File

@@ -32,6 +32,12 @@ namespace Dibi;
* @method Fluent and(...$cond)
* @method Fluent or(...$cond)
* @method Fluent using(...$cond)
* @method Fluent update(...$cond)
* @method Fluent insert(...$cond)
* @method Fluent delete(...$cond)
* @method Fluent into(...$cond)
* @method Fluent values(...$cond)
* @method Fluent set(...$args)
* @method Fluent asc()
* @method Fluent desc()
*/
@@ -113,7 +119,7 @@ class Fluent implements IDataSource
$this->connection = $connection;
if (self::$normalizer === null) {
self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
self::$normalizer = new HashMap([self::class, '_formatClause']);
}
}
@@ -305,11 +311,9 @@ class Fluent implements IDataSource
*/
public function fetch()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(null, ['%lmt', 1]))->fetch();
} else {
return $this->query($this->_export())->fetch();
}
return $this->command === 'SELECT' && !$this->clauses['LIMIT']
? $this->query($this->_export(null, ['%lmt', 1]))->fetch()
: $this->query($this->_export())->fetch();
}
@@ -319,11 +323,9 @@ class Fluent implements IDataSource
*/
public function fetchSingle()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(null, ['%lmt', 1]))->fetchSingle();
} else {
return $this->query($this->_export())->fetchSingle();
}
return $this->command === 'SELECT' && !$this->clauses['LIMIT']
? $this->query($this->_export(null, ['%lmt', 1]))->fetchSingle()
: $this->query($this->_export())->fetchSingle();
}

View File

@@ -143,8 +143,8 @@ class Helpers
{
$best = null;
$min = (strlen($value) / 4 + 1) * 10 + .1;
foreach (array_unique($items, SORT_REGULAR) as $item) {
$item = is_object($item) ? $item->getName() : $item;
$items = array_map('strval', $items);
foreach (array_unique($items) as $item) {
if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) {
$min = $len;
$best = $item;
@@ -205,7 +205,7 @@ class Helpers
public static function getTypeCache(): HashMap
{
if (self::$types === null) {
self::$types = new HashMap([__CLASS__, 'detectType']);
self::$types = new HashMap([self::class, 'detectType']);
}
return self::$types;
}

View File

@@ -73,8 +73,9 @@ class FileLogger
private function writeToFile(Dibi\Event $event, string $message): void
{
$driver = $event->connection->getConfig('driver');
$message .=
"\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
"\n-- driver: " . (is_object($driver) ? get_class($driver) : $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);

View File

@@ -72,7 +72,9 @@ class Column
public function getTableName(): ?string
{
return isset($this->info['table']) && $this->info['table'] != null ? $this->info['table'] : null; // intentionally ==
return isset($this->info['table']) && $this->info['table'] != null // intentionally ==
? $this->info['table']
: null;
}

View File

@@ -82,7 +82,9 @@ class Result
{
if ($this->columns === null) {
$this->columns = [];
$reflector = $this->driver instanceof Dibi\Reflector ? $this->driver : null;
$reflector = $this->driver instanceof Dibi\Reflector
? $this->driver
: null;
foreach ($this->driver->getResultColumns() as $info) {
$this->columns[] = $this->names[strtolower($info['name'])] = new Column($reflector, $info);
}

View File

@@ -19,7 +19,7 @@ class Result implements IDataSource
{
use Strict;
/** @var ResultDriver */
/** @var ResultDriver|null */
private $driver;
/** @var array Translate table */
@@ -83,7 +83,9 @@ class Result implements IDataSource
*/
final public function seek(int $row): bool
{
return ($row !== 0 || $this->fetched) ? $this->getResultDriver()->seek($row) : true;
return ($row !== 0 || $this->fetched)
? $this->getResultDriver()->seek($row)
: true;
}
@@ -199,7 +201,7 @@ class Result implements IDataSource
*/
final public function fetchAll(int $offset = null, int $limit = null): array
{
$limit = $limit === null ? -1 : $limit;
$limit = $limit ?? -1;
$this->seek($offset ?: 0);
$row = $this->fetch();
if (!$row) {
@@ -242,6 +244,9 @@ class Result implements IDataSource
$data = null;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
if (!$assoc) {
throw new \InvalidArgumentException("Invalid descriptor '$assoc'.");
}
// check columns
foreach ($assoc as $as) {
@@ -292,6 +297,7 @@ class Result implements IDataSource
} while ($row = $this->fetch());
unset($x);
/** @var mixed[] $data */
return $data;
}
@@ -353,11 +359,9 @@ class Result implements IDataSource
}
if ($x === null) { // build leaf
if ($leaf === '=') {
$x = $row->toArray();
} else {
$x = $row;
}
$x = $leaf === '='
? $row->toArray()
: $row;
}
} while ($row = $this->fetch());
@@ -481,7 +485,9 @@ class Result implements IDataSource
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
if ($value && substr((string) $value, 0, 3) !== '000') { // '', null, false, '0000-00-00', ...
$value = new DateTime($value);
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
$row[$key] = empty($this->formats[$type])
? $value
: $value->format($this->formats[$type]);
} else {
$row[$key] = null;
}
@@ -492,7 +498,9 @@ class Result implements IDataSource
$row[$key]->invert = (int) (bool) $m[1];
} elseif ($type === Type::BINARY) {
$row[$key] = is_string($value) ? $this->getResultDriver()->unescapeBinary($value) : $value;
$row[$key] = is_string($value)
? $this->getResultDriver()->unescapeBinary($value)
: $value;
} elseif ($type === Type::JSON) {
if ($this->formats[$type] === 'string') {

View File

@@ -29,9 +29,12 @@ trait Strict
*/
public function __call(string $name, array $args)
{
$class = method_exists($this, $name) ? 'parent' : get_class($this);
$class = method_exists($this, $name) ? 'parent' : static::class;
$items = (new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC);
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
$items = array_map(function ($item) { return $item->getName(); }, $items);
$hint = ($t = Helpers::getSuggestion($items, $name))
? ", did you mean $t()?"
: '.';
throw new \LogicException("Call to undefined method $class::$name()$hint");
}
@@ -42,9 +45,12 @@ trait Strict
*/
public static function __callStatic(string $name, array $args)
{
$rc = new ReflectionClass(get_called_class());
$rc = new ReflectionClass(static::class);
$items = array_intersect($rc->getMethods(ReflectionMethod::IS_PUBLIC), $rc->getMethods(ReflectionMethod::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
$items = array_map(function ($item) { return $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");
}
@@ -63,7 +69,10 @@ trait Strict
}
$rc = new ReflectionClass($this);
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
$items = array_map(function ($item) { return $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");
}
@@ -76,7 +85,10 @@ trait Strict
{
$rc = new ReflectionClass($this);
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
$items = array_map(function ($item) { return $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");
}
@@ -93,7 +105,7 @@ trait Strict
*/
public function __unset(string $name)
{
$class = get_class($this);
$class = static::class;
throw new \LogicException("Attempt to unset undeclared property $class::$$name.");
}
}

View File

@@ -92,24 +92,26 @@ final class Translator
$sql[] = $arg;
} else {
$sql[] = substr($arg, 0, $toSkip)
/*
. preg_replace_callback('/
(?=[`[\'":%?]) ## speed-up
(?:
`(.+?)`| ## 1) `identifier`
\[(.+?)\]| ## 2) [identifier]
(\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string'
(")((?:""|[^"])*)"| ## 5,6) "string"
(\'|")| ## 7) lone quote
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution:
%([a-zA-Z~][a-zA-Z0-9~]{0,5})|## 10) modifier
(\?) ## 11) placeholder
)/xs',
*/ // note: this can change $this->args & $this->cursor & ...
. preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s',
// note: this can change $this->args & $this->cursor & ...
. preg_replace_callback(
<<<'XX'
/
(?=[`['":%?]) ## speed-up
(?:
`(.+?)`| ## 1) `identifier`
\[(.+?)\]| ## 2) [identifier]
(')((?:''|[^'])*)'| ## 3,4) string
(")((?:""|[^"])*)"| ## 5,6) "string"
('|")| ## 7) lone quote
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution:
%([a-zA-Z~][a-zA-Z0-9~]{0,5})| ## 10) modifier
(\?) ## 11) placeholder
)/xs
XX
,
[$this, 'cb'],
substr($arg, $toSkip)
);
);
if (preg_last_error()) {
throw new PcreException;
}
@@ -289,7 +291,7 @@ final class Translator
if (is_array($v)) {
$vx[] = $this->formatValue($v, 'ex');
} elseif (is_string($k)) {
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
$v = (is_string($v) ? strncasecmp($v, 'd', 1) : $v > 0) ? 'ASC' : 'DESC';
$vx[] = $this->identifiers->$k . ' ' . $v;
} else {
$vx[] = $this->identifiers->$v;
@@ -322,7 +324,13 @@ final class Translator
} elseif ($value instanceof Expression && $modifier === 'ex') {
return $this->connection->translate(...$value->getValues());
} elseif ($value instanceof \DateTimeInterface && ($modifier === 'd' || $modifier === 't' || $modifier === 'dt')) {
} elseif (
$value instanceof \DateTimeInterface
&& ($modifier === 'd'
|| $modifier === 't'
|| $modifier === 'dt'
)
) {
// continue
} else {
$type = is_object($value) ? get_class($value) : gettype($value);
@@ -332,21 +340,29 @@ final class Translator
switch ($modifier) {
case 's': // string
return $value === null ? 'NULL' : $this->driver->escapeText((string) $value);
return $value === null
? 'NULL'
: $this->driver->escapeText((string) $value);
case 'bin':// binary
return $value === null ? 'NULL' : $this->driver->escapeBinary($value);
return $value === null
? 'NULL'
: $this->driver->escapeBinary($value);
case 'b': // boolean
return $value === null ? 'NULL' : $this->driver->escapeBool((bool) $value);
return $value === null
? 'NULL'
: $this->driver->escapeBool((bool) $value);
case 'sN': // string or null
case 'sn':
return $value == '' ? 'NULL' : $this->driver->escapeText((string) $value); // notice two equal signs
return $value === '' || $value === 0 || $value === null
? 'NULL'
: $this->driver->escapeText((string) $value);
case 'iN': // signed int or null
if ($value == '') {
$value = null;
if ($value === '' || $value === 0 || $value === null) {
return 'NULL';
}
// break omitted
case 'i': // signed int
@@ -384,7 +400,9 @@ final class Translator
} elseif (!$value instanceof \DateTimeInterface) {
$value = new DateTime($value);
}
return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value);
return $modifier === 'd'
? $this->driver->escapeDate($value)
: $this->driver->escapeDateTime($value);
case 'by':
case 'n': // composed identifier name
@@ -400,11 +418,23 @@ final class Translator
$toSkip = strcspn($value, '`[\'":');
if (strlen($value) !== $toSkip) {
$value = substr($value, 0, $toSkip)
. preg_replace_callback(
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
[$this, 'cb'],
substr($value, $toSkip)
);
. preg_replace_callback(
<<<'XX'
/
(?=[`['":])
(?:
`(.+?)`|
\[(.+?)\]|
(')((?:''|[^'])*)'|
(")((?:""|[^"])*)"|
('|")|
:(\S*?:)([a-zA-Z0-9._]?)
)/sx
XX
,
[$this, 'cb'],
substr($value, $toSkip)
);
if (preg_last_error()) {
throw new PcreException;
}
@@ -597,7 +627,9 @@ final class Translator
if ($matches[8]) { // SQL identifier substitution
$m = substr($matches[8], 0, -1);
$m = $this->connection->getSubstitutes()->$m;
return $matches[9] == '' ? $this->formatValue($m, null) : $m . $matches[9]; // value or identifier
return $matches[9] === ''
? $this->formatValue($m, null)
: $m . $matches[9]; // value or identifier
}
throw new \Exception('this should be never executed');

View File

@@ -30,6 +30,6 @@ class Type
final public function __construct()
{
throw new \LogicException('Cannot instantiate static class ' . __CLASS__);
throw new \LogicException('Cannot instantiate static class ' . self::class);
}
}

View File

@@ -75,7 +75,7 @@ class dibi
*/
final public function __construct()
{
throw new LogicException('Cannot instantiate static class ' . get_class($this));
throw new LogicException('Cannot instantiate static class ' . static::class);
}

View File

@@ -1,7 +0,0 @@
imports:
- { resource: '../temp/coding-standard/coding-standard-php71.yml' }
parameters:
skip:
PhpCsFixer\Fixer\Operator\TernaryToNullCoalescingFixer:
- src/Dibi/HashMap.php # issue #260

View File

@@ -12,7 +12,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php';
test(function () use ($config) {
test('', function () use ($config) {
$conn = new Connection($config);
Assert::true($conn->isConnected());
@@ -21,7 +21,7 @@ test(function () use ($config) {
});
test(function () use ($config) { // lazy
test('lazy', function () use ($config) {
$conn = new Connection($config + ['lazy' => true]);
Assert::false($conn->isConnected());
@@ -30,7 +30,7 @@ test(function () use ($config) { // lazy
});
test(function () use ($config) {
test('', function () use ($config) {
$conn = new Connection($config);
Assert::true($conn->isConnected());
@@ -40,7 +40,7 @@ test(function () use ($config) {
});
test(function () use ($config) {
test('', function () use ($config) {
$conn = new Connection($config);
Assert::true($conn->isConnected());
@@ -52,7 +52,7 @@ test(function () use ($config) {
});
test(function () use ($config) {
test('', function () use ($config) {
$conn = new Connection($config);
Assert::equal('hello', $conn->query('SELECT %s', 'hello')->fetchSingle());
@@ -63,7 +63,7 @@ test(function () use ($config) {
});
test(function () use ($config) {
test('', function () use ($config) {
Assert::exception(function () use ($config) {
new Connection($config + ['onConnect' => '']);
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");

View File

@@ -29,13 +29,6 @@ Assert::exception(function () {
}, Dibi\DriverException::class, 'PDO connection in exception or warning error mode is not supported.');
// PDO error mode: explicitly set silent
test(function () {
test('PDO error mode: explicitly set silent', function () {
buildPdoDriver(PDO::ERRMODE_SILENT);
});
// PDO error mode: implicitly set silent
test(function () {
buildPdoDriver(null);
});

View File

@@ -24,7 +24,7 @@ class MockResult extends Dibi\Result
}
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::BOOL);
@@ -46,7 +46,7 @@ test(function () {
});
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::TEXT);
@@ -62,7 +62,7 @@ test(function () {
});
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::FLOAT);
@@ -139,7 +139,7 @@ test(function () {
});
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::INTEGER);
@@ -147,7 +147,14 @@ test(function () {
Assert::same(['col' => 1], $result->test(['col' => true]));
Assert::same(['col' => 0], $result->test(['col' => false]));
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning in PHP 7.1
if (PHP_VERSION_ID < 80000) {
Assert::same(['col' => 0], @$result->test(['col' => ''])); // triggers warning since PHP 7.1
} else {
Assert::exception(function () use ($result) {
Assert::same(['col' => 0], $result->test(['col' => '']));
}, TypeError::class);
}
Assert::same(['col' => 0], $result->test(['col' => '0']));
Assert::same(['col' => 1], $result->test(['col' => '1']));
Assert::same(['col' => 10], $result->test(['col' => '10']));
@@ -165,7 +172,7 @@ test(function () {
});
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::DATETIME);
@@ -183,7 +190,7 @@ test(function () {
});
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::DATETIME);
$result->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
@@ -202,7 +209,7 @@ test(function () {
});
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::DATE);
@@ -218,7 +225,7 @@ test(function () {
});
test(function () {
test('', function () {
$result = new MockResult;
$result->setType('col', Type::TIME);

View File

@@ -23,7 +23,9 @@ Assert::equal(1, $conn->getInsertId());
$conn->query(
'CREATE TRIGGER %n ON %n AFTER INSERT AS INSERT INTO %n DEFAULT VALUES',
'UpdAAB', 'aab', 'aaa'
'UpdAAB',
'aab',
'aaa'
);
$conn->query('INSERT INTO %n DEFAULT VALUES', 'aab');

View File

@@ -1,9 +1,5 @@
<?php
/**
* @phpversion 5.5
*/
declare(strict_types=1);
use Tester\Assert;

View File

@@ -19,12 +19,17 @@ Assert::same(
SELECT *
FROM [customers]
/* WHERE ... LIKE ... */'),
$conn->translate('
$conn->translate(
'
SELECT *
FROM [customers]
%if', isset($name), 'WHERE [name] LIKE %s', 'xxx', '%end'
));
%if',
isset($name),
'WHERE [name] LIKE %s',
'xxx',
'%end'
)
);
// if & else & end (last end is optional)
@@ -32,11 +37,14 @@ Assert::same(
reformat('
SELECT *
FROM [customers] /* ... */'),
$conn->translate('
$conn->translate(
'
SELECT *
FROM %if', true, '[customers] %else [products]'
));
FROM %if',
true,
'[customers] %else [products]'
)
);
// if & else & (optional) end
@@ -48,14 +56,14 @@ WHERE [id] > 0
/* AND ...=...
*/ AND [bar]=1
'),
$conn->translate('
SELECT *
FROM [people]
WHERE [id] > 0
%if', false, 'AND [foo]=%i', 1, '
%else %if', true, 'AND [bar]=%i', 1, '
'));
')
);
// nested condition
@@ -76,24 +84,35 @@ WHERE
/* AND ...=1 */
/* 1 LIMIT 10 */",
]),
$conn->translate('
$conn->translate(
'
SELECT *
FROM [customers]
WHERE
%if', true, '[name] LIKE %s', 'xxx', '
%if', false, 'AND [admin]=1 %end
%if',
true,
'[name] LIKE %s',
'xxx',
'
%if',
false,
'AND [admin]=1 %end
%else 1 LIMIT 10 %end'
));
)
);
// limit & offset
Assert::same(
'SELECT * FROM foo /* (limit 3) (offset 5) */',
$conn->translate(
'SELECT * FROM foo',
'%if', false,
'%lmt', 3,
'%ofs', 5,
'%end'
));
'SELECT * FROM foo',
'%if',
false,
'%lmt',
3,
'%ofs',
5,
'%end'
)
);

View File

@@ -261,7 +261,7 @@ if ($config['system'] === 'postgre') {
'sqlite' => "SELECT * FROM products WHERE (title LIKE 'C%' ESCAPE '\\' AND title LIKE '%r' ESCAPE '\\') OR title LIKE '%a\n\\%\\_\\\\''\"%' ESCAPE '\\'",
'odbc' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
'sqlsrv' => "SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\n[%][_]\\''\"%'",
"SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\\n\\%\\_\\\\\\\\\'\"%'",
"SELECT * FROM products WHERE (title LIKE 'C%' AND title LIKE '%r') OR title LIKE '%a\\n\\%\\_\\\\\\\\\\'\"%'",
]),
$conn->translate($args[0], $args[1], $args[2], $args[3])
);
@@ -279,7 +279,7 @@ Assert::match(
CONCAT(last_name, ', ', first_name) AS full_name
GROUP BY `user`
HAVING MAX(salary) > %i 123
INTO OUTFILE '/tmp/result\'.txt'
INTO OUTFILE '/tmp/result\\'.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\\\"'
LINES TERMINATED BY '\\\\n'
",
@@ -344,7 +344,7 @@ WHERE (`test`.`a` LIKE '1995-03-01'
OR `b8` IN (RAND() `col1` > `col2` )
OR `b9` IN (RAND(), [col1] > [col2] )
OR `b10` IN ( )
AND `c` = 'embedded \' string'
AND `c` = 'embedded \\' string'
OR `d`=10
OR `e`=NULL
OR `true`= 1
@@ -437,7 +437,6 @@ WHERE ([test].[a] LIKE '1995-03-01'
OR [str_not_null]='hello'
LIMIT 10",
]),
$conn->translate('SELECT *
FROM [db.table]
WHERE ([test.a] LIKE %d', '1995-03-01', '
@@ -671,7 +670,6 @@ Assert::same(
'sqlsrv' => "UPDATE [colors] SET [color]=N'blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
"UPDATE [colors] SET [color]='blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
]),
$conn->translate('UPDATE [colors] SET', [
'color' => 'blue',
'price' => -12.4,

View File

@@ -37,7 +37,7 @@ if ($config['system'] === 'odbc') {
}
function test(Closure $function)
function test(string $title, Closure $function): void
{
$function();
}