mirror of
https://github.com/dg/dibi.git
synced 2025-09-16 00:52:29 +02:00
Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
34bc742245 | ||
|
f5fa2255ff | ||
|
7b02296f3e | ||
|
df4cddac1f | ||
|
cc37121390 | ||
|
a95b409231 | ||
|
3b057c2e35 | ||
|
f444b5d993 | ||
|
6e41c4223b | ||
|
0ee4628712 | ||
|
ab3677203c | ||
|
1bdf6e93d0 | ||
|
ed2a827419 | ||
|
e46be6cee6 | ||
|
0f69d5d32c | ||
|
e826e3a719 | ||
|
6eac117f5f | ||
|
2a2c814b0a | ||
|
dfab3d711c | ||
|
34e16031f7 | ||
|
73160e9418 | ||
|
f18056a066 | ||
|
0bd222b3f1 | ||
|
9f71f39470 | ||
|
0b0d805040 | ||
|
8c761eac5c | ||
|
2f857c28d6 | ||
|
efe1cbdc20 | ||
|
21dad1d846 | ||
|
7d55fd03b0 | ||
|
294787a26e |
1
.github/funding.yml
vendored
Normal file
1
.github/funding.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: dg
|
10
.travis.yml
10
.travis.yml
@@ -32,6 +32,7 @@ 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:
|
||||
@@ -39,6 +40,7 @@ jobs:
|
||||
|
||||
|
||||
- name: Nette Coding Standard
|
||||
php: 7.4
|
||||
install:
|
||||
- travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress
|
||||
script:
|
||||
@@ -46,15 +48,13 @@ jobs:
|
||||
|
||||
|
||||
- stage: Static Analysis (informative)
|
||||
install:
|
||||
# Install PHPStan
|
||||
- travis_retry composer create-project phpstan/phpstan-shim temp/phpstan --no-progress
|
||||
- travis_retry composer install --no-progress --prefer-dist
|
||||
php: 7.4
|
||||
script:
|
||||
- php temp/phpstan/phpstan.phar analyse --autoload-file vendor/autoload.php --level 5 src
|
||||
- composer run-script phpstan
|
||||
|
||||
|
||||
- stage: Code Coverage
|
||||
php: 7.4
|
||||
script:
|
||||
- vendor/bin/tester -p phpdbg tests -s --coverage ./coverage.xml --coverage-src ./src
|
||||
after_script:
|
||||
|
@@ -3,7 +3,7 @@
|
||||
"description": "Dibi is Database Abstraction Library for PHP",
|
||||
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"],
|
||||
"homepage": "https://dibiphp.com",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
@@ -15,7 +15,9 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"tracy/tracy": "~2.2",
|
||||
"nette/tester": "~2.0"
|
||||
"nette/tester": "~2.0",
|
||||
"nette/di": "^3.0",
|
||||
"phpstan/phpstan": "^0.12"
|
||||
},
|
||||
"replace": {
|
||||
"dg/dibi": "*"
|
||||
@@ -23,6 +25,10 @@
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"scripts": {
|
||||
"phpstan": "phpstan analyse --autoload-file vendor/autoload.php --level 5 --configuration tests/phpstan.neon src",
|
||||
"tester": "tester tests -s"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
|
@@ -20,6 +20,7 @@ $dibi = new Dibi\Connection([
|
||||
// enable query logging to this file
|
||||
'profiler' => [
|
||||
'file' => 'log/log.sql',
|
||||
'errorsOnly' => false,
|
||||
],
|
||||
]);
|
||||
|
||||
|
49
readme.md
49
readme.md
@@ -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?
|
||||
|
||||
[](https://nette.org/make-donation?to=dibi)
|
||||
|
||||
|
||||
Installation
|
||||
@@ -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');
|
||||
@@ -75,6 +81,8 @@ In the event of a connection error, it throws `Dibi\Exception`.
|
||||
|
||||
We query the database queries by the method `query()` which returns `Dibi\Result`. Rows are objects `Dibi\Row`.
|
||||
|
||||
You can try all the examples [online at the playground](https://repl.it/@DavidGrudl/dibi-playground).
|
||||
|
||||
```php
|
||||
$result = $database->query('SELECT * FROM users');
|
||||
|
||||
@@ -183,7 +191,7 @@ $result = $database->query('SELECT * FROM users WHERE id IN (%i)', $ids);
|
||||
// SELECT * FROM users WHERE id IN (10, 20, 30)
|
||||
```
|
||||
|
||||
The modifier '%n' is used if the table or column name is a variable. (Beware, do not allow the user to manipulate the content of such a variable):
|
||||
The modifier `%n` is used if the table or column name is a variable. (Beware, do not allow the user to manipulate the content of such a variable):
|
||||
|
||||
```php
|
||||
$table = 'blog.users';
|
||||
@@ -199,6 +207,7 @@ Three special modifiers are available for LIKE:
|
||||
| `%like~` | the expression starts with a string
|
||||
| `%~like` | the expression ends with a string
|
||||
| `%~like~` | the expression contains a string
|
||||
| `%like` | the expression matches a string
|
||||
|
||||
Search for names beginning with a string:
|
||||
|
||||
@@ -226,8 +235,8 @@ Example:
|
||||
|
||||
```php
|
||||
$arr = [
|
||||
'a' => 'hello',
|
||||
'b' => true,
|
||||
'a' => 'hello',
|
||||
'b' => true,
|
||||
];
|
||||
|
||||
$database->query('INSERT INTO table %v', $arr);
|
||||
@@ -499,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) {
|
||||
...
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -531,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) {
|
||||
...
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -545,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
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -568,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";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@@ -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;
|
||||
@@ -135,7 +144,10 @@ class Panel implements Tracy\IBarPanel
|
||||
$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 +155,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 ms</th><th>SQL Statement</th><th>Rows</th></tr>' . $s . '
|
||||
<tr><th>Time 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') : '');
|
||||
}
|
||||
}
|
||||
|
@@ -51,6 +51,7 @@ class Connection implements IConnection
|
||||
* - profiler (array)
|
||||
* - run (bool) => enable profiler?
|
||||
* - file => file to log
|
||||
* - errorsOnly (bool) => log only errors
|
||||
* - substitutes (array) => map of driver specific substitutes (under development)
|
||||
* - onConnect (array) => list of SQL queries to execute (by Connection::query()) after connection is established
|
||||
* @throws Exception
|
||||
@@ -70,7 +71,8 @@ class Connection implements IConnection
|
||||
// profiler
|
||||
if (isset($config['profiler']['file']) && (!isset($config['profiler']['run']) || $config['profiler']['run'])) {
|
||||
$filter = $config['profiler']['filter'] ?? Event::QUERY;
|
||||
$this->onEvent[] = [new Loggers\FileLogger($config['profiler']['file'], $filter), 'logEvent'];
|
||||
$errorsOnly = $config['profiler']['errorsOnly'] ?? false;
|
||||
$this->onEvent[] = [new Loggers\FileLogger($config['profiler']['file'], $filter, $errorsOnly), 'logEvent'];
|
||||
}
|
||||
|
||||
$this->substitutes = new HashMap(function (string $expr) { return ":$expr:"; });
|
||||
@@ -108,6 +110,7 @@ class Connection implements IConnection
|
||||
{
|
||||
if ($this->config['driver'] instanceof Driver) {
|
||||
$this->driver = $this->config['driver'];
|
||||
$this->translator = new Translator($this);
|
||||
return;
|
||||
|
||||
} elseif (is_subclass_of($this->config['driver'], Driver::class)) {
|
||||
@@ -124,6 +127,8 @@ class Connection implements IConnection
|
||||
$event = $this->onEvent ? new Event($this, Event::CONNECT) : null;
|
||||
try {
|
||||
$this->driver = new $class($this->config);
|
||||
$this->translator = new Translator($this);
|
||||
|
||||
if ($event) {
|
||||
$this->onEvent($event->done());
|
||||
}
|
||||
@@ -149,7 +154,7 @@ class Connection implements IConnection
|
||||
{
|
||||
if ($this->driver) {
|
||||
$this->driver->disconnect();
|
||||
$this->driver = null;
|
||||
$this->driver = $this->translator = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,11 +255,7 @@ class Connection implements IConnection
|
||||
if (!$this->driver) {
|
||||
$this->connect();
|
||||
}
|
||||
if (!$this->translator) {
|
||||
$this->translator = new Translator($this);
|
||||
}
|
||||
$translator = clone $this->translator;
|
||||
return $translator->translate($args);
|
||||
return (clone $this->translator)->translate($args);
|
||||
}
|
||||
|
||||
|
||||
|
219
src/Dibi/Drivers/DummyDriver.php
Normal file
219
src/Dibi/Drivers/DummyDriver.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?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\Drivers;
|
||||
|
||||
use Dibi;
|
||||
|
||||
|
||||
/**
|
||||
* The dummy driver for testing purposes.
|
||||
*/
|
||||
class DummyDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
|
||||
{
|
||||
use Dibi\Strict;
|
||||
|
||||
public function disconnect(): void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function query(string $sql): ?Dibi\ResultDriver
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function getAffectedRows(): ?int
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function getInsertId(?string $sequence): ?int
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function begin(string $savepoint = null): void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function commit(string $savepoint = null): void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function rollback(string $savepoint = null): void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function getResource()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the connection reflector.
|
||||
*/
|
||||
public function getReflector(): Dibi\Reflector
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/********************* SQL ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Encodes data for use in a SQL statement.
|
||||
*/
|
||||
public function escapeText(string $value): string
|
||||
{
|
||||
return "'" . str_replace("'", "''", $value) . "'";
|
||||
}
|
||||
|
||||
|
||||
public function escapeBinary(string $value): string
|
||||
{
|
||||
return "N'" . str_replace("'", "''", $value) . "'";
|
||||
}
|
||||
|
||||
|
||||
public function escapeIdentifier(string $value): string
|
||||
{
|
||||
return '[' . strtr($value, '[]', ' ') . ']';
|
||||
}
|
||||
|
||||
|
||||
public function escapeBool(bool $value): string
|
||||
{
|
||||
return $value ? '1' : '0';
|
||||
}
|
||||
|
||||
|
||||
public function escapeDate(\DateTimeInterface $value): string
|
||||
{
|
||||
return $value->format("'Y-m-d'");
|
||||
}
|
||||
|
||||
|
||||
public function escapeDateTime(\DateTimeInterface $value): string
|
||||
{
|
||||
return $value->format("'Y-m-d H:i:s.u'");
|
||||
}
|
||||
|
||||
|
||||
public function escapeDateInterval(\DateInterval $value): string
|
||||
{
|
||||
throw new Dibi\NotImplementedException;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes string for use in a LIKE statement.
|
||||
*/
|
||||
public function escapeLike(string $value, int $pos): string
|
||||
{
|
||||
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injects LIMIT/OFFSET to the SQL query.
|
||||
*/
|
||||
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
|
||||
{
|
||||
if ($limit < 0 || $offset < 0) {
|
||||
throw new Dibi\NotSupportedException('Negative offset or limit.');
|
||||
|
||||
} elseif ($limit !== null || $offset) {
|
||||
$sql .= ' LIMIT ' . ($limit ?? '-1')
|
||||
. ($offset ? ' OFFSET ' . $offset : '');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* Result ****************d*g**/
|
||||
|
||||
|
||||
public function getRowCount(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public function fetch(bool $assoc): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function seek(int $row): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function free(): void
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function getResultResource()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function getResultColumns(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes data from result set.
|
||||
*/
|
||||
public function unescapeBinary(string $value): string
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/********************* Reflector ****************d*g**/
|
||||
|
||||
|
||||
public function getTables(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
public function getColumns(string $table): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
public function getIndexes(string $table): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
public function getForeignKeys(string $table): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@@ -40,9 +40,7 @@ class FirebirdDriver implements Dibi\Driver
|
||||
private $inTransaction = false;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('interbase')) {
|
||||
@@ -271,7 +269,7 @@ class FirebirdDriver implements Dibi\Driver
|
||||
public function escapeLike(string $value, int $pos): string
|
||||
{
|
||||
$value = addcslashes($this->escapeText($value), '%_\\');
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
|
||||
|
@@ -47,9 +47,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
private $buffered;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('mysqli')) {
|
||||
@@ -93,7 +91,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
@$this->connection->real_connect( // intentionally @
|
||||
(empty($config['persistent']) ? '' : 'p:') . $config['host'],
|
||||
$config['username'],
|
||||
$config['password'],
|
||||
$config['password'] ?? '',
|
||||
$config['database'] ?? '',
|
||||
$config['port'] ?? 0,
|
||||
$config['socket'],
|
||||
@@ -132,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
|
||||
@@ -150,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)) {
|
||||
@@ -307,7 +317,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'");
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +327,7 @@ class MySqliDriver implements Dibi\Driver
|
||||
public function escapeLike(string $value, int $pos): string
|
||||
{
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
|
@@ -37,9 +37,7 @@ class OdbcDriver implements Dibi\Driver
|
||||
private $microseconds = true;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('odbc')) {
|
||||
@@ -250,7 +248,7 @@ class OdbcDriver implements Dibi\Driver
|
||||
public function escapeLike(string $value, int $pos): string
|
||||
{
|
||||
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
|
@@ -42,9 +42,7 @@ class OracleDriver implements Dibi\Driver
|
||||
private $affectedRows;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('oci8')) {
|
||||
@@ -270,7 +268,7 @@ class OracleDriver implements Dibi\Driver
|
||||
{
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
|
||||
$value = str_replace("'", "''", $value);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
|
@@ -42,9 +42,7 @@ class PdoDriver implements Dibi\Driver
|
||||
private $serverVersion = '';
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('pdo')) {
|
||||
@@ -324,29 +322,29 @@ class PdoDriver implements Dibi\Driver
|
||||
switch ($this->driverName) {
|
||||
case 'mysql':
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
|
||||
case 'oci':
|
||||
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
|
||||
$value = str_replace("'", "''", $value);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
|
||||
case 'pgsql':
|
||||
$bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off
|
||||
$value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1);
|
||||
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
|
||||
case 'sqlite':
|
||||
$value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\');
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
|
||||
case 'odbc':
|
||||
case 'mssql':
|
||||
case 'dblib':
|
||||
case 'sqlsrv':
|
||||
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
|
||||
default:
|
||||
throw new Dibi\NotImplementedException;
|
||||
|
@@ -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
|
||||
{
|
||||
@@ -35,9 +36,7 @@ class PostgreDriver implements Dibi\Driver
|
||||
private $affectedRows;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('pgsql')) {
|
||||
@@ -64,14 +63,15 @@ 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);
|
||||
$this->connection = pg_connect($string, $connectType);
|
||||
} else {
|
||||
$this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW);
|
||||
$this->connection = pg_pconnect($string, $connectType);
|
||||
}
|
||||
restore_error_handler();
|
||||
}
|
||||
@@ -318,7 +318,7 @@ class PostgreDriver implements Dibi\Driver
|
||||
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
|
||||
$value = pg_escape_string($this->connection, $value);
|
||||
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
|
@@ -37,9 +37,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
private $fmtDateTime;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('sqlite3')) {
|
||||
@@ -254,7 +252,7 @@ class SqliteDriver implements Dibi\Driver
|
||||
public function escapeLike(string $value, int $pos): string
|
||||
{
|
||||
$value = addcslashes($this->connection->escapeString($value), '%_\\');
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
|
||||
|
@@ -96,7 +96,7 @@ class SqliteResult implements Dibi\ResultDriver
|
||||
'name' => $this->resultSet->columnName($i),
|
||||
'table' => null,
|
||||
'fullname' => $this->resultSet->columnName($i),
|
||||
'nativetype' => $types[$this->resultSet->columnType($i)],
|
||||
'nativetype' => $types[$this->resultSet->columnType($i)] ?? null, // buggy in PHP 7.4.4 & 7.3.16, bug 79414
|
||||
];
|
||||
}
|
||||
return $columns;
|
||||
|
@@ -39,9 +39,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
private $version = '';
|
||||
|
||||
|
||||
/**
|
||||
* @throws Dibi\NotSupportedException
|
||||
*/
|
||||
/** @throws Dibi\NotSupportedException */
|
||||
public function __construct(array $config)
|
||||
{
|
||||
if (!extension_loaded('sqlsrv')) {
|
||||
@@ -202,7 +200,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
|
||||
public function escapeBinary(string $value): string
|
||||
{
|
||||
return "'" . str_replace("'", "''", $value) . "'";
|
||||
return '0x' . bin2hex($value);
|
||||
}
|
||||
|
||||
|
||||
@@ -243,7 +241,7 @@ class SqlsrvDriver implements Dibi\Driver
|
||||
public function escapeLike(string $value, int $pos): string
|
||||
{
|
||||
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
|
||||
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
|
||||
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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()
|
||||
*/
|
||||
|
@@ -201,9 +201,7 @@ class Helpers
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
public static function getTypeCache(): HashMap
|
||||
{
|
||||
if (self::$types === null) {
|
||||
@@ -279,18 +277,14 @@ class Helpers
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
public static function false2Null($val)
|
||||
{
|
||||
return $val === false ? null : $val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
/** @internal */
|
||||
public static function intVal($value): int
|
||||
{
|
||||
if (is_int($value)) {
|
||||
|
@@ -25,11 +25,15 @@ class FileLogger
|
||||
/** @var int */
|
||||
public $filter;
|
||||
|
||||
/** @var bool */
|
||||
private $errorsOnly;
|
||||
|
||||
public function __construct(string $file, int $filter = null)
|
||||
|
||||
public function __construct(string $file, int $filter = null, bool $errorsOnly = false)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->filter = $filter ?: Dibi\Event::QUERY;
|
||||
$this->errorsOnly = $errorsOnly;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,39 +42,42 @@ class FileLogger
|
||||
*/
|
||||
public function logEvent(Dibi\Event $event): void
|
||||
{
|
||||
if (($event->type & $this->filter) === 0) {
|
||||
if (
|
||||
(($event->type & $this->filter) === 0)
|
||||
|| ($this->errorsOnly === true && !$event->result instanceof \Exception)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$handle = fopen($this->file, 'a');
|
||||
if (!$handle) {
|
||||
return; // or throw exception?
|
||||
}
|
||||
flock($handle, LOCK_EX);
|
||||
|
||||
if ($event->result instanceof \Exception) {
|
||||
$message = $event->result->getMessage();
|
||||
if ($code = $event->result->getCode()) {
|
||||
$message = "[$code] $message";
|
||||
}
|
||||
fwrite($handle,
|
||||
$this->writeToFile(
|
||||
$event,
|
||||
"ERROR: $message"
|
||||
. "\n-- SQL: " . $event->sql
|
||||
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
|
||||
. ";\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
. "\n-- SQL: " . $event->sql
|
||||
);
|
||||
} else {
|
||||
fwrite($handle,
|
||||
$this->writeToFile(
|
||||
$event,
|
||||
'OK: ' . $event->sql
|
||||
. ($event->count ? ";\n-- rows: " . $event->count : '')
|
||||
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
|
||||
. "\n-- source: " . implode(':', $event->source)
|
||||
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
|
||||
. "\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
. ($event->count ? ";\n-- rows: " . $event->count : '')
|
||||
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
|
||||
. "\n-- source: " . implode(':', $event->source)
|
||||
);
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
|
||||
private function writeToFile(Dibi\Event $event, string $message): void
|
||||
{
|
||||
$driver = $event->connection->getConfig('driver');
|
||||
$message .=
|
||||
"\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);
|
||||
}
|
||||
}
|
||||
|
@@ -106,18 +106,14 @@ class Column
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
/** @return mixed */
|
||||
public function getDefault()
|
||||
{
|
||||
return $this->info['default'] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
/** @return mixed */
|
||||
public function getVendorInfo(string $key)
|
||||
{
|
||||
return $this->info['vendor'][$key] ?? null;
|
||||
|
@@ -46,9 +46,7 @@ class Database
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Table[]
|
||||
*/
|
||||
/** @return Table[] */
|
||||
public function getTables(): array
|
||||
{
|
||||
$this->init();
|
||||
@@ -56,9 +54,7 @@ class Database
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
/** @return string[] */
|
||||
public function getTableNames(): array
|
||||
{
|
||||
$this->init();
|
||||
|
@@ -38,9 +38,7 @@ class Result
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Column[]
|
||||
*/
|
||||
/** @return Column[] */
|
||||
public function getColumns(): array
|
||||
{
|
||||
$this->initColumns();
|
||||
@@ -48,9 +46,7 @@ class Result
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
/** @return string[] */
|
||||
public function getColumnNames(bool $fullNames = false): array
|
||||
{
|
||||
$this->initColumns();
|
||||
|
@@ -69,9 +69,7 @@ class Table
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Column[]
|
||||
*/
|
||||
/** @return Column[] */
|
||||
public function getColumns(): array
|
||||
{
|
||||
$this->initColumns();
|
||||
@@ -79,9 +77,7 @@ class Table
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
/** @return string[] */
|
||||
public function getColumnNames(): array
|
||||
{
|
||||
$this->initColumns();
|
||||
@@ -113,9 +109,7 @@ class Table
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ForeignKey[]
|
||||
*/
|
||||
/** @return ForeignKey[] */
|
||||
public function getForeignKeys(): array
|
||||
{
|
||||
$this->initForeignKeys();
|
||||
@@ -123,9 +117,7 @@ class Table
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Index[]
|
||||
*/
|
||||
/** @return Index[] */
|
||||
public function getIndexes(): array
|
||||
{
|
||||
$this->initIndexes();
|
||||
|
@@ -19,7 +19,7 @@ class Result implements IDataSource
|
||||
{
|
||||
use Strict;
|
||||
|
||||
/** @var ResultDriver */
|
||||
/** @var ResultDriver|null */
|
||||
private $driver;
|
||||
|
||||
/** @var array Translate table */
|
||||
@@ -242,6 +242,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) {
|
||||
@@ -282,7 +285,7 @@ class Result implements IDataSource
|
||||
}
|
||||
|
||||
} elseif ($as !== '|') { // associative-array node
|
||||
$x = &$x[$row->$as];
|
||||
$x = &$x[(string) $row->$as];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,13 +295,12 @@ class Result implements IDataSource
|
||||
} while ($row = $this->fetch());
|
||||
|
||||
unset($x);
|
||||
/** @var mixed[] $data */
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
/** @deprecated */
|
||||
private function oldFetchAssoc(string $assoc)
|
||||
{
|
||||
$this->seek(0);
|
||||
@@ -350,7 +352,7 @@ class Result implements IDataSource
|
||||
}
|
||||
|
||||
} else { // associative-array node
|
||||
$x = &$x[$row->$as];
|
||||
$x = &$x[(string) $row->$as];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,6 +454,7 @@ class Result implements IDataSource
|
||||
continue;
|
||||
}
|
||||
$value = $row[$key];
|
||||
|
||||
if ($type === Type::TEXT) {
|
||||
$row[$key] = (string) $value;
|
||||
|
||||
@@ -501,6 +504,12 @@ class Result implements IDataSource
|
||||
} else {
|
||||
$row[$key] = json_decode($value, $this->formats[$type] === 'array');
|
||||
}
|
||||
|
||||
} elseif ($type === null) {
|
||||
$row[$key] = $value;
|
||||
|
||||
} else {
|
||||
throw new \RuntimeException('Unexpected type ' . $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,9 +578,7 @@ class Result implements IDataSource
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Reflection\Column[]
|
||||
*/
|
||||
/** @return Reflection\Column[] */
|
||||
final public function getColumns(): array
|
||||
{
|
||||
return $this->getInfo()->getColumns();
|
||||
|
@@ -92,24 +92,25 @@ 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;
|
||||
}
|
||||
@@ -400,11 +401,22 @@ 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;
|
||||
}
|
||||
@@ -415,12 +427,15 @@ final class Translator
|
||||
return (string) $value;
|
||||
|
||||
case 'like~': // LIKE string%
|
||||
return $this->driver->escapeLike($value, 1);
|
||||
return $this->driver->escapeLike($value, 2);
|
||||
|
||||
case '~like': // LIKE %string
|
||||
return $this->driver->escapeLike($value, -1);
|
||||
return $this->driver->escapeLike($value, 1);
|
||||
|
||||
case '~like~': // LIKE %string%
|
||||
return $this->driver->escapeLike($value, 3);
|
||||
|
||||
case 'like': // LIKE string
|
||||
return $this->driver->escapeLike($value, 0);
|
||||
|
||||
case 'and':
|
||||
|
@@ -44,7 +44,7 @@ class dibi
|
||||
|
||||
/** version */
|
||||
public const
|
||||
VERSION = '4.1.0';
|
||||
VERSION = '4.1.3';
|
||||
|
||||
/** sorting order */
|
||||
public const
|
||||
|
@@ -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,18 @@ 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());
|
||||
|
||||
$conn->disconnect();
|
||||
|
||||
$conn->connect();
|
||||
Assert::equal('hello', $conn->query('SELECT %s', 'hello')->fetchSingle());
|
||||
});
|
||||
|
||||
|
||||
test('', function () use ($config) {
|
||||
Assert::exception(function () use ($config) {
|
||||
new Connection($config + ['onConnect' => '']);
|
||||
}, InvalidArgumentException::class, "Configuration option 'onConnect' must be array.");
|
||||
|
@@ -29,13 +29,11 @@ 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 () {
|
||||
test('PDO error mode: implicitly set silent', function () {
|
||||
buildPdoDriver(null);
|
||||
});
|
||||
|
@@ -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);
|
||||
|
||||
@@ -165,7 +165,7 @@ test(function () {
|
||||
});
|
||||
|
||||
|
||||
test(function () {
|
||||
test('', function () {
|
||||
$result = new MockResult;
|
||||
$result->setType('col', Type::DATETIME);
|
||||
|
||||
@@ -183,7 +183,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 +202,7 @@ test(function () {
|
||||
});
|
||||
|
||||
|
||||
test(function () {
|
||||
test('', function () {
|
||||
$result = new MockResult;
|
||||
$result->setType('col', Type::DATE);
|
||||
|
||||
@@ -218,7 +218,7 @@ test(function () {
|
||||
});
|
||||
|
||||
|
||||
test(function () {
|
||||
test('', function () {
|
||||
$result = new MockResult;
|
||||
$result->setType('col', Type::TIME);
|
||||
|
||||
|
@@ -71,3 +71,9 @@ Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'baa', 'aa'));
|
||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'aab', 'aa'));
|
||||
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %~like~', 'bba', '%a'));
|
||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %~like~', 'b%a', '%a'));
|
||||
|
||||
|
||||
// matches
|
||||
Assert::truthy($conn->fetchSingle('SELECT ? LIKE %like', 'a', 'a'));
|
||||
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like', 'a', 'aa'));
|
||||
Assert::falsey($conn->fetchSingle('SELECT ? LIKE %like', 'a', 'b'));
|
||||
|
@@ -37,7 +37,7 @@ if ($config['system'] === 'odbc') {
|
||||
}
|
||||
|
||||
|
||||
function test(Closure $function)
|
||||
function test(string $title, Closure $function): void
|
||||
{
|
||||
$function();
|
||||
}
|
||||
|
4
tests/phpstan.neon
Normal file
4
tests/phpstan.neon
Normal file
@@ -0,0 +1,4 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# The namespace is referenced, not the class.
|
||||
- '#Class dibi referenced with incorrect case: Dibi#'
|
Reference in New Issue
Block a user